Given the code sample below, how can I serialize these class instances with JSON using Python 3?
class TreeNode():
def __init__(self, name):
self.name = name
self.children = []
When I try to do a json.dumps
I get the following error:
TypeError: <TreeNode object at 0x7f6sf4276f60> is not JSON serializable
I was then able to find that if I set the default to json.dumps
to return a __dict__
I could serialize it fine but then doing a json.loads
becomes an issue.
I can find a lot of custom encoder/decoder examples with basic strings but none where there is a list, in this case self.children. The children list will hold child nodes and their children other nodes. I need a way to get all of it.
Use toJSON() Method to make class JSON serializable So we don't need to write custom JSONEncoder. This new toJSON() serializer method will return the JSON representation of the Object. i.e., It will convert custom Python Object to JSON string.
The json module exposes two methods for serializing Python objects into JSON format. dump() will write Python data to a file-like object. We use this when we want to serialize our Python data to an external JSON file. dumps() will write Python data to a string in JSON format.
It is a format that encodes the data in string format. JSON is language independent and because of that, it is used for storing or transferring data in files. The conversion of data from JSON object string is known as Serialization and its opposite string JSON object is known as Deserialization.
JSON is a ubiquitous human-readable data serialization format that is supported by almost every popular programming language. JSON's data structures closely represent common objects in many languages, e.g. a Python dict can be represented by a JSON object , and a Python list by a JSON array .
Since you're dealing with a tree structure, it's natural to use nested dictionaries. The snippet of code below creates a subclass of dict
and uses itself as the underlying __dict__
of the instance — which is an interesting and useful trick I've run across in many different contexts:
Is it preferable to return an anonymous class or an object to use as a 'struct'? (stackoverflow)
How to use a dot “.” to access members of dictionary? (stackoverflow)
jsobject.py
(PyDoc.net)
Making Python Objects that act like Javascript Objects
(James Robert's blog)
AttrDict
(ActiveState recipe)
Dictionary with attribute-style access
(ActiveState recipe)
…so often in fact, that I consider it to be a (less well-known) Python idiom.
class TreeNode(dict):
def __init__(self, name, children=None):
super().__init__()
self.__dict__ = self
self.name = name
self.children = list(children) if children is not None else []
This solves half the serialization battle, but when the data produced is read back in with json.loads()
it will be a regular dictionary object, not an instance of TreeNode
. This is because JSONEncoder
can encode dictionaries (and subclasses of them) itself.
One way to address that is add an alternative constructor method to the TreeNode
class that can be called to reconstruct the data structure from the nested dictionary that json.loads()
returns.
Here's what I mean:
...
@staticmethod
def from_dict(dict_):
""" Recursively (re)construct TreeNode-based tree from dictionary. """
node = TreeNode(dict_['name'], dict_['children'])
# node.children = [TreeNode.from_dict(child) for child in node.children]
node.children = list(map(TreeNode.from_dict, node.children))
return node
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, 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, indent=2))
Output:
{
"name": "Parent",
"children": [
{
"name": "Child 1",
"children": []
},
{
"name": "Child 2",
"children": [
{
"name": "Grand Kid",
"children": [
{
"name": "Great Grand Kid",
"children": []
}
]
}
]
}
]
}
pyobj class: TreeNode
{
"name": "Parent",
"children": [
{
"name": "Child 1",
"children": []
},
{
"name": "Child 2",
"children": [
{
"name": "Grand Kid",
"children": [
{
"name": "Great Grand Kid",
"children": []
}
]
}
]
}
]
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With