How do you encode AttrDict objects in json?
import sys, json, attrdict
ad = attrdict.AttrDict({'else': 1, 'inner': attrdict.AttrDict({'something': 2})})
json.dump(ad, sys.stdout)
This fails with TypeError: a{'something': 2} is not JSON serializable
Using a custom encoder like this works but I have to reference the private _mapping property:
import sys, json, attrdict
class attrDictEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, attrdict.AttrDict):
return obj._mapping
return json.JSONEncoder.default(self, obj)
ad = attrdict.AttrDict({'else': 1, 'inner': attrdict.AttrDict({'something': 2})})
json.dump(ad, sys.stdout, cls=attrDictEncoder)
is there a better/cleaner way? I don't want to have to rebuild the dicts, item by item into a plain dicts.
Your code was very close. The following solution passes a function into the JSON encoder, which convertsAttrDicts into normal dictionaries. The normal JSON machinery calls it when it finds a nonstandard type, like anAttrDict.
import json, attrdict
def as_attrdict(val):
if not isinstance(val, attrdict.AttrDict):
raise TypeError('not AttrDict')
return dict(val)
ad = attrdict.AttrDict({'else': 1,
'inner': attrdict.AttrDict({'something': 2})})
print json.dumps(ad, default=as_attrdict)
{"inner": {"something": 2}, "else": 1}
An AttrDict is a dictionary-like object that allows its elements to be accessed both as keys and as attributes.
Thanks to user2357112 for simplifying the code.
It's possible to fool json into believing it is handling a real dict by subclassing dict and then monkey patching the methods that json uses to create the json object string. This way we can give json a dummy class that just calls the relevant methods of the given AttrDict.
def as_attrdict(val):
if not isinstance(val, AttrDict):
raise TypeError('not AttrDict')
return AttrDictForJson(val)
class AttrDictForJson(dict):
def __init__(self, attrdict):
super().__init__()
self.items = attrdict.items
self._len = attrdict.__len__
# key creation necessary for json.dump to work with CPython
# This is because optimised json bypasses __len__ on CPython
if self._len() != 0:
self[None] = None
def __len__(self):
return self._len()
Usage:
json_string = dumps(attrdict, default=as_attrdict)
I've tested this on python 3.4, if you have a different version of python then the above may require some tweaking, such as changing attrdict.items to attrdict.iteritems
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