#Python 中使用 JSON
JSON(JavaScript Object Notation)是用于将结构化数据表示为 JavaScript 对象的标准格式,通常用于在网站上表示和传输数据(例如从服务器向客户端发送一些数据,因此可以将其显示在网页上)。 虽然 JSON 源自 JavaScript,但几乎所有编程语言都有存取 JSON 的 API。
在 Python 中,可以使用内置的 json
模块将对象序列化为 JSON 字符串或将 JSON 字符串反序列化为对象。
函数 | 说明 |
---|---|
dumps | 将对象序列化为 str |
loads | 将 str 反列化为对象 |
dump | 将对象序列化为 str 并写入文件 |
laod | 读取文件中的 str 并反列化为对象 |
函数说明:
def dumps(obj) -> str:
'''
将对象序列化为 JSON 字符串
:param obj: 要序列化的对象
:return: 序列化后的 JSON 字符串
'''
pass
def loads(json_str:str):
'''
将 JSON 字符串反列化为对象
:param json_str: 要反序列化的 JSON 字符串
:return: 反序列化后的对象
'''
pass
def dump(obj, fp) -> str:
'''
将对象序列化为 JSON 字符串并存入文件中
:param obj: 要序列化的对象
:param fp: 要写入的字节流
:return: 序列化后的 JSON 字符串
'''
pass
def load(fp):
'''
读取文件中的 JSON 字符串并反列化为对象
:param fp: 要读取的字节流
:return: 反序列化后的对象
'''
pass
Python 中的值和 JSON 中的表示方式按照下表的对应关系互相转换:
Python 中的类型 | JSON 中的表示方法 | 备注 |
---|---|---|
int 或 float | 3.1415926 | |
True /False | true /false | |
None | null | |
str | "hello world" | JSON 中必须使用双引号 |
dict | { "key": value } | JSON 中必须使用双引号 |
list 或 tuple | [ value1, value2 ] |
示例:
import json
# 数据
player_state:dict = {
'name': 'link',
'max_hp': 10,
'current_hp': 7,
}
# 通过 json.dumps 转换为字符串
with open("save.data", "w", encoding='utf-8') as fp:
json.dump(player_state, fp)
# ...
# 下次启动,通过 json.loads 加载数据
with open("save.data", "r", encoding='utf-8') as fp:
backup_state = json.load(fp)
# 查看数据
print(backup_state)
#自定义类与 JSON 的转换
如下表所示,JSON 本身只支持少数基本类型,虽然通过它们的组合,能够满足一切数据结构,但使用起来并不方便。
Python 中的类型 | JSON 中的表示方法 | 备注 |
---|---|---|
int 或 float | 3.1415926 | |
True /False | true /false | |
None | null | |
str | "hello world" | JSON 中必须使用双引号 |
dict | { "key": value } | JSON 中必须使用双引号 |
list 或 tuple | [ value1, value2 ] |
如果直接尝试序列化一个自定义类的对象,会产生一个 TypeError
的异常。
json.dumps
和 json.dump
函数包含一个名为 default
的参数,定义将对象转换为 dict
的函数,然后 json
模块负责将 dict
转换为 JOSN 字符串。
将对象转换为 dict
的函数非常简单,直接返回对象的 __dict__
属性即可,一行 lambda obj:obj.__dict__
就能实现。
json.loads
和 json.load
函数包含一个名为 object_hook
的参数,定义将 dict
转换为对象的函数,然后 json
模块负责将 JOSN 字符串转换为 dict
。
实现 dict
转换为对象则相对麻烦,一个较为简单的实现如下:
def make_dict_to_obj(cls):
'''
创建字典转对象的函数
:cls: 对象的类型
:return: 转换函数
'''
def dict_to_obj(dict_data:dict):
'''
将字典转换为对象
:param dict_data: 要转换的字典
:return: 转换后的对象
'''
# 创建一个 cls 类型的对象
obj = cls()
# 遍历字典
for key in dict_data:
# 将 obj 的 key 属性设为 dict_data[key]
setattr(obj, key, dict_data[key])
# 返回对象
return obj
# 返回创建的转换函数
return dict_to_obj
这个实现无脑将字典的所有元素都塞给了对象,可能缺少了类需要的属性或包含了类不需要的属性,这通常不是什么问题。
缺少的属性在创建对象时已经设置了初始值,多余的属性通常没有影响,也可以在 setattr
之前通过 hasattr
判断,在实际开发中应当根据实际需求决定处理逻辑。
示例:
import json
def make_dict_to_obj(cls):
'''
创建字典转对象的函数
:cls: 对象的类型
:return: 转换函数
'''
def dict_to_obj(dict_data:dict):
'''
将字典转换为对象
:param dict_data: 要转换的字典
:return: 转换后的对象
'''
# 创建一个 cls 类型的对象
obj = cls()
# 遍历字典
for key in dict_data:
# 将 obj 的 key 属性设为 dict_data[key]
setattr(obj, key, dict_data[key])
# 返回对象
return obj
# 返回创建的转换函数
return dict_to_obj
# 自定义类
class PlayerState:
def __init__(self):
self.name = 'link'
self.max_hp = 10
self.current_hp = 7
# 创建对象
player_state = PlayerState()
# 修改属性值
player_state.name = 'zelda'
player_state.max_hp = 20
player_state.current_hp = 2
# 序列化为 JSON 字符串
json_str:str = json.dumps(player_state, default=lambda obj:obj.__dict__) # 通过 __dict__ 属性返回对象的字典
print(json_str)
# 反序列化为对象
backup_state = json.loads(json_str, object_hook=make_dict_to_obj(PlayerState)) # 通过 make_dict_to_obj 创建字典到类的转换函数
print(backup_state.name)
print(backup_state.max_hp)
print(backup_state.current_hp)
如果完全不需要考虑字段的缺失和冗余,转换函数还有一种更简单的写法,通过内置函数 type
创建对象。
def dict_to_obj(dict_data:dict):
'''
将字典转换为对象
:param dict_data: 要转换的字典
:return: 转换后的对象
'''
# 创建并返回一个自定义对象
return type('CustomType', (), dict_data)
示例:
import json
def dict_to_obj(dict_data:dict):
'''
将字典转换为对象
:param dict_data: 要转换的字典
:return: 转换后的对象
'''
# 创建并返回一个自定义对象
return type('CustomType', (), dict_data)
# 自定义类
class PlayerState:
def __init__(self):
self.name = 'link'
self.max_hp = 10
self.current_hp = 7
# 创建对象
player_state = PlayerState()
# 修改属性值
player_state.name = 'zelda'
player_state.max_hp = 20
player_state.current_hp = 2
# 序列化为 JSON 字符串
json_str:str = json.dumps(player_state, default=lambda obj:obj.__dict__) # 通过 __dict__ 属性返回对象的字典
print(json_str)
# 反序列化为对象
backup_state = json.loads(json_str, object_hook=dict_to_obj) # 通过 dict_to_obj 将字典转换为对象
print(backup_state.name)
print(backup_state.max_hp)
print(backup_state.current_hp)