Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you enable python's json package to encode attrdict.AttrDict objects?

Tags:

python

json

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.

like image 989
Mike Avatar asked Jul 12 '26 07:07

Mike


2 Answers

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.

source

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)

output

{"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.

like image 180
johntellsall Avatar answered Jul 14 '26 20:07

johntellsall


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

like image 30
Dunes Avatar answered Jul 14 '26 20:07

Dunes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!