[python] Python namedtuple을 json으로 직렬화

namedtuple유지 된 필드 이름으로 json 에 직렬화하는 권장 방법은 무엇입니까 ?

a namedtuple를 json으로 직렬화하면 값이 직렬화되고 필드 이름이 변환에서 손실됩니다. 필드도 json-ized 때 유지되기를 원하므로 다음을 수행했습니다.

class foobar(namedtuple('f', 'foo, bar')):
    __slots__ = ()
    def __iter__(self):
        yield self._asdict()

위의 내용은 내가 예상 한대로 json으로 직렬화하고 namedtuple내가 사용하는 다른 장소 (속성 액세스 등)에서 와 같이 동작 합니다. 단, 반복하는 동안 튜플과 같은 결과가 아닌 경우 (내 사용 사례에 적합)를 제외하고는 작동합니다.

필드 이름이 유지 된 상태에서 json으로 변환하는 “올바른 방법”은 무엇입니까?



답변

namedtuple()에서 파생 된 새로운 유형을 반환하는 팩토리 이기 때문에 이것은 매우 까다 롭습니다 tuple. 한 가지 방법은 클래스도에서 상속하도록하는 UserDict.DictMixin것이지만 tuple.__getitem__이미 정의되어 있으며 속성 이름이 아닌 요소의 위치를 ​​나타내는 정수를 예상합니다.

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

기본적으로 namedtuple은 키 이름이 인스턴스 내부에 저장되는 사전과 달리 키 이름이 유형 정의의 일부로 고정 되는 사용자 지정 빌드 유형 이므로 JSON에 이상하게 맞습니다 . 이렇게하면 명명 된 튜플을 “왕복”하는 것을 방지 할 수 있습니다. 예를 들어 dict의 앱 특정 유형 마커와 같은 다른 정보 없이는 사전을 명명 된 튜플로 다시 디코딩 할 수 없습니다 {'a': 1, '#_type': 'foobar'}.

이것은 이상적이지는 않지만 명명 된 튜플을 사전 으로 인코딩하기 만하면되는 경우 다른 접근 방식은 JSON 인코더를 이러한 유형의 특수한 경우로 확장하거나 수정하는 것입니다. 다음은 Python을 서브 클래 싱하는 예입니다 json.JSONEncoder. 이렇게하면 중첩 된 명명 된 튜플이 사전으로 제대로 변환되는지 확인하는 문제가 해결됩니다.

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"}}


답변

namedtuple직렬화하려는 경우에만 해당 _asdict()메서드를 사용 하면 작동합니다 (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}'


답변

simplejson.JSONEncoder이 작업을 수행하기 위해 하위 클래스 를 만들 수 있었던 것처럼 보이지만 최신 simplejson 코드에서는 더 이상 그렇지 않습니다. 실제로 프로젝트 코드를 수정해야합니다. simplejson이 namedtuple을 지원하지 않아야 할 이유가 없으므로 프로젝트를 분기하고 namedtuple 지원을 추가했으며 현재 브랜치가 메인 프로젝트로 돌아 오기를 기다리고 있습니다 . 지금 수정이 필요하면 내 포크에서 가져 오세요.

편집 : 최신 버전처럼 보인다는 simplejson이제 기본적으로 이것을 지원 namedtuple_as_object되는 디폴트 옵션 True.


답변

이 작업을 수행하기 위해 라이브러리를 작성했습니다 : https://github.com/ltworf/typedload

명명 된 튜플 사이를 오가고 돌아갈 수 있습니다.

목록, 집합, 열거 형, 공용체, 기본값이있는 매우 복잡한 중첩 구조를 지원합니다. 대부분의 일반적인 경우를 다루어야합니다.

편집 : 라이브러리는 데이터 클래스 및 속성 클래스도 지원합니다.


답변

namedTuple 데이터를 재귀 적으로 json으로 변환합니다.

print(m1)
## Message(id=2, agent=Agent(id=1, first_name='asd', last_name='asd', mail='2@mai.com'), customer=Customer(id=1, first_name='asd', last_name='asd', mail='2@mai.com', 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': '2@mai.com',
'id': 1},
'content': 'text',
'customer': {'first_name': 'asd',
'last_name': 'asd',
'mail': '2@mai.com',
'phone_number': 123123,
'id': 1},
'id': 2,
'la': 123123,
'ls': 4512313,
'media_url': 'h.com',
'type': 'image'}


답변

더 편리한 해결책은 데코레이터를 사용하는 것입니다 (보호 된 필드를 사용합니다 _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))


답변

jsonplus의 라이브러리는 NamedTuple 인스턴스들에 대한 serializer를 제공합니다. 필요한 경우 호환성 모드를 사용하여 간단한 개체를 출력하지만 다시 디코딩하는 데 도움이되는 기본값을 선호합니다.