This is pretty tricky since namedtuple() is a factory that returns a new type derived from tuple . One approach would be to have your class also inherit from UserDict.DictMixin , but tuple.__getitem__ already defined and expects an integer indicating the position of the element, and not the name of its attribute:
>>> f = foobar('a', 1) >>> f[0] 'a'
Essentially, namedtuple is an odd position for JSON, since it really is a custom 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 the "circular disconnect" of the named set, for example. you cannot decode the dictionary back to namedtuple without any other piece of information, such as an application type marker in dict {'a': 1, '#_type': 'foobar'} , which is a bit hacked.
This is not ideal, but if you only need to code namedtuples into dictionaries, another approach is to extend or modify your JSON encoder in special cases of these types. The following is an example of Python subclasses json.JSONEncoder . This solves the problem of ensuring the correct conversion of nested namedtuples into dictionaries:
from collections import namedtuple from json import JSONEncoder class 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 chunk class foobar(namedtuple('f', 'foo, bar')): pass enc = 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"}}
samplebias May 6 '11 at 14:52 2011-05-06 14:52
source share