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

Barbara Streisand
Libérer: 2024-10-30 20:25:30
original
300 Les gens l'ont consulté

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>
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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

{"type": "Foo", "name": "sazpaz"}
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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>
Copier après la connexion

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!

source:php.cn
Déclaration de ce site Web
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
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!