Comment convertir des données JSON en objet Python

je veux utiliser Python pour convertir des données JSON en un objet Python.

je reçois des objets de données JSON de L'API Facebook, que je souhaite stocker dans ma base de données.

Ma Vue actuelle dans Django (Python) ( request.POST contient le JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • cela fonctionne très bien, mais comment gérer des objets de données JSON complexes?

  • ne serait-ce pas beaucoup mieux si je pouvais convertir cet objet JSON en un objet Python pour une utilisation facile?

171
demandé sur fragilewindows 2011-07-05 11:01:55

11 réponses

vous pouvez le faire en une ligne, en utilisant namedtuple et object_hook :

import json
from collections import namedtuple

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

# Parse JSON into an object with attributes corresponding to dict keys.
x = json.loads(data, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))
print x.name, x.hometown.name, x.hometown.id

ou, pour les réutiliser facilement:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def json2obj(data): return json.loads(data, object_hook=_json_object_hook)

x = json2obj(data)

si vous voulez qu'il manipule des clés qui ne sont pas de bons noms d'Attributs, cochez namedtuple 's rename paramètre .

236
répondu DS. 2013-04-08 14:40:17

consultez la section intitulée décodage spécialisé d'objet JSON dans le json documentation de module . Vous pouvez utiliser cela pour décoder un objet JSON dans un type Python spécifique.

voici un exemple:

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

import json
def object_decoder(obj):
    if '__type__' in obj and obj['__type__'] == 'User':
        return User(obj['name'], obj['username'])
    return obj

json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}',
           object_hook=object_decoder)

print type(User)  # -> <type 'type'>

mise à Jour

si vous voulez accéder à des données dans un dictionnaire via le module json faites ceci:

user = json.loads('{"__type__": "User", "name": "John Smith", "username": "jsmith"}')
print user['name']
print user['username']

comme un dictionnaire.

106
répondu Shakakai 2018-06-12 01:07:46

ce n'est pas du golf de code, mais voici mon truc le plus court, en utilisant types.SimpleNamespace comme conteneur pour les objets JSON.

par rapport à la première solution namedtuple , il est:

  • probablement plus rapide / plus petit car il ne crée pas une classe pour chaque objet
  • plus court
  • Non rename option, et probablement la même limitation sur les clés qui ne sont pas valides identificateurs (utilise setattr sous les couvertures)

exemple:

from __future__ import print_function
import json

try:
    from types import SimpleNamespace as Namespace
except ImportError:
    # Python 2.x fallback
    from argparse import Namespace

data = '{"name": "John Smith", "hometown": {"name": "New York", "id": 123}}'

x = json.loads(data, object_hook=lambda d: Namespace(**d))

print (x.name, x.hometown.name, x.hometown.id)
63
répondu eddygeek 2017-02-14 21:22:16

vous pourriez essayer ceci:

class User(object):
    def __init__(self, name, username, *args, **kwargs):
        self.name = name
        self.username = username

import json
j = json.loads(your_json)
u = User(**j)

il suffit de créer un nouvel objet, et passer les paramètres comme une carte.

54
répondu cmaluenda 2017-02-10 21:19:47

Voici un moyen rapide et sale json pickle alternative

import json

class User:
    def __init__(self, name, username):
        self.name = name
        self.username = username

    def to_json(self):
        return json.dumps(self.__dict__)

    @classmethod
    def from_json(cls, json_str):
        json_dict = json.loads(json_str)
        return cls(**json_dict)

# example usage
User("tbrown", "Tom Brown").to_json()
User.from_json(User("tbrown", "Tom Brown").to_json()).to_json()
16
répondu ubershmekel 2015-10-21 23:29:50

pour les objets complexes, vous pouvez utiliser JSON Pickle

bibliothèque Python pour sérialiser n'importe quel graphique d'objet arbitraire dans JSON. Il peut prendre presque n'importe quel objet Python et transformer l'objet en JSON. En outre, il peut reconstituer l'objet en Python.

13
répondu sputnikus 2017-02-21 18:33:28

j'ai écrit un petit cadre de sérialisation appelé any2any qui aide à faire des transformations complexes entre deux types de Python.

dans votre cas, je suppose que vous voulez transformer d'un dictionnaire (obtenu avec json.loads ) à un objet complexe response.education ; response.name , avec une structure imbriquée response.education.id , etc... C'est exactement ce que ce cadre est fait pour. La documentation n'est pas encore grande, mais en utilisant any2any.simple.MappingToObject , vous doit être capable de faire cela très facilement. Demandez si vous avez besoin d'aide.

5
répondu sebpiq 2016-06-01 07:38:34

modifier un peu la réponse @DS, pour charger à partir d'un fichier:

def _json_object_hook(d): return namedtuple('X', d.keys())(*d.values())
def load_data(file_name):
  with open(file_name, 'r') as file_data:
    return file_data.read().replace('\n', '')
def json2obj(file_name): return json.loads(load_data(file_name), object_hook=_json_object_hook)

une chose: cela ne peut pas charger des articles avec des nombres à l'avance. Comme ceci:

{
  "1_first_item": {
    "A": "1",
    "B": "2"
  }
}

parce que" 1_first_item " n'est pas un nom de champ valide de python.

1
répondu Valtoni Boaventura 2016-12-06 08:45:08

Python3.x

