Python--JSON格式
一、基础:JSON 与 Python 数据类型的对应关系
JSON 仅支持有限的数据类型,与 Python 类型的对应关系如下(核心基础,必须掌握):
Python 类型 | JSON 类型 | 说明 |
---|---|---|
dict | object | 键必须为字符串(JSON 要求) |
list /tuple | array | 元组序列化后会转为列表 |
str | string | 必须为 Unicode 字符串 |
int /float | number | 整数 / 浮点数直接对应 |
True | true | 布尔值对应 |
False | false | 布尔值对应 |
None | null | 空值对应 |
注意:JSON 不支持 Python 的set
、datetime
、自定义类
等类型,需特殊处理(见后文)
补充:
字符串与Unicode字符串的区别
存储的内容不同
- Unicode 字符串:存储的是 Unicode 码点(code point),即字符的抽象数字表示(例如,汉字 “中” 的 Unicode 码点是 U+4E2D)。它不依赖于具体的编码方式(如 UTF-8、UTF-16 等),而是直接代表字符本身。
- 普通字符串(字节字符串):存储的是原始字节(0-255 的整数),这些字节需要通过特定编码(如 ASCII、UTF-8、GBK 等)才能解析为有意义的字符。
编码依赖性不同
- Unicode 字符串:与编码方式无关,是 “字符的抽象序列”。例如,在 Python 中,
str
类型就是 Unicode 字符串,无论字符是中文、英文还是其他语言,都统一用 Unicode 码点表示。 - 普通字符串:依赖于编码方式。同样的字节序列,用不同编码解析可能得到完全不同的字符。例如,字节
0xE4 0xB8 0xAD
用 UTF-8 解析是 “中”,但用其他编码解析可能是乱码。
- Unicode 字符串:与编码方式无关,是 “字符的抽象序列”。例如,在 Python 中,
支持的字符范围不同
- Unicode 字符串:支持全球所有语言的字符(包括 emoji),因为 Unicode 标准包含了几乎所有已知的字符集。
- 普通字符串(如基于 ASCII 编码):仅支持有限字符(如英文字母、数字和部分符号),无法直接表示中文、日文等复杂字符。
在编程语言中的表现
- 例如在 Python 中:
str
类型是 Unicode 字符串(如s = "中"
)。bytes
类型是字节字符串(如b = b"\xe4\xb8\xad"
,是 “中” 的 UTF-8 编码字节)。
- 两者需要通过编码(
str.encode(encoding)
)或解码(bytes.decode(encoding)
)进行转换。
- 例如在 Python 中:
简单来说,Unicode 字符串是 “字符的抽象表示”,而普通字符串(字节串)是 “字符的具体编码字节”。理解两者的区别是处理多语言文本的基础,可避免常见的乱码问题。
二、核心操作:序列化与反序列化
1. 序列化(Python 对象→JSON)
json.dumps(obj)
:将 Python 对象转为 JSON 字符串json.dump(obj, file)
:将 Python 对象直接写入文件(推荐,避免手动处理文件关闭)
import json# 定义Python对象
data = {"name": "Alice","age": 30,"is_student": False,"hobbies": ["reading", "coding"],"address": None,"scores": (90.5, 85, 95) # 元组会转为JSON数组
}# 1. 转为JSON字符串
json_str = json.dumps(data)
print("JSON字符串:", json_str)# 2. 直接写入文件(最佳实践:用with语句)
with open("data.json", "w", encoding="utf-8") as f:json.dump(data, f, ensure_ascii=False) # ensure_ascii=False:保留中文原样
运行结果:
2. 反序列化(JSON→Python 对象)
json.loads(json_str)
:将 JSON 字符串转为 Python 对象json.load(file)
:从文件读取 JSON 并转为 Python 对象(推荐)
import json
import osdef main():# 1. 从JSON字符串解析json_str = '{"name": "Bob", "age": 25, "hobbies": ["gaming"]}'try:data = json.loads(json_str)print("1. 字符串解析结果:", data)print(" 数据类型:", type(data)) # 输出字典类型print(" 姓名:", data["name"])print(" 年龄:", data["age"])print(" 爱好:", data["hobbies"][0])except json.JSONDecodeError as e:print("字符串解析错误:", e)# 2. 从文件读取(包含文件创建逻辑,确保文件存在)file_name = "data.json"# 检查文件是否存在,不存在则创建并写入示例数据if not os.path.exists(file_name):print(f"\n检测到'{file_name}'不存在,正在创建示例文件...")sample_data = {"name": "Alice", "age": 30, "city": "New York"}with open(file_name, "w", encoding="utf-8") as f:json.dump(sample_data, f, ensure_ascii=False, indent=2)print(f"示例文件'{file_name}'创建成功")# 从文件读取JSON数据try:with open(file_name, "r", encoding="utf-8") as f:data_from_file = json.load(f)print("\n2. 文件读取结果:", data_from_file)print(" 从文件获取的姓名:", data_from_file["name"])print(" 从文件获取的城市:", data_from_file["city"])except FileNotFoundError:print(f"文件读取错误:'{file_name}'未找到")except json.JSONDecodeError as e:print(f"文件内容解析错误:", e)except KeyError as e:print(f"数据键不存在:", e)if __name__ == "__main__":main()
运行结果:
三、进阶:处理复杂数据类型(datetime、自定义类等)
JSON 默认不支持 Python 的datetime
、set
、自定义类
等类型,直接序列化会报错。解决方法:自定义编码器 / 解码器。
1. 处理 datetime 对象
from datetime import datetime
import json# 定义含datetime的对象
data = {"event": "会议","start_time": datetime(2023, 10, 1, 9, 30),"end_time": datetime(2023, 10, 1, 11, 0)
}# 方法1:自定义序列化函数(推荐简单场景)
def datetime_serializer(obj):if isinstance(obj, datetime):return obj.isoformat() # 转为ISO格式字符串(如"2023-10-01T09:30:00")raise TypeError(f"类型 {type(obj)} 不支持序列化")# 序列化时指定default参数
json_str = json.dumps(data, default=datetime_serializer, ensure_ascii=False)
print("含datetime的JSON:", json_str)# 反序列化时还原为datetime
def datetime_decoder(dct):for key, value in dct.items():if key.endswith("_time"): # 假设时间字段以_time结尾try:dct[key] = datetime.fromisoformat(value)except ValueError:pass # 非时间格式不处理return dctdata_restored = json.loads(json_str, object_hook=datetime_decoder)
print("还原的start_time:", data_restored["start_time"], type(data_restored["start_time"])) # <class 'datetime.datetime'>
运行结果:
2. 处理自定义类
import jsonclass User:def __init__(self, name, age):self.name = nameself.age = age# 实例化
user = User("Charlie", 28)# 方法2:继承JSONEncoder实现自定义编码器(适合复杂场景)
class UserEncoder(json.JSONEncoder):def default(self, obj):if isinstance(obj, User):# 转为字典(包含类标识,便于反序列化)return {"__class__": "User", "name": obj.name, "age": obj.age}return super().default(obj) # 其他类型用默认处理# 序列化
json_str = json.dumps(user, cls=UserEncoder, ensure_ascii=False)
print("User序列化:", json_str) # {"__class__": "User", "name": "Charlie", "age": 28}# 反序列化时还原为User对象
def user_decoder(dct):if "__class__" in dct and dct["__class__"] == "User":return User(dct["name"], dct["age"])return dctuser_restored = json.loads(json_str, object_hook=user_decoder)
print("还原的User:", user_restored.name, user_restored.age) # Charlie 28
运行结果:
四、错误处理:捕获 JSON 操作中的异常
JSON 处理中常见异常:
json.JSONDecodeError
:JSON 格式错误(如括号不匹配、语法错误)TypeError
:序列化不支持的类型(未自定义处理时)
最佳实践:强制捕获异常
代码:
import json# 示例1:处理JSON格式错误
bad_json = '{"name": "Alice", age: 30}' # 错误:age未加引号
try:data = json.loads(bad_json)
except json.JSONDecodeError as e:print(f"JSON格式错误:{e},位置:{e.pos}") # 提示错误位置# 示例2:处理不支持的类型
data = {"set": {1, 2, 3}} # set不支持序列化
try:json.dumps(data)
except TypeError as e:print(f"序列化错误:{e}") # 提示"Object of type set is not JSON serializable"
运行结果
JSON格式错误:Expecting property name enclosed in double quotes: line 1 column 19 (char 18),位置:18
序列化错误:Object of type set is not JSON serializable