85 votes

Désérialiser une chaîne json en un objet en python

J'ai la chaîne suivante

{"action":"print","method":"onData","data":"Madan Mohan"}

Je veux désérialiser un objet de la classe

class payload
    string action
    string method
    string data

J'utilise python 2.6 et 2.7

143voto

gnibbler Points 103484
>>> j = '{"action": "print", "method": "onData", "data": "Madan Mohan"}'
>>> import json
>>> 
>>> class Payload(object):
...     def __init__(self, j):
...         self.__dict__ = json.loads(j)
... 
>>> p = Payload(j)
>>>
>>> p.action
'print'
>>> p.method
'onData'
>>> p.data
'Madan Mohan'

58voto

Alex Points 3933

Pour développer la réponse de Sami :

De les docs :

class Payload(object):
    def __init__(self, action, method, data):
        self.action = action
        self.method = method
        self.data = data

import json

def as_payload(dct):
    return Payload(dct['action'], dct['method'], dct['data'])

payload = json.loads(message, object_hook = as_payload)

Mon objection à la

.__dict__ 

La solution est que, bien qu'elle fasse le travail et qu'elle soit concise, la classe Payload devient totalement générique - il ne documente pas ses champs.

Par exemple, si le message Payload a un format inattendu, au lieu de générer une erreur de clé non trouvée lors de la création du Payload, aucune erreur ne sera générée jusqu'à ce que le Payload soit utilisé.

15voto

GaspardP Points 1898

Si vous utilisez les indications de type de Python 3.6, vous pouvez procéder de la manière suivante :

def from_json(data, cls):
    annotations: dict = cls.__annotations__ if hasattr(cls, '__annotations__') else None
    if issubclass(cls, List):
        list_type = cls.__args__[0]
        instance: list = list()
        for value in data:
            instance.append(from_json(value, list_type))
        return instance
    elif issubclass(cls, Dict):
            key_type = cls.__args__[0]
            val_type = cls.__args__[1]
            instance: dict = dict()
            for key, value in data.items():
                instance.update(from_json(key, key_type), from_json(value, val_type))
            return instance
    else:
        instance : cls = cls()
        for name, value in data.items():
            field_type = annotations.get(name)
            if inspect.isclass(field_type) and isinstance(value, (dict, tuple, list, set, frozenset)):
                setattr(instance, name, from_json(value, field_type))
            else:
                setattr(instance, name, value)
        return instance

Ce qui permet ensuite d'instancier des objets typés comme ceci :

class Bar:
    value : int

class Foo:
    x : int
    bar : List[Bar]

obj : Foo = from_json(json.loads('{"x": 123, "bar":[{"value": 3}, {"value": 2}, {"value": 1}]}'), Foo)
print(obj.x)
print(obj.bar[2].value)

Cette syntaxe nécessite cependant Python 3.6 et ne couvre pas tous les cas - par exemple, le support du typage.Any... Mais au moins, elle ne pollue pas les classes qui doivent être désérialisées avec des méthodes init/tojson supplémentaires.

11voto

LukTar Points 63

Je pensais perdre tous mes cheveux pour avoir résolu ce "défi". J'ai rencontré les problèmes suivants :

  1. Comment désérialiser des objets imbriqués, des listes, etc.
  2. J'aime les constructeurs dont les champs sont spécifiés
  3. Je n'aime pas les champs dynamiques
  4. Je n'aime pas les solutions de fortune

J'ai trouvé une bibliothèque appelée jsonpickle qui s'est avérée très utile.

Installation :

pip install jsonpickle

Voici un exemple de code avec l'écriture d'objets imbriqués dans un fichier :

import jsonpickle

class SubObject:
    def __init__(self, sub_name, sub_age):
        self.sub_name = sub_name
        self.sub_age = sub_age

class TestClass:

    def __init__(self, name, age, sub_object):
        self.name = name
        self.age = age
        self.sub_object = sub_object

john_junior = SubObject("John jr.", 2)

john = TestClass("John", 21, john_junior)

file_name = 'JohnWithSon' + '.json'

john_string = jsonpickle.encode(john)

with open(file_name, 'w') as fp:
    fp.write(john_string)

john_from_file = open(file_name).read()

test_class_2 = jsonpickle.decode(john_from_file)

print(test_class_2.name)
print(test_class_2.age)
print(test_class_2.sub_object.sub_name)

Sortie :

John
21
John jr.

Site web : http://jsonpickle.github.io/

J'espère que cela vous fera gagner du temps (et des cheveux).

10voto

Nery Jr Points 1008

Si vous voulez économiser des lignes de code et laisser la solution la plus flexible, nous pouvons désérialiser la chaîne json en un objet dynamique :

p = lambda:None
p.__dict__ = json.loads('{"action": "print", "method": "onData", "data": "Madan Mohan"}')

>>>> p.action
output : u'print'

>>>> p.method
output : u'onData'

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