cal*_*shy 69 python json namedtuple
在namedtuple保留字段名称的情况下序列化到json 的推荐方法是什么?
将a namedtuple序列化为json只会导致序列化的值和字段名称在翻译中丢失.我想在json化时保留字段,因此做了以下事情:
class foobar(namedtuple('f', 'foo, bar')):
__slots__ = ()
def __iter__(self):
yield self._asdict()
Run Code Online (Sandbox Code Playgroud)
上面按照我的预期序列化到json,并且namedtuple在我使用的其他地方行为(属性访问等),除了在迭代它时使用非元组结果(这对我的用例来说很好).
在保留字段名称的情况下转换为json的"正确方法"是什么?
ben*_*lme 59
如果它只是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}'
Run Code Online (Sandbox Code Playgroud)
sam*_*ias 49
这非常棘手,因为它namedtuple()是一个返回派生自的新类型的工厂tuple.一种方法是让你的类继承UserDict.DictMixin,但是tuple.__getitem__已经定义并期望一个表示元素位置的整数,而不是它的属性名称:
>>> f = foobar('a', 1)
>>> f[0]
'a'
Run Code Online (Sandbox Code Playgroud)
在其核心,namedtuple是一个奇怪的JSON,因为它实际上是一个自定义类型,其键名作为类型定义的一部分被修复,不像一个字典,其中键名存储在实例中.这可以防止你"绕过"一个namedtuple,例如你不能将字典解码回一个namedTuple而没有其他一些信息,比如dict中特定于应用程序的类型标记{'a': 1, '#_type': 'foobar'},这有点像hacky.
这并不理想,但如果您只需要将 namedtuples 编码为字典,另一种方法是将JSON编码器扩展或修改为特殊情况下的这些类型.这是一个子类化Python的例子json.JSONEncoder.这解决了确保嵌套的namedtuples正确转换为字典的问题:
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"}}
Run Code Online (Sandbox Code Playgroud)
sin*_*boy 20
看起来你曾经能够通过子类simplejson.JSONEncoder来实现这个功能,但是使用最新的simplejson代码,情况就不再那样了:你必须实际修改项目代码.我看不出为什么simplejson不应该支持namedtuples,所以我分叉了项目,添加了namedtuple支持,我正在等待我的分支被拉回到主项目中.如果你现在需要修复,只需从我的叉子拉.
编辑:看起来simplejson现在的最新版本本身支持这个namedtuple_as_object选项,默认为True.
我为此编写了一个库:https : //github.com/ltworf/typedload
它可以往返于命名元组并返回。
它支持非常复杂的嵌套结构,包括列表、集合、枚举、联合、默认值。它应该涵盖最常见的情况。
编辑:该库还支持 dataclass 和 attr 类。
小智 5
有一个更方便的解决方案是使用装饰器(它使用 protected field _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))
Run Code Online (Sandbox Code Playgroud)
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))
Run Code Online (Sandbox Code Playgroud)
使用本机 python json 库无法正确序列化命名元组。它将始终将元组视为列表,并且不可能覆盖默认序列化器来更改此行为。如果对象是嵌套的,情况会更糟。
最好使用更强大的库,例如orjson:
import orjson
from typing import NamedTuple
class Rectangle(NamedTuple):
width: int
height: int
def default(obj):
if hasattr(obj, '_asdict'):
return obj._asdict()
rectangle = Rectangle(width=10, height=20)
print(orjson.dumps(rectangle, default=default))
Run Code Online (Sandbox Code Playgroud)
=>
{
"width":10,
"height":20
}
Run Code Online (Sandbox Code Playgroud)