Program Tip

Python namedtuple을 json으로 직렬화

programtip 2020. 11. 14. 11:00
반응형

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를 제공합니다. 필요한 경우 호환 모드를 사용하여 간단한 개체를 출력하지만 다시 디코딩하는 데 도움이되는 기본값을 선호합니다.


이것은 오래된 질문입니다. 하나:

같은 질문을 가진 모든 사람들을위한 제안입니다.의 사적 또는 내부 기능 중 하나를 사용하는 것에 대해 신중하게 생각 NamedTuple하십시오. 이전에 가지고 있었고 시간이 지남에 따라 다시 변경 될 것입니다.

For example, if your NamedTuple is a flat value object and you're only interested in serializing it and not in cases where it is nested into another object, you could avoid the troubles that would come up with __dict__ being removed or _as_dict() changing and just do something like (and yes this is Python 3 because this answer is for the present):

from typing import NamedTuple

class ApiListRequest(NamedTuple):
  group: str="default"
  filter: str="*"

  def to_dict(self):
    return {
      'group': self.group,
      'filter': self.filter,
    }

  def to_json(self):
    return json.dumps(self.to_dict())

I tried to use the default callable kwarg to dumps in order to do the to_dict() call if available, but that didn't get called as the NamedTuple is convertible to a list.

참고URL : https://stackoverflow.com/questions/5906831/serializing-a-python-namedtuple-to-json

반응형