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

3voto

Dmitry T. Points 504

Il existe une solution plus pratique qui consiste à utiliser le décorateur (il utilise le champ protégé _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))

3voto

mikebridge Points 437

Il est impossible de sérialiser correctement les namedtuples avec la bibliothèque python json native. Elle verra toujours les tuples comme des listes, et il est impossible de modifier le sérialiseur par défaut pour changer ce comportement. C'est pire si les objets sont imbriqués.

Il est préférable d'utiliser une bibliothèque plus robuste comme 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
}

2voto

Gonzalo Points 404

En jsonplus fournit un sérialiseur pour les instances NamedTuple. Utilisez son mode de compatibilité pour sortir des objets simples si nécessaire, mais préférez le mode par défaut car il est utile pour le décodage en retour.

1voto

dlamblin Points 14546

Il s'agit d'une vieille question. Cependant :

Une suggestion pour tous ceux qui se posent la même question : réfléchissez bien avant d'utiliser l'une ou l'autre des fonctions privées ou internes de l'interface utilisateur. NamedTuple parce qu'ils ont déjà changé et qu'ils changeront encore au fil du temps.

Par exemple, si votre NamedTuple est un objet à valeur plate et que vous êtes uniquement intéressé par sa sérialisation et non par les cas où il est imbriqué dans un autre objet, vous pouvez éviter les problèmes qui se posent avec l'option __dict__ en cours de suppression ou _as_dict() et faites quelque chose comme (et oui c'est Python 3 car cette réponse est pour le présent) :

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

J'ai essayé d'utiliser le default kwarg à appeler à dumps afin d'effectuer le to_dict() si elle était disponible, mais elle n'a pas été appelée car la NamedTuple est convertible en une liste.

1voto

Smit Johnth Points 1333

simplejson.dump() au lieu de json.dump fait le travail. Mais il est peut-être plus lent.

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