1. ホーム
  2. python

[解決済み] Pythonのnamedtupleをjsonにシリアライズする

2022-12-07 18:58:24

質問

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

をシリアライズすると namedtuple をjsonにシリアライズすると、値だけがシリアライズされ、フィールド名は翻訳中に失われてしまいます。json化してもフィールドは残しておきたいので、以下のようにしました。

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

上記は期待通りにjsonにシリアライズされ、以下のような挙動をします。 namedtuple として動作します(私のユースケースには問題ありません)。

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

どのように解決するのですか?

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

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

名前付きタプルはJSONに適合する奇妙なもので、それは実際には カスタムビルドタイプで、キー名はタイプ定義の一部として固定されています。 というのも、キー名がインスタンス内部に格納される辞書とは異なり、キー名が型定義の一部として固定されるカスタムビルドタイプだからです。 このため、namedtupleをラウンドトリップすることができません。たとえば、dictにアプリケーション固有の型マーカーなどの情報がないと、dictionaryをnamedtupleにデコードして戻すことはできません。 {'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"}}