总览
描述一个JSON编码/解码包含自定义类的实例(对象)的复杂结构的示例。
语言是Python 3.8.1。
对于Python,使用
我认为除了这里描述的方法以外,还有其他方法,但是我在以下几点上喜欢这种方法。
定制和复杂的数据示例
因为它用于解释,所以我不会使其变得如此复杂,但是我将尝试使用满足以下条件的数据。
1 2 3 4 5 6 7 8 9 10 11 12 | class Person: def __init__(self, name): self.name = name class Robot: def __init__(self, name, creator=None): self.name = name self.creator = creator alan = Person('Alan') beetle = Robot('Beetle', creator=alan) chappy = Robot('Chappy', creator=beetle) |
在下面,我想列出一个机器人数据列表,并对列表进行编码/解码。
1 | robots = [beetle, chappy] |
编码
将
对象序列化为JSON字符串称为编码。
此列表包含
简单编码
首先,让我们对一个简单的
确定编码规范
要对自定义对象进行编码,必须决定如何对其进行编码(规范)。
在这里,我们将类名称和属性内容作为名称-值对输出。
在上述
1 | {"class": "Person", "name": "Alan"} |
制作自定义编码器
您可以通过在
标准json.dumps函数中指定
通过继承json.JSONEncoder并覆盖
由于对象包含在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import json # Person オブジェクト用の、カスタムのエンコーダ class PersonEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Person): return {'class': 'Person', 'name': obj.name} else: return super().default(obj) print(json.dumps(alan, cls=PersonEncoder)) # 結果: {"class": "Person", "name": "Alan"} |
复合编码
接下来,我们将创建类
如我在"概述"中所写,编码逻辑分为多个类。
1 2 3 4 5 6 7 | # Robot オブジェクト用の、カスタムのエンコーダ class RobotEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Robot): return {'class': 'Robot', 'name': obj.name, 'creator': obj.creator} else: return super().default(obj) |
与
但是,它没有像以前的
我敢于以这种方式划分逻辑,并且在实际编码时,请将两个编码器一起使用。
组合编码器
要合并编码器,请使用多重继承创建一个新类。
1 2 3 4 5 6 7 8 | # 2つのエンコーダを継承した、新しいエンコーダ class XEncoder(PersonEncoder, RobotEncoder): pass print(json.dumps(robots, cls=XEncoder)) # 結果: [{"class": "Robot", "name": "Beetle", "creator": {"class": "Person", "name": "Alan"}}, {"class": "Robot", "name": "Chappy", "creator": {"class": "Robot", "name": "Beetle", "creator": {"class": "Person", "name": "Alan"}}}] |
<详细信息>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | print(json.dumps(robots, cls=XEncoder, indent=4)) # 結果: [ { "class": "Robot", "name": "Beetle", "creator": { "class": "Person", "name": "Alan" } }, { "class": "Robot", "name": "Chappy", "creator": { "class": "Robot", "name": "Beetle", "creator": { "class": "Person", "name": "Alan" } } } ] |
详细信息>
此方法是因为您只能为
(补充)多重继承操作
我将通过制作上面的
在Python类的多重继承中,按继承顺序引用属性。
当调用
如果
在这种情况下,超级方法将是
这是Python的多重继承运动。
如果
我还没有研究如何递归调用
解码
与
编码相反,将JSON字符串反序列化为对象称为解码。
您可以通过将
简单的object_hook示例
首先,让我们看一个仅对
由于作为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # Person オブジェクト用のフック関数 def person_hook(obj): if type(obj) == dict and obj.get('class') == 'Person': return Person(obj['name']) else: return obj # JSON 文字列にエンコードする alan_encoded = json.dumps(alan, cls=PersonEncoder) # JSON 文字列からデコードする alan_decoded = json.loads(alan_encoded, object_hook=person_hook) print(alan_decoded) print(alan_decoded.__class__.__name__, vars(alan_decoded)) # 結果: <__main__.Person object at 0x0000027F67919AF0> Person {'name': 'Alan'} |
结合object_hook
接下来,为
1 2 3 4 5 6 7 8 9 10 | # Robot オブジェクト用のフック関数 def robot_hook(obj): if type(obj) == dict and obj.get('class') == 'Robot': return Robot(obj['name'], creator=obj['creator']) else: return obj # 2つのフック関数を呼び出す、新しいフック関数 def x_hook(obj): return person_hook(robot_hook(obj)) |
组合功能
1 2 3 4 5 | def x_hook(obj): hooks = [person_hook, robot_hook] for hook in hooks: obj = hook(obj) return obj |
使用它来编码/解码上面创建的机械手列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # JSON 文字列にエンコードする robots_encoded = json.dumps(robots, cls=XEncoder) # JSON 文字列からデコードする robots_decoded = json.loads(robots_encoded, object_hook=x_hook) for robot in robots_decoded: print(robot) print(robot.__class__.__name__, vars(robot)) # 結果: <__main__.Robot object at 0x0000027F67919A30> Robot {'name': 'Beetle', 'creator': <__main__.Person object at 0x0000027F67919B50>} <__main__.Robot object at 0x0000027F67919E50> Robot {'name': 'Chappy', 'creator': <__main__.Robot object at 0x0000027F679199D0>} |
与
编码一样(可能是因为它是从内部递归解码的),更改钩子的应用顺序不会更改结果。
(补充)可以使用相同的方式自定义编码
实际上,可以通过以相同方式提供功能来定制编码端。
相反,如果尝试使解码侧成为解码器的子类,则将变得更加复杂。
结合使用自定义编码逻辑时,如果只想编写具有多个继承的代码,则应选择创建子类的方法,如果要匹配解码端的样式,则应选择赋予功能。
通过提供函数
定制编码端的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | def person_default(obj): if isinstance(obj, Person): return {'class': 'Person', 'name': obj.name} else: return obj def robot_default(obj): if isinstance(obj, Robot): return {'class': 'Robot', 'name': obj.name, 'creator': obj.creator} else: return obj def x_default(obj): defaults = [person_default, robot_default] for default in defaults: obj = default(obj) return obj print(json.dumps(robots, default=x_default)) # 結果: [{"class": "Robot", "name": "Beetle", "creator": {"class": "Person", "name": "Alan"}}, {"class": "Robot", "name": "Chappy", "creator": {"class": "Robot", "name": "Beetle", "creator": {"class": "Person", "name": "Alan"}}}] |
任务
解码存在一些问题。
在上面的示例中,第一个解码的机器人\\'Beetle \\'和\\'Beetle \\'是\\'Chappy \\'的
同样,那些\\'Beelte \\'中的
上述解码方法不能完全重现编码之前的情况,因为它并不意味着"重用已经创建的对象,因为它们具有相同的名称"。
如果要这样做,可以为