将Python namedtuple序列化为json

python json namedtuple

30065 观看

8回复

1830 作者的声誉

namedtuple保留字段名称的情况下序列化到json 的推荐方法是什么?

将a namedtuple序列化为json只会导致序列化的值,并且字段名称在转换时会丢失。我想在json化时保留字段,因此做了以下事情:

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

上面按照我的预期序列化到json,并且namedtuple在我使用的其他地方行为(属性访问等),除了在迭代它时使用非元组结果(这对我的用例来说很好)。

在保留字段名称的情况下转换为json的“正确方法”是什么?

作者: calvinkrishy 的来源 发布者: 2011 年 5 月 6 日

回应 (8)


47

30421 作者的声誉

决定

这非常棘手,因为它namedtuple()是一个工厂,它返回一个派生自的新类型tuple。一种方法是让你的类继承UserDict.DictMixin,但是tuple.__getitem__已经定义了并且需要一个表示元素位置的整数,而不是它的属性名称:

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

在其核心,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"}}
作者: samplebias 发布者: 06.05.2011 02:52

20

3591 作者的声誉

看起来你曾经能够通过子类simplejson.JSONEncoder来实现这个功能,但是使用最新的simplejson代码,情况就不再那样了:你必须实际修改项目代码。我看不出为什么simplejson不应该支持namedtuples,所以我分叉了项目,添加了namedtuple支持,我正在等待我的分支被拉回到主项目中。如果你现在需要修复,只需从我的叉子拉。

编辑:看起来simplejson现在的最新版本本身支持这个namedtuple_as_object选项,默认为True

作者: singingwolfboy 发布者: 04.07.2011 01:21

53

2380 作者的声誉

如果它只是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}'
作者: benselme 发布者: 03.04.2013 11:55

1

168 作者的声誉

它以递归方式将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'}
作者: Tolgahan ÜZÜN 发布者: 22.03.2018 01:08

4

4567 作者的声誉

我为此写了一个库:https//github.com/ltworf/typedload

它可以来自和命名元组和返回。

它支持非常复杂的嵌套结构,包括列表,集合,枚举,联合,默认值。它应涵盖最常见的情况。

编辑:该库还支持dataclass和attr类。

作者: LtWorf 发布者: 07.04.2018 10:20

1

504 作者的声誉

有一个更方便的解决方案是使用装饰器(它使用受保护的字段_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))
作者: Dmitry T. 发布者: 07.09.2018 02:19

0

28078 作者的声誉

这是一个老问题。然而:

对于所有具有相同问题的人的建议,请仔细考虑使用之前的任何私有或内部功能,NamedTuple因为他们以前会随着时间的推移再次发生变化。

例如,如果你NamedTuple是一个扁平值对象并且你只对序列化它感兴趣,而不是在它嵌套到另一个对象的情况下,你可以避免__dict__因删除或_as_dict()改变而产生的麻烦而只是做类似的事情。 (是的,这是Python 3,因为这个答案适用于现在):

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())

我试图使用default可调用的kwarg来dumps进行to_dict()调用(如果可用),但是没有被调用,因为它NamedTuple可以转换为列表。

作者: dlamblin 发布者: 29.11.2018 01:00

0

1921 作者的声誉

jsonplus库提供了NamedTuple实例串行。如果需要,使用其兼容模式输出简单对象,但更喜欢默认值,因为它有助于解码。

作者: Gonzalo 发布者: 01.07.2019 08:46
32x32