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