鉴于下面的代码示例,如何使用Python 3使用JSON序列化这些类实例?
class TreeNode():
def __init__(self, name):
self.name = name
self.children = []
当我尝试json.dumps
时,我收到以下错误:
TypeError: <TreeNode object at 0x7f6sf4276f60> is not JSON serializable
然后我才发现,如果我将默认值设置为json.dumps
以返回__dict__
,我可以将其序列化,但是然后执行json.loads
会成为一个问题。
我可以找到许多带有基本字符串的自定义编码器/解码器示例,但是没有列表,在这种情况下是self.children。子列表将保存子节点及其子节点的其他节点。我需要一种方法来获得所有这些。
答案 0 :(得分:6)
由于您正在处理树结构,因此使用嵌套字典很自然。下面的代码片段创建了一个dict
的子类,并将自己用作实例的底层__dict__
- 这是我在许多不同的上下文中遇到的一个有趣且有用的技巧:
Is it preferable to return an anonymous class or an object to use as a 'struct'?
(Stackoverflow)
jsobject.py
(PyDoc.net)
Making Python Objects that act like Javascript Objects
(詹姆斯罗伯特的博客)
AttrDict
(ActiveState配方)
Dictionary with attribute-style access
(ActiveState配方)
class TreeNode(dict):
def __init__(self, name, children=None):
super().__init__()
self.__dict__ = self
self.name = name
self.children = [] if not children else children
这解决了序列化争斗的一半,但是当用json.loads()
读回产生的数据时,它将是常规字典对象,而不是TreeNode
的实例。这是因为JSONEncoder
可以编码字典(及其子类)本身。
解决这个问题的一种方法是向TreeNode
类添加一个替代构造函数方法,可以调用该方法从json.loads()
返回的嵌套字典重构数据结构。
这就是我的意思:
@staticmethod
def from_dict(dict_):
""" Recursively (re)construct TreeNode-based tree from dictionary. """
root = TreeNode(dict_['name'], dict_['children'])
root.children = list(map(TreeNode.from_dict, root.children))
return root
if __name__ == '__main__':
import json
tree = TreeNode('Parent')
tree.children.append(TreeNode('Child 1'))
child2 = TreeNode('Child 2')
tree.children.append(child2)
child2.children.append(TreeNode('Grand Kid'))
child2.children[0].children.append(TreeNode('Great Grand Kid'))
json_str = json.dumps(tree, sort_keys=True, indent=2)
print(json_str)
print()
pyobj = TreeNode.from_dict(json.loads(json_str)) # reconstitute
print('pyobj class: {}'.format(pyobj.__class__.__name__)) # -> TreeNode
print(json.dumps(pyobj, sort_keys=True, indent=2))
输出:
{
"children": [
{
"children": [],
"name": "Child 1"
},
{
"children": [
{
"children": [
{
"children": [],
"name": "Great Grand Kid"
}
],
"name": "Grand Kid"
}
],
"name": "Child 2"
}
],
"name": "Parent"
}
pyobj class: TreeNode
{
"children": [
... same as before ...
],
"name": "Parent"
}
答案 1 :(得分:1)
这里有一个替代答案,它基本上是answer的问题Making object JSON serializable with regular encoder的Python 3版本,用于挑选普通json
编码器不支持的任何Python对象# 39;已经处理好了。
存在一些差异。一个是它没有修补json
模块,因为它不是解决方案的重要组成部分。另一个是虽然这次TreeNode
类不是dict
类派生的,但它具有基本相同的功能。这样做是为了防止库存JSONEncoder
对其进行编码,并导致_default()
子类中的JSONEncoder
方法被替代使用。
除此之外,它是一种非常通用的方法,能够处理许多其他类型的Python对象,包括用户定义的类,无需修改。
import base64
from collections import MutableMapping
import json
import pickle
class PythonObjectEncoder(json.JSONEncoder):
def default(self, obj):
return {'_python_object':
base64.b64encode(pickle.dumps(obj)).decode('utf-8') }
def as_python_object(dct):
if '_python_object' in dct:
return pickle.loads(base64.b64decode(dct['_python_object']))
return dct
# based on AttrDict -- https://code.activestate.com/recipes/576972-attrdict
class TreeNode(MutableMapping):
""" dict-like object whose contents can be accessed as attributes. """
def __init__(self, name, children=None):
self.name = name
self.children = list(children) if children is not None else []
def __getitem__(self, key):
return self.__getattribute__(key)
def __setitem__(self, key, val):
self.__setattr__(key, val)
def __delitem__(self, key):
self.__delattr__(key)
def __iter__(self):
return iter(self.__dict__)
def __len__(self):
return len(self.__dict__)
tree = TreeNode('Parent')
tree.children.append(TreeNode('Child 1'))
child2 = TreeNode('Child 2')
tree.children.append(child2)
child2.children.append(TreeNode('Grand Kid'))
child2.children[0].children.append(TreeNode('Great Grand Kid'))
json_str = json.dumps(tree, cls=PythonObjectEncoder, indent=4)
print('json_str:', json_str)
pyobj = json.loads(json_str, object_hook=as_python_object)
print(type(pyobj))
输出:
json_str: {
"_python_object": "gANjX19tYWluX18KVHJlZU5vZGUKcQApgXEBfXECKFgIAAAAY2hp"
"bGRyZW5xA11xBChoACmBcQV9cQYoaANdcQdYBAAAAG5hbWVxCFgH"
"AAAAQ2hpbGQgMXEJdWJoACmBcQp9cQsoaANdcQxoACmBcQ19cQ4o"
"aANdcQ9oACmBcRB9cREoaANdcRJoCFgPAAAAR3JlYXQgR3JhbmQg"
"S2lkcRN1YmFoCFgJAAAAR3JhbmQgS2lkcRR1YmFoCFgHAAAAQ2hp"
"bGQgMnEVdWJlaAhYBgAAAFBhcmVudHEWdWIu"
}
<class '__main__.TreeNode'>