la meilleure approche que j'ai pu atteindre avec ma connaissance était celle-ci.

Notez que ce code de traiter set ().

Cette approche est générique et ne nécessite que l'extension de la classe (dans le deuxième exemple).

Notez que je le fais juste aux fichiers, mais il est facile de modifier le comportement à votre goût.

cependant ceci est un CoDec.

avec un peu plus travail vous pouvez construire votre classe d'autres façons. Je suppose un constructeur par défaut pour l'afficher, puis je mets à jour la classe dict.

import json
import collections


class JsonClassSerializable(json.JSONEncoder):

    REGISTERED_CLASS = {}

    def register(ctype):
        JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in self.REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = self.REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


JsonClassSerializable.register(C)


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


JsonClassSerializable.register(B)


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()

JsonClassSerializable.register(A)

A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)

modifier

avec un peu plus de recherche j'ai trouvé un moyen de généraliser sans le besoin de la SUPERCLASS appel de méthode de registre, en utilisant un metaclass

import json
import collections

REGISTERED_CLASS = {}

class MetaSerializable(type):

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in REGISTERED_CLASS:
            REGISTERED_CLASS[cls.__name__] = cls
        return super(MetaSerializable, cls).__call__(*args, **kwargs)


class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):

    def default(self, obj):
        if isinstance(obj, collections.Set):
            return dict(_set_object=list(obj))
        if isinstance(obj, JsonClassSerializable):
            jclass = {}
            jclass["name"] = type(obj).__name__
            jclass["dict"] = obj.__dict__
            return dict(_class_object=jclass)
        else:
            return json.JSONEncoder.default(self, obj)

    def json_to_class(self, dct):
        if '_set_object' in dct:
            return set(dct['_set_object'])
        elif '_class_object' in dct:
            cclass = dct['_class_object']
            cclass_name = cclass["name"]
            if cclass_name not in REGISTERED_CLASS:
                raise RuntimeError(
                    "Class {} not registered in JSON Parser"
                    .format(cclass["name"])
                )
            instance = REGISTERED_CLASS[cclass_name]()
            instance.__dict__ = cclass["dict"]
            return instance
        return dct

    def encode_(self, file):
        with open(file, 'w') as outfile:
            json.dump(
                self.__dict__, outfile,
                cls=JsonClassSerializable,
                indent=4,
                sort_keys=True
            )

    def decode_(self, file):
        try:
            with open(file, 'r') as infile:
                self.__dict__ = json.load(
                    infile,
                    object_hook=self.json_to_class
                )
        except FileNotFoundError:
            print("Persistence load failed "
                  "'{}' do not exists".format(file)
                  )


class C(JsonClassSerializable):

    def __init__(self):
        self.mill = "s"


class B(JsonClassSerializable):

    def __init__(self):
        self.a = 1230
        self.c = C()


class A(JsonClassSerializable):

    def __init__(self):
        self.a = 1
        self.b = {1, 2}
        self.c = B()


A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s
0
répondu Davi Abreu Wasserberg 2018-08-19 05:48:28

tout en cherchant une solution, je suis tombé sur ce billet de blog: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects /

il utilise la même technique que celle décrite dans les réponses précédentes, mais avec un usage de décorateurs. Une autre chose que j'ai trouvé utile est le fait qu'il renvoie un objet dactylographié à la fin de la désérialisation

class JsonConvert(object):
    class_mappings = {}

    @classmethod
    def class_mapper(cls, d):
        for keys, cls in clsself.mappings.items():
            if keys.issuperset(d.keys()):   # are all required arguments present?
                return cls(**d)
        else:
            # Raise exception instead of silently returning None
            raise ValueError('Unable to find a matching class for object: {!s}'.format(d))

    @classmethod
    def complex_handler(cls, Obj):
        if hasattr(Obj, '__dict__'):
            return Obj.__dict__
        else:
            raise TypeError('Object of type %s with value of %s is not JSON serializable' % (type(Obj), repr(Obj)))

    @classmethod
    def register(cls, claz):
        clsself.mappings[frozenset(tuple([attr for attr,val in cls().__dict__.items()]))] = cls
        return cls

    @classmethod
    def to_json(cls, obj):
        return json.dumps(obj.__dict__, default=cls.complex_handler, indent=4)

    @classmethod
    def from_json(cls, json_str):
        return json.loads(json_str, object_hook=cls.class_mapper)

Utilisation:

@JsonConvert.register
class Employee(object):
    def __init__(self, Name:int=None, Age:int=None):
        self.Name = Name
        self.Age = Age
        return

@JsonConvert.register
class Company(object):
    def __init__(self, Name:str="", Employees:[Employee]=None):
        self.Name = Name
        self.Employees = [] if Employees is None else Employees
        return

company = Company("Contonso")
company.Employees.append(Employee("Werner", 38))
company.Employees.append(Employee("Mary"))

as_json = JsonConvert.to_json(company)
from_json = JsonConvert.from_json(as_json)
as_json_from_json = JsonConvert.to_json(from_json)

assert(as_json_from_json == as_json)

print(as_json_from_json)
0
répondu enazar 2018-10-05 10:39:31

utilisez le json module ( nouveau en Python 2.6 ) ou le simplejson module qui est presque toujours installé.

-1
répondu Chris Morgan 2011-07-05 07:06:26