102

namedtupleフィールド名を保持したままaをjsonにシリアル化するための推奨される方法は何ですか?

anamedtupleをjsonにシリアル化すると、値のみがシリアル化され、フィールド名が変換で失われます。json化したときにフィールドも保持したいので、次のようにしました。

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

上記は、期待どおりにjsonにシリアル化されnamedtuple、反復中の非タプルのような結果(私のユースケースでは問題ありません)を除いて、私が使用する他の場所(属性アクセスなど)と同じように動作します。

フィールド名を保持したままjsonに変換する「正しい方法」とは何ですか?

4

11 に答える 11

93

シリアル化しようとしているのが1つだけの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}'
于 2013-04-03T23:55:52.933 に答える
57

namedtuple()から派生した新しい型を返すファクトリであるため、これはかなり注意が必要tupleです。1つのアプローチは、クラスもから継承することですUserDict.DictMixinが、tuple.__getitem__すでに定義されており、属性の名前ではなく、要素の位置を示す整数を期待しています。

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

本質的に、namedtupleはJSONに奇妙に適合します。これは、キー名がインスタンス内に格納される辞書とは異なり、キー名がタイプ定義の一部として固定されているカスタムビルドのタイプであるためです。{'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"}}
于 2011-05-06T14:52:48.313 に答える
22

以前はサブクラス化してこれを機能させることができたようですsimplejson.JSONEncoderが、最新のsimplejsonコードでは、もはやそうではありません。実際にプロジェクトコードを変更する必要があります。simplejsonがnamedtuplesをサポートしない理由がわからないので、プロジェクトをフォークし、namedtupleのサポートを追加しました。現在、ブランチがメインプロジェクトに戻されるのを待っています。今すぐ修正が必要な場合は、フォークから引っ張ってください。

編集simplejson:現在の最新バージョンは、オプションでこれをネイティブにサポートしているように見えます。namedtuple_as_objectデフォルトはTrue。です。

于 2011-07-04T13:21:09.253 に答える
6

これを行うためのライブラリを作成しました:https ://github.com/ltworf/typedload

名前付きタプルとの間を行き来することができます。

リスト、セット、列挙型、共用体、デフォルト値など、非常に複雑なネストされた構造をサポートします。最も一般的なケースをカバーする必要があります。

編集:ライブラリはdataclassおよびattrクラスもサポートします。

于 2018-04-07T22:20:22.137 に答える
3

これは、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'}
于 2018-03-22T13:08:52.010 に答える
3

より便利な解決策は、デコレータを使用することです(保護されたフィールドを使用します_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))
于 2018-09-07T14:19:27.403 に答える
2

jsonplusライブラリは、NamedTupleインスタンスのシリアライザーを提供します。必要に応じて互換モードを使用して単純なオブジェクトを出力しますが、デコードバックに役立つため、デフォルトを使用することをお勧めします。

于 2019-07-01T20:46:13.850 に答える
2

ネイティブのpythonjsonライブラリを使用してnamedtuplesを正しくシリアル化することは不可能です。タプルは常にリストとして表示され、デフォルトのシリアライザーをオーバーライドしてこの動作を変更することはできません。オブジェクトがネストされているとさらに悪いことになります。

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

=>

{
    "width":10,
    "height":20
}
于 2020-05-22T05:55:26.287 に答える
1

これは古い質問です。でも:

同じ質問を持つすべての人への提案は、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呼び出し可能なkwargtoを使用しようとしましたが、リストに変換できるため、呼び出されませんでした。dumpsto_dict()NamedTuple

于 2018-11-29T13:00:19.227 に答える
1

simplejson.dump()代わりにjson.dump仕事をします。ただし、遅い場合があります。

于 2020-11-05T00:13:53.443 に答える
0

これが私の問題に対する見方です。NamedTupleをシリアル化し、折りたたまれたNamedTuplesとその中のリストを処理します

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
于 2020-05-14T09:29:16.920 に答える