Esto es bastante complicado, ya que namedtuple()
es una fábrica que devuelve un nuevo tipo derivado de tuple
. Un enfoque sería hacer que su clase también herede UserDict.DictMixin
, pero tuple.__getitem__
ya está definida y espera un número entero que denote la posición del elemento, no el nombre de su atributo:
>>> f = foobar('a', 1)
>>> f[0]
'a'
En el fondo, namedtuple es un ajuste extraño para JSON, ya que en realidad es un tipo personalizado cuyos nombres de clave se fijan como parte de la definición de tipo , a diferencia de un diccionario donde los nombres de clave se almacenan dentro de la instancia. Esto evita que usted "haga un viaje redondo" a una tupla con nombre, por ejemplo, no puede decodificar un diccionario en una tupla con nombre sin alguna otra información, como un marcador de tipo específico de aplicación en el dict {'a': 1, '#_type': 'foobar'}
, que es un poco hacky.
Esto no es ideal, pero si solo necesita codificar tuplas con nombre en diccionarios, otro enfoque es extender o modificar su codificador JSON para casos especiales de estos tipos. Aquí hay un ejemplo de subclases de Python json.JSONEncoder
. Esto aborda el problema de garantizar que las tuplas con nombre anidadas se conviertan correctamente en diccionarios:
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"}}