Maison >développement back-end >Tutoriel Python >Comment puis-je rendre les objets personnalisés JSON sérialisables sans sous-classer « json.JSONEncoder » ?

Comment puis-je rendre les objets personnalisés JSON sérialisables sans sous-classer « json.JSONEncoder » ?

Barbara Streisand
Barbara Streisandoriginal
2024-10-30 20:25:30364parcourir

How can I make custom objects JSON serializable without subclassing `json.JSONEncoder`?

Rendre les objets JSON sérialisables avec l'encodeur normal

La méthode par défaut pour sérialiser des objets personnalisés non sérialisables en JSON consiste à sous-classer json.JSONEncoder et à transmettre un encodeur personnalisé à json.dumps(). Cela ressemble généralement à ce qui suit :

<code class="python">class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Foo):
            return obj.to_json()

        return json.JSONEncoder.default(self, obj)

print(json.dumps(obj, cls=CustomEncoder))</code>

Cependant, que se passe-t-il si vous souhaitez rendre un objet sérialisable avec l'encodeur par défaut ? Après avoir examiné le code source du module json, il semble que l'extension directe de l'encodeur ne répondra pas à cette exigence.

Au lieu de cela, vous pouvez utiliser une technique appelée "monkey-patching" dans le script d'initialisation __init__.py de votre package. Cela affecte toutes les sérialisations ultérieures des modules JSON puisque les modules ne sont généralement chargés qu'une seule fois et que le résultat est mis en cache dans sys.modules.

Le correctif modifierait la méthode par défaut de l'encodeur JSON par défaut pour rechercher une méthode "to_json" unique. et utilisez-le pour encoder l'objet s'il est trouvé.

Voici un exemple implémenté en tant que module autonome pour plus de simplicité :

<code class="python"># Module: make_json_serializable.py

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder.default  # Save unmodified default.
JSONEncoder.default = _default  # Replace it.</code>

L'utilisation de ce patch est simple : il suffit d'importer le module pour appliquer le singe -patch.

<code class="python"># Sample client script

import json
import make_json_serializable  # apply monkey-patch

class Foo(object):
    def __init__(self, name):
        self.name = name

    def to_json(self):  # New special method.
        """Convert to JSON format string representation."""
        return '{"name": "%s"}' % self.name

foo = Foo('sazpaz')
print(json.dumps(foo))  # -> '{"name": "sazpaz"}'</code>

Pour conserver les informations sur le type d'objet, la méthode to_json peut les inclure dans la chaîne renvoyée :

<code class="python">def to_json(self):
    """Convert to JSON format string representation."""
    return '{"type": "%s", "name": "%s"}' % (self.__class__.__name__, self.name)</code>

Cela produit un JSON qui inclut le nom de la classe :

{"type": "Foo", "name": "sazpaz"}

La magie réside ici

Une approche encore plus puissante consiste à demander à la méthode de remplacement par défaut de sérialiser automatiquement la plupart des objets Python, y compris les instances de classe définies par l'utilisateur, sans nécessiter une méthode unique.

Après avoir recherché plusieurs alternatives, l'approche suivante basée sur pickle semble la plus proche de cet idéal :

<code class="python"># Module: make_json_serializable2.py

from json import JSONEncoder
import pickle

def _default(self, obj):
    return {"_python_object": pickle.dumps(obj)}

JSONEncoder.default = _default  # Replace with the above.</code>

Bien que tout ne puisse pas être picklé (par exemple, les types d'extensions), pickle fournit des méthodes pour les gérer via un protocole. en utilisant des méthodes uniques. Cependant, cette approche couvre plus de cas.

Désérialisation

L'utilisation du protocole pickle simplifie la reconstruction de l'objet Python d'origine en fournissant un argument de fonction object_hook personnalisé à json.loads() lors de la rencontre d'un "_python_object" clé dans le dictionnaire.

<code class="python">def as_python_object(dct):
    try:
        return pickle.loads(str(dct['_python_object']))
    except KeyError:
        return dct

pyobj = json.loads(json_str, object_hook=as_python_object)</code>

Cela peut être simplifié en une fonction wrapper :

<code class="python">json_pkloads = functools.partial(json.loads, object_hook=as_python_object)

pyobj = json_pkloads(json_str)</code>

Ce code ne fonctionne pas dans Python 3 car json.dumps() renvoie un objet bytes que JSONEncoder ne peut pas gérer. Toutefois, la démarche reste valable avec la modification suivante :

<code class="python">def _default(self, obj):
    return {"_python_object": pickle.dumps(obj).decode('latin1')}

def as_python_object(dct):
    try:
        return pickle.loads(dct['_python_object'].encode('latin1'))
    except KeyError:
        return dct</code>

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn