Isso é bastante complicado, pois namedtuple()
é uma fábrica que retorna um novo tipo derivado de tuple
. Uma abordagem seria fazer com que sua classe também herde de UserDict.DictMixin
, mas tuple.__getitem__
já está definida e espera um número inteiro denotando a posição do elemento, não o nome de seu atributo:
>>> f = foobar('a', 1)
>>> f[0]
'a'
Em sua essência, o namedtuple é um ajuste estranho para JSON, pois é realmente um tipo customizado cujos nomes de chave são fixados como parte da definição de tipo , ao contrário de um dicionário onde os nomes de chave são armazenados dentro da instância. Isso evita que você faça um "round-trip" de um namedtuple, por exemplo, você não pode decodificar um dicionário de volta para um namedtuple sem alguma outra informação, como um marcador de tipo específico do aplicativo no dicionário {'a': 1, '#_type': 'foobar'}
, que é um pouco hackeado.
Isso não é o ideal, mas se você só precisa codificar namedtuples em dicionários, outra abordagem é estender ou modificar seu codificador JSON para casos especiais desses tipos. Aqui está um exemplo de subclasse de Python json.JSONEncoder
. Isso resolve o problema de garantir que as duplicatas nomeadas aninhadas sejam convertidas corretamente em dicionários:
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"}}