Serializing a Python namedtuple to json Serializing a Python namedtuple to json python python

Serializing a Python namedtuple to json


If it's just one namedtuple you're looking to serialize, using its _asdict() method will work (with Python >= 2.7)

>>> from collections import namedtuple>>> import json>>> FB = namedtuple("FB", ("foo", "bar"))>>> fb = FB(123, 456)>>> json.dumps(fb._asdict())'{"foo": 123, "bar": 456}'


This is pretty tricky, since namedtuple() is a factory which returns a new type derived from tuple. One approach would be to have your class also inherit from UserDict.DictMixin, but tuple.__getitem__ is already defined and expects an integer denoting the position of the element, not the name of its attribute:

>>> f = foobar('a', 1)>>> f[0]'a'

At its heart the namedtuple is an odd fit for JSON, since it is really a custom-built type whose key names are fixed as part of the type definition, unlike a dictionary where key names are stored inside the instance. This prevents you from "round-tripping" a namedtuple, e.g. you cannot decode a dictionary back into a namedtuple without some other a piece of information, like an app-specific type marker in the dict {'a': 1, '#_type': 'foobar'}, which is a bit hacky.

This is not ideal, but if you only need to encode namedtuples into dictionaries, another approach is to extend or modify your JSON encoder to special-case these types. Here is an example of subclassing the Python json.JSONEncoder. This tackles the problem of ensuring that nested namedtuples are properly converted to dictionaries:

from collections import namedtuplefrom json import JSONEncoderclass MyEncoder(JSONEncoder):    def _iterencode(self, obj, markers=None):        if isinstance(obj, tuple) and hasattr(obj, '_asdict'):            gen = self._iterencode_dict(obj._asdict(), markers)        else:            gen = JSONEncoder._iterencode(self, obj, markers)        for chunk in gen:            yield chunkclass foobar(namedtuple('f', 'foo, bar')):    passenc = MyEncoder()for obj in (foobar('a', 1), ('a', 1), {'outer': foobar('x', 'y')}):    print enc.encode(obj){"foo": "a", "bar": 1}["a", 1]{"outer": {"foo": "x", "bar": "y"}}


It looks like you used to be able to subclass simplejson.JSONEncoder to make this work, but with the latest simplejson code, that is no longer the case: you have to actually modify the project code. I see no reason why simplejson should not support namedtuples, so I forked the project, added namedtuple support, and I'm currently waiting for my branch to be pulled back into the main project. If you need the fixes now, just pull from my fork.

EDIT: Looks like the latest versions of simplejson now natively support this with the namedtuple_as_object option, which defaults to True.