Serializowanie Pythona nazwanego json

Jaki jest zalecany sposób serializacji a namedtuple do jsona z zachowanymi nazwami pól?

Serializacja a namedtuple do json powoduje serializację tylko wartości i utratę nazw pól podczas tłumaczenia. Chciałbym, aby pola również zostały zachowane, gdy json-ized i stąd zrobił następujące:
class foobar(namedtuple('f', 'foo, bar')):
    __slots__ = ()
    def __iter__(self):
        yield self._asdict()

Powyższe serializuje się do json tak, jak oczekuję i zachowuje się jak namedtuple w innych miejscach, których używam (dostęp do atrybutów itp.,) z wyjątkiem braku krotki jak wyniki podczas iteracja go (co jest dobre dla mojego przypadku użycia).

Jaki jest "prawidłowy sposób" konwersji do json z zachowanymi nazwami pól?

Author: Russ, 2011-05-06

6 answers

Jest to dość trudne, ponieważ namedtuple() jest fabryką, która zwraca nowy typ pochodzący z tuple. Jedną z metod jest dziedziczenie klasy z UserDict.DictMixin, ale {[5] } jest już zdefiniowana i oczekuje liczby całkowitej oznaczającej pozycję elementu, a nie nazwę jego atrybutu:

>>> f = foobar('a', 1)
>>> f[0]
'a'
W przeciwieństwie do słownika, w którym klucz jest zdefiniowany jako klucz, jest to typ Niestandardowy, którego nazwy są stałe w ramach definicji typu . nazwy są przechowywane wewnątrz instancji. Zapobiega to "round-potknięciu" nazwanego tuple, np. nie można dekodować słownika z powrotem do nazwanego tuple bez innych informacji, takich jak znacznik typu specyficznego dla aplikacji w dict {'a': 1, '#_type': 'foobar'}, co jest nieco trudne.

Nie jest to idealne rozwiązanie, ale jeśli potrzebujesz tylko zakodować nazwapliki do słowników, innym podejściem jest rozszerzenie lub zmodyfikowanie kodera JSON na specjalne przypadki tego typu. Oto przykład podkategorii Python json.JSONEncoder. To rozwiązuje problem zapewnienia, że zagnieżdżone namedtuples są poprawnie konwertowane do słowników: {]}

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"}}
 45
Author: samplebias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-06 14:52:48

Jeśli jest to tylko jedna namedtuple chcesz serializować, użycie jej metody _asdict() będzie działać (z Pythonem >= 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}'
 46
Author: benselme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-09 14:31:57

Wygląda na to, że kiedyś byłeś w stanie podklasować simplejson.JSONEncoder, aby to działało, ale z najnowszym kodem simplejson już tak nie jest: musisz faktycznie zmodyfikować kod projektu. Nie widzę powodu, dla którego simplejson nie miałby obsługiwać namedtuples, więc rozwidliłem projekt, dodałem namedtuple support, I jestem obecnie czekam, aż moja gałąź zostanie ściągnięta z powrotem do głównego projektu . Jeśli potrzebujesz poprawek teraz, po prostu wyciągnij z mojego widelca.

EDIT : wygląda jak najnowszy wersje simplejson teraz natywnie wspierają to za pomocą opcji namedtuple_as_object, która domyślnie wynosi True.

 17
Author: singingwolfboy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-06 20:17:53

Napisałem do tego bibliotekę: https://github.com/ltworf/typedload

Może przechodzić od I Do nazw-krotki i z powrotem.

Obsługuje dość skomplikowane zagnieżdżone struktury, z listami, zestawami, enumami, uniami, wartościami domyślnymi. Powinna ona obejmować najczęstsze przypadki.

 3
Author: LtWorf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-07 22:20:22

Rekurencyjnie konwertuje namedTuple dane do json.

print(m1)
## Message(id=2, agent=Agent(id=1, first_name='asd', last_name='asd', mail='[email protected]'), customer=Customer(id=1, first_name='asd', last_name='asd', mail='[email protected]', phone_number=123123), type='image', content='text', media_url='h.com', la=123123, ls=4512313)

def reqursive_to_json(obj):
    _json = {}

    if isinstance(obj, tuple):
        datas = obj._asdict()
        for data in datas:
            if isinstance(datas[data], tuple):
                _json[data] = (reqursive_to_json(datas[data]))
            else:
                 print(datas[data])
                _json[data] = (datas[data])
    return _json

data = reqursive_to_json(m1)
print(data)
{'agent': {'first_name': 'asd',
'last_name': 'asd',
'mail': '[email protected]',
'id': 1},
'content': 'text',
'customer': {'first_name': 'asd',
'last_name': 'asd',
'mail': '[email protected]',
'phone_number': 123123,
'id': 1},
'id': 2,
'la': 123123,
'ls': 4512313,
'media_url': 'h.com',
'type': 'image'}
 1
Author: Tolgahan ÜZÜN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-22 13:08:52

Wygodniejszym rozwiązaniem jest użycie dekoratora(używa on chronionego pola _fields).

Python 2.7+:

import json
from collections import namedtuple, OrderedDict

def json_serializable(cls):
    def as_dict(self):
        yield OrderedDict(
            (name, value) for name, value in zip(
                self._fields,
                iter(super(cls, self).__iter__())))
    cls.__iter__ = as_dict
    return cls

#Usage:

C = json_serializable(namedtuple('C', 'a b c'))
print json.dumps(C('abc', True, 3.14))

# or

@json_serializable
class D(namedtuple('D', 'a b c')):
    pass

print json.dumps(D('abc', True, 3.14))

Python 3.6.6+:

import json
from typing import TupleName

def json_serializable(cls):
    def as_dict(self):
        yield {name: value for name, value in zip(
            self._fields,
            iter(super(cls, self).__iter__()))}
    cls.__iter__ = as_dict
    return cls

# Usage:

@json_serializable
class C(NamedTuple):
    a: str
    b: bool
    c: float

print(json.dumps(C('abc', True, 3.14))
 1
Author: Dmitry T.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-07 14:19:27