Comment, en Python, convertir au format JSON un objet de type ‘set’

En Python, par défaut, l’objet de type ‘set’ n’est pas sérialisable. En conséquence, il n’est pas possible d’utiliser la fonction json.dumps() sur ce type d’objet sous peine d’avoir l’erreur Python : TypeError: set([]) is not JSON serializable.

La notation JSON (JavaScript Object Notation) ne permet nativement que de sérialiser les types/structures de données suivantes :

JSONPython
objetdict
arraylist
stringstr
number (nombre entier)int
number (nombre réel)float
trueTrue
falseFalse
nullNone

Donc tout ce qui doit être converti au format JSON doit être exprimé comme l’une de ces structures de données. Comme le montre la documentation du module Python JSON, cette conversion peut être effectuée automatiquement par les API JSONEncoder et JSONDecoder mais il faudrait alors renoncer à d’autres types de structures dont vous pourriez avoir besoin comme les ensembles (set).

Dans cet article, nous allons voir deux approches différentes permettant d’avoir cette fonctionnalité de sérialisation / conversion au format JSON sur l’objet de type set.

Extension pour la classe json.JSONEncoder

Cette première solution consiste en la création d’un encodeur personnalisé qui renvoie une liste (list) lorsqu’il rencontre un ensemble (set). Voici un exemple

import json

class SetEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return json.JSONEncoder.default(self, obj)

>>> data_str = json.dumps(set([1,2,3,4,5]), cls=SetEncoder)
>>> print(data_str)
[1, 2, 3, 4, 5]

Utilisation du module Python Pickle

Cette solution plus sophistiquée consiste à créer un type personnalisé qui peut coexister avec d’autres types JSON natifs. Cela vous permet de stocker des structures imbriquées comprenant des listes, des ensembles, des dictionnaires, des décimales, des objets date-heure, etc.

L’utilisation d’une technique de sérialisation plus générale, comme le module Pickle de Python, permet de prendre en charge un éventail beaucoup plus large de types de données.

from json import dumps, loads, JSONEncoder, JSONDecoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        try:
            return {'_python_object': pickle.dumps(obj).decode('latin-1')}
        except pickle.PickleError:
            return super().default(obj)

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(dct['_python_object'].encode('latin-1'))
    return dct

Voici un exemple, montrant la possibilité de gérer des listes, des dictionnaires et des ensembles :

>>> data = [
   1,2,3,                                          # Nombre
   set(['knights', 'who', 'say', 'ni']), # Ensemble
   {'key':'value'},                             # Dictionnaire
   Decimal('3.14')                          # Nombre flottant
]

>>> j = dumps(data, cls=PythonObjectEncoder)

>>> loads(j, object_hook=as_python_object)
[1, 2, 3, set(['knights', 'say', 'who', 'ni']), {'key': 'value'}, Decimal('3.14')]

Alternativement, vous pouvez aussi utiliser YAML, qui est un sur-ensemble strict de JSON, permettant de sérialiser tous types de données en Python.

Laisser un commentaire