94 votes

Sérialisation d'un namedtuple Python en json

Quelle est la méthode recommandée pour sérialiser un namedtuple en json avec les noms de champs conservés ?

Sérialisation d'un namedtuple en json, seules les valeurs sont sérialisées et les noms des champs sont perdus dans la traduction. Je voudrais que les champs soient également conservés lors de la conversion en json et j'ai donc procédé comme suit :

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

Ce qui précède est sérialisé en json comme prévu et se comporte comme suit namedtuple dans d'autres endroits où je l'utilise (accès aux attributs, etc.), sauf avec des résultats non-tuple comme ceux de l'itération (ce qui convient à mon cas d'utilisation).

Quelle est la "bonne méthode" pour convertir en json en conservant les noms des champs ?

0 votes

86voto

benselme Points 569

Si c'est juste un namedtuple que vous cherchez à sérialiser, en utilisant sa fonction _asdict() La méthode fonctionnera (avec 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}'

4 votes

J'obtiens AttributeError : 'FB' object has no attribute '. dict lorsqu'on exécute ce code dans Python 2.7 (x64) sous Windows. Cependant, fb._asdict() fonctionne bien.

6 votes

fb._asdict() o vars(fb) serait mieux.

1 votes

@jpmc26 : Vous ne pouvez pas utiliser vars sur un objet sans __dict__ .

56voto

samplebias Points 19805

C'est assez délicat, car namedtuple() est une usine qui renvoie un nouveau type dérivé de tuple . Une approche consisterait à faire en sorte que votre classe hérite également de UserDict.DictMixin mais tuple.__getitem__ est déjà défini et attend un nombre entier indiquant la position de l'élément, et non le nom de son attribut :

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

À la base, le namedtuple est étrangement adapté à JSON, puisqu'il s'agit en fait d'un fichier de type type personnalisé dont les noms de clés sont fixés dans le cadre de la définition du type contrairement à un dictionnaire où les noms des clés sont stockés dans l'instance. Cela vous empêche de "faire le tour" d'un namedtuple, c'est-à-dire que vous ne pouvez pas décoder un dictionnaire en un namedtuple sans une autre information, comme un marqueur de type spécifique à l'application dans le dict. {'a': 1, '#_type': 'foobar'} ce qui est un peu compliqué.

Ce n'est pas idéal, mais si vous avez seulement besoin de coder Une autre approche consiste à étendre ou à modifier votre encodeur JSON pour qu'il tienne compte de ces types. Voici un exemple de sous-classement du codeur Python json.JSONEncoder . Cela permet de s'assurer que les namedtuples imbriqués sont correctement convertis en dictionnaires :

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

14 votes

À la base, le namedtuple est une forme étrange pour JSON, puisqu'il s'agit d'un type personnalisé dont les noms de clés sont fixés dans le cadre de la définition du type, contrairement à un dictionnaire dont les noms de clés sont stockés dans l'instance. Commentaire très perspicace. Je n'y avais pas pensé. Merci. J'aime les tuples nommés car ils fournissent une belle structure immuable. avec la commodité de la dénomination des attributs. J'accepte votre réponse. Cela dit, le mécanisme de sérialisation de Java offre un meilleur contrôle sur les éléments suivants comment l'objet est sérialisé et je suis curieux de savoir pourquoi de tels crochets ne semblent pas exister en Python.

0 votes

C'était ma première approche, mais elle ne fonctionne pas vraiment (pour moi en tout cas).

1 votes

>>> json.dumps(foobar('x', 'y'), cls=MyEncoder) <<< '["x", "y"]'

22voto

singingwolfboy Points 1358

Il semble que vous pouviez autrefois sous-classer simplejson.JSONEncoder pour que cela fonctionne, mais avec le dernier code de simplejson, ce n'est plus le cas : il faut en fait modifier le code du projet. Je ne vois pas pourquoi simplejson ne devrait pas supporter les namedtuple, donc j'ai bifurqué le projet, ajouté le support des namedtuple, et je suis J'attends actuellement que ma branche soit réintégrée dans le projet principal. . Si vous avez besoin des réparations maintenant, il suffit de tirer de ma fourchette.

EDIT : Il semble que les dernières versions de simplejson le supportent désormais de manière native avec l namedtuple_as_object qui a pour valeur par défaut True .

3 votes

Votre édition est la bonne réponse. simplejson sérialise les namedtuples différemment (mon avis : mieux) que json. Cela fait vraiment le modèle : "try : import simplejson as json except : import json", risqué puisque vous pourriez obtenir un comportement différent sur certaines machines selon que simplejson est installé ou non. Pour cette raison, j'exige maintenant simplejson dans beaucoup de mes fichiers d'installation et je m'abstiens de ce modèle.

2 votes

@marr75 - Idem pour ujson qui est encore plus bizarre et imprévisible dans ces cas limites...

0 votes

J'ai pu obtenir un namedtuple récursif sérialisé en json (pretty-printed) en utilisant : simplejson.dumps(my_tuple, indent=4)

6voto

LtWorf Points 2847

J'ai écrit une bibliothèque pour faire cela : https://github.com/ltworf/typedload

Il peut passer de and à named-tuple et inversement.

Il supporte des structures imbriquées assez complexes, avec des listes, des ensembles, des enums, des unions, des valeurs par défaut. Il devrait couvrir les cas les plus courants.

edit : La bibliothèque supporte également les classes dataclass et attr.

3voto

Tolgahan ÜZÜN Points 168

Il convertit récursivement les données namedTuple en 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'}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X