Serialize and Deserialze Sqlalchemy Nested Objects with JSON
At first we build a relationship :
1 2 3 4 5 6 7 8 9 10 |
from sqlalchemy import Base, Unicode class County(Base): __tablename__ = 'county' name = Column(Unicode(5)) towns = relationship('Town') class Town(Base): __tablename__ = 'town' name = Column(Unicode(5)) county = relationship('County', back_populates='towns') |
and we want to dump a county instance to json like this:
1 |
{"name": "台北市", "towns": [{"name": "大安區"},{"name": "文山區"}]} |
extending JSONEncoder:
1 2 3 4 5 6 7 8 9 10 |
class Encoder(JSONEncoder): def default(self, obj): d = {} if isinstance(obj, County): return {'name': obj.name, 'towns': obj.towns} if isinstance(obj, Town): return {'name': obj.name} d.update(obj.__dict__) return d |
1 |
json.dumps(county_instance, cls=Encoder) |
if you want to decode your json file, simply supply a function for object_hood attribute:
1 2 3 4 5 6 7 8 9 |
def hook(dict): if dict.get('towns'): county = County() county.__dict__.update(dict) return county else: town = Town() town.__dict__.update(dict) return town |
1 |
json.load(file, object_hook=hook) |
object_hook is an optional function that will be called with the result of any object literal decoded (a dict). The return value of object_hook will be used instead of the dict. This feature can be used to implement custom decoders (e.g. JSON-RPC class hinting).
when a json file loads, we can print the parameter “dict” which pass in to object_hood function, the whole process will loads from the most inner object (a dict) to the outer objects look like this:
1 2 3 4 5 6 |
{"name": "文山區"} {"name": "大安區"} {"name": "台北市", "towns": [ <Town object at 0x0000018FFE817B38>, <Town object at 0x0000018FFE817B38> ]} |