将名为tuple的Python序列化为json

将名为tuple的Python序列化为json,python,json,namedtuple,Python,Json,Namedtuple,在保留字段名的情况下,建议使用什么方法将字符串序列化为json 将namedtuple序列化为json只会导致值被序列化,字段名在翻译过程中丢失。我希望在json初始化时也保留这些字段,因此执行了以下操作: class foobar(namedtuple('f', 'foo, bar')): __slots__ = () def __iter__(self): yield self._asdict() 上述内容按我的预期序列化为json,在我使用的其他地方(属性

在保留字段名的情况下,建议使用什么方法将字符串序列化为json

namedtuple
序列化为json只会导致值被序列化,字段名在翻译过程中丢失。我希望在json初始化时也保留这些字段,因此执行了以下操作:

class foobar(namedtuple('f', 'foo, bar')):
    __slots__ = ()
    def __iter__(self):
        yield self._asdict()
上述内容按我的预期序列化为json,在我使用的其他地方(属性访问等)表现为
namedtuple
,但在迭代时会出现类似于非元组的结果(这对于我的用例来说很好)


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

这是一个相当棘手的问题,因为
namedtuple()
是一个返回从
元组派生的新类型的工厂。一种方法是让您的类也继承自
UserDict.DictMixin
,但
tuple.\uuu getitem\uuuu
已经定义,并且需要一个表示元素位置的整数,而不是其属性的名称:

>>> f = foobar('a', 1)
>>> f[0]
'a'
namedtuple的核心是一个奇怪的JSON,因为它实际上是一个定制类型,其键名作为类型定义的一部分固定下来,而不像字典中的键名存储在实例中。这可以防止您“往返”命名元组,例如,如果没有其他一些信息,例如dict
{'a':1',#u type':'foobar'}
中的特定于应用程序的类型标记,您无法将字典解码回命名元组,这有点老套

这并不理想,但是如果您只需要将namedtuples编码到字典中,另一种方法是扩展或修改JSON编码器以适应这些类型的特殊情况。下面是一个将Python
json.jsonecoder
子类化的示例。这解决了确保嵌套的namedtuple正确转换为字典的问题:

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

看起来您过去可以使用子类
simplejson.jsonecoder
来实现这一点,但是使用最新的simplejson代码,情况就不再是这样了:您必须实际修改项目代码。我看不出simplejson为什么不支持namedtuple,所以我分叉了这个项目,添加了namedtuple支持,我很高兴。如果你现在需要修理,就从我的叉子上拔下来


EDIT:看起来最新版本的
simplejson
现在通过
namedtuple\u as\u object
选项本机支持这一点,该选项默认为
True

,如果它只是一个
namedtuple
,则使用它的
\u asdict()
方法将起作用(Python>=2.7)


它递归地将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'}

我为此编写了一个库:

它可以在命名元组之间来回移动

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


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

更方便的解决方案是使用装饰器(它使用受保护的字段
\u 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))

这是一个老问题。然而:

对于所有有相同问题的人,请仔细考虑使用
NamedTuple
的任何私有或内部功能,因为它们以前有过,并且会随着时间的推移再次改变

例如,如果您的
NamedTuple
是一个平面值对象,并且您只对序列化它感兴趣,而不关心它嵌套到另一个对象中的情况,那么您可以避免删除
\uuu dict\uuu
或更改
\u as\u dict()
所带来的问题,只需执行以下操作(是的,这是Python3,因为这是目前的答案):


我尝试使用
default
callable-kwarg-to
dumps
来执行
to_dict()
调用(如果可用),但没有调用,因为
NamedTuple
可转换为列表。

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

以下是我对这个问题的看法。它序列化了对NamedTuple进行初始化,处理折叠的NamedTuple和其中的列表

def recursive_to_dict(obj: Any) -> dict:
_dict = {}

if isinstance(obj, tuple):
    node = obj._asdict()
    for item in node:
        if isinstance(node[item], list): # Process as a list
            _dict[item] = [recursive_to_dict(x) for x in (node[item])]
        elif getattr(node[item], "_asdict", False): # Process as a NamedTuple
            _dict[item] = recursive_to_dict(node[item])
        else: # Process as a regular element
            _dict[item] = (node[item])
return _dict

无法使用本机python json库正确序列化namedtuples。它将始终将元组视为列表,并且无法覆盖默认序列化程序来更改此行为。如果对象嵌套,则情况更糟

最好使用更健壮的库,如:

=>


simplejson.dump()
而不是
json.dump
完成了这项工作。但是它可能会慢一些。

在其核心,namedtuple对于json来说是一个奇怪的适合,因为它实际上是一个定制类型,其键名作为类型定义的一部分固定,而不像字典中的键名存储在实例中。非常有见地的注释。我没有想过谢谢。我喜欢namedtuples,因为它们提供了一个很好的不可变结构和属性命名便利。我接受你的答案。话虽如此,Java的序列化机制提供了对对象序列化方式的更多控制,我很想知道为什么Python中似乎不存在这样的钩子。这是我的第一个方法,b但它实际上不起作用(对我来说)。
>json.dumps(foobar('x','y'),cls=MyEncoder)
啊,在python 2.7+\u中,代码不再是JSONECODER的一种方法。@calvin谢谢,我发现命名的倍数也很有用,希望有更好的解决方案将其递归编码为JSON。@zeekay Yep,似乎在2.7+中,他们将其隐藏起来,以便不再被重写。这令人失望。您的编辑是正确的答案。simplejson ser序列化namedtuples的方式与json不同(我的观点是:更好)
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())
def recursive_to_dict(obj: Any) -> dict:
_dict = {}

if isinstance(obj, tuple):
    node = obj._asdict()
    for item in node:
        if isinstance(node[item], list): # Process as a list
            _dict[item] = [recursive_to_dict(x) for x in (node[item])]
        elif getattr(node[item], "_asdict", False): # Process as a NamedTuple
            _dict[item] = recursive_to_dict(node[item])
        else: # Process as a regular element
            _dict[item] = (node[item])
return _dict
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))
{
    "width":10,
    "height":20
}