为了账号安全,请及时绑定邮箱和手机立即绑定

使用Python> = 2.7将嵌套的namedtuple序列化为JSON

使用Python> = 2.7将嵌套的namedtuple序列化为JSON

繁花不似锦 2021-03-19 14:15:06
我有一个类似于CalvinKrishy的问题 Samplebias的解决方案无法使用我拥有的数据。我正在使用Python 2.7。数据如下:元组>>> a_t = namedtuple('a','f1 words')>>> word_t = namedtuple('word','f2 value')>>> w1 = word_t(f2=[0,1,2], value='abc')>>> w2 = word_t(f2=[3,4], value='def')>>> a1 = a_t(f1=[0,1,2,3,4],words=[w1, w2])>>> a1a(f1=[0, 1, 2, 3, 4], words=[word(f2=[0, 1, 2], value='abc'), word(f2=[3, 4], value='def')])辞典>>> w3 = {}>>> w3['f2'] = [0,1,2]>>> w3['value'] = 'abc'>>> w4 = {}>>> w4['f2'] = [3,4]>>> w4['value'] = 'def'>>> a2 = {}>>> a2['f1'] = [0, 1, 2, 3, 4]>>> a2['words'] = [w3,w4]>>> a2{'f1': [0, 1, 2, 3, 4], 'words': [{'f2': [0, 1, 2], 'value': 'abc'}, {'f2': [3, 4], 'value': 'def'}]}如您所见,a1和a2都相同,除了一个命名为tuple,另一个是dict。但是json.dumps是不同的:>>> json.dumps(a1._asdict())'{"f1": [0, 1, 2, 3, 4], "words": [[[0, 1, 2], "abc"], [[3, 4], "def"]]}'>>> json.dumps(a2)'{"f1": [0, 1, 2, 3, 4], "words": [{"f2": [0, 1, 2], "value": "abc"}, {"f2": [3, 4], "value": "def"}]}'我想要a1的json格式完全像对a2所做的那样。
查看完整描述

2 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

问题出在使用namedtuple._asdict,而不是json.dumps。如果您一起看代码,namedtuple(..., verbose=True)将会看到以下内容:


def _asdict(self):

    'Return a new OrderedDict which maps field names to their values'

    return OrderedDict(zip(self._fields, self))

实际上,只有顶层被更改为OrderedDict,所有包含的元素都保持不变。这意味着嵌套的namedtuples仍然是tuple子类,并且(正确地)进行了序列化等等。


如果可以接受对特定转换函数的调用(如对的调用_asdict),则可以编写自己的函数。


def namedtuple_asdict(obj):

  if hasattr(obj, "_asdict"): # detect namedtuple

    return OrderedDict(zip(obj._fields, (namedtuple_asdict(item) for item in obj)))

  elif isinstance(obj, basestring): # iterables - strings

     return obj

  elif hasattr(obj, "keys"): # iterables - mapping

     return OrderedDict(zip(obj.keys(), (namedtuple_asdict(item) for item in obj.values())))

  elif hasattr(obj, "__iter__"): # iterables - sequence

     return type(obj)((namedtuple_asdict(item) for item in obj))

  else: # non-iterable cannot contain namedtuples

    return obj


json.dumps(namedtuple_asdict(a1))

# prints '{"f1": [0, 1, 2, 3, 4], "words": [{"f2": [0, 1, 2], "value": "abc"}, {"f2": [3, 4], "value": "def"}]}'

如您所见,最大的问题是嵌套结构不是 namedtuples而是可以包含它们。


查看完整回答
反对 回复 2021-03-30
?
DIEA

TA贡献1820条经验 获得超2个赞

这是我使用的版本,改编自宫城先生的版本。我使用isinstancewithcollections.abc代替hasattr,然后_type在结果字典中将一个键命名为namedtuple类的名称。


import collections.abc


def _nt_to_dict(obj):

    recurse = lambda x: map(_nt_to_dict, x)

    obj_is = lambda x: isinstance(obj, x)

    if obj_is(tuple) and hasattr(obj, '_fields'):  # namedtuple

        fields = zip(obj._fields, recurse(obj))

        class_name = obj.__class__.__name__

        return dict(fields, **{'_type': class_name})

    elif obj_is(collections.abc.Mapping):

        return type(obj)(zip(obj.keys(), recurse(obj.values())))

    elif obj_is(collections.abc.Iterable) and not obj_is(str):

        return type(obj)(recurse(obj))

    else:

        return obj


查看完整回答
反对 回复 2021-03-30
  • 2 回答
  • 0 关注
  • 171 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信