Come convertire i dati JSON in un oggetto Python


282

Voglio usare Python per convertire i dati JSON in un oggetto Python.

Ricevo oggetti di dati JSON dall'API di Facebook, che desidero archiviare nel mio database.

La mia vista corrente in Django (Python) ( request.POSTcontiene il JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Funziona bene, ma come posso gestire oggetti dati JSON complessi?

  • Non sarebbe molto meglio se potessi in qualche modo convertire questo oggetto JSON in un oggetto Python per un facile utilizzo?


In genere JSON viene convertito in elenchi o dadi vaniglia. È questo che vuoi? O speri di convertire JSON direttamente in un tipo personalizzato?
Shakakai,

Voglio convertirlo in un oggetto, qualcosa a cui posso accedere usando il "." . Come nell'esempio precedente -> reponse.name, response.education.id ecc ....
Sai Krishna,

44
L'uso di dicts è un modo di fare debole per fare una programmazione orientata agli oggetti. I dizionari sono un modo molto scarso per comunicare le aspettative ai lettori del tuo codice. Utilizzando un dizionario, come è possibile specificare in modo chiaro e riutilizzabile che alcune coppie chiave-valore del dizionario sono richieste, mentre altre no? Che ne dite di confermare che un determinato valore è compreso nell'intervallo o set accettabile? Che dire delle funzioni specifiche del tipo di oggetto con cui stai lavorando (aka metodi)? I dizionari sono utili e versatili, ma troppi sviluppatori si comportano come se avessero dimenticato Python è un linguaggio orientato agli oggetti per una ragione.
Spezzatino il

1
C'è una libreria Python per questo github.com/jsonpickle/jsonpickle (commentare poiché la risposta è troppo sotto nella discussione e non sarà raggiungibile.)
migliori auguri

Risposte:


357

Puoi farlo in una riga, usando namedtuplee 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

o, per riutilizzarlo facilmente:

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)

Se si desidera gestire le chiavi che non sono buoni nomi degli attributi, controlla namedtuples'rename parametro .


8
ciò può comportare un errore Value, ValueError: i nomi dei tipi e dei campi non possono iniziare con un numero: '123'
PvdL

3
Come novizio di Python, mi interessa se questa è una cosa salvifica anche quando la sicurezza è un problema.
Benjist,

8
Questo crea una nuova classe diversa ogni volta che incontra un oggetto JSON durante l'analisi, giusto?
fikr4n,

2
Interessante. Pensavo che fare affidamento d.keys()e d.values()iterare nello stesso ordine non fosse garantito, ma mi sbagliavo. I documenti dicono: "Se le chiavi, i valori e le viste degli elementi vengono ripetute senza modifiche al dizionario, l'ordine degli elementi corrisponderà direttamente". Buono a sapersi per blocchi di codice locali così piccoli. Aggiungerei un commento per avvisare esplicitamente i manutentori del codice di tale dipendenza.
cfi,

1
Non sono a conoscenza di nessuna operazione inversa per scopi generici. Qualsiasi individuo nominato può essere trasformato in un dict usando x._asdict(), il che potrebbe aiutare per casi semplici.
DS.

127

Consulta la sezione intitolata Specializzazione della decodifica degli oggetti JSON nella json documentazione del modulo . Puoi usarlo per decodificare un oggetto JSON in un tipo Python specifico.

Ecco un esempio:

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'>

Aggiornare

Se si desidera accedere ai dati in un dizionario tramite il modulo json, procedere come segue:

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

Proprio come un normale dizionario.


1
Ehi, stavo solo leggendo e ho capito che i dizionari lo faranno totalmente, solo mi chiedevo come convertire gli oggetti JSON in dizionari e come posso accedere a questi dati dal dizionario?
Sai Krishna,

Fantastico, è quasi chiaro, volevo solo sapere un'altra piccola cosa che se c'è questo oggetto -> {'education': {'name1': 456, 'name2': 567}}, come posso accedere a questi dati?
Sai Krishna,

sarebbe solo topLevelData ['education'] ['name1'] ==> 456. Ha senso?
Shakakai,

1
@Ben: penso che il tuo commento sia inappropriato. Di tutte le risposte qui al momento è l'unica a ottenere le lezioni giuste. Il che significa: è un'operazione one-pass e il risultato utilizza i tipi corretti. Pickle stesso è per applicazioni diverse rispetto a JSON (binario contro rappresentante testuale) e jsonpickle è una lib non standard. Sarei interessato a vedere come risolvi il problema che la libreria std json non fornisce l'albero di analisi superiore
all'hook

Sono d'accordo con @Ben su questo. Questa è davvero una pessima soluzione. Per nulla scalabile. Dovrai mantenere i nomi dei campi come stringa e come campo. Se vuoi riformattare i tuoi campi, la decodifica fallirà (ovviamente i dati già serializzati non saranno più pertinenti comunque). Lo stesso concetto è già ben implementato con jsonpickle
guyarad il

99

Questo non è il golf del codice, ma qui è il mio trucco più breve, usando types.SimpleNamespacecome contenitore per oggetti JSON.

Rispetto alla namedtuplesoluzione leader , è:

  • probabilmente più veloce / più piccolo in quanto non crea una classe per ogni oggetto
  • più breve
  • nessuna renameopzione, e probabilmente la stessa limitazione su chiavi che non sono identificatori validi (utilizza setattrsotto le copertine)

Esempio:

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)

2
A proposito, la libreria di serializzazione Marshmallow offre una funzionalità simile con il suo @post_loaddecoratore. marshmallow.readthedocs.io/it/latest/…
Taylor Edmiston,

3
Per evitare la dipendenza da argparse: sostituire l'importazione argparse con from types import SimpleNamespacee usare:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig

8
Questa è la soluzione più elegante, dovrebbe essere al top.
ScalaWilliam,

4
Modificato per utilizzare la soluzione di @ maxschlepzig quando si esegue in Python 3.x ( types.SimpleNamespacepurtroppo non esiste in 2.7).
Dan Lenski,

1
perchè print_function?
chwi,

90

Puoi provare questo:

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)

Basta creare un nuovo oggetto e passare i parametri come una mappa.


1
Ottengo TypeError: l'oggetto 'Utente' non è sottoscrivibile
Mahdi

1
Questa dovrebbe essere la risposta accettata. ha funzionato per me e molto più semplice di tutto il resto.
Izik,

Non ho usato * args, ** kwargs, ma la soluzione ha funzionato.
Malkaviano,

1
L'utente (** j) dice che mancano i parametri nome e nome utente, come viene inizializzato il dict ?
Aaron Stainback,

40

Ecco un'alternativa rapida e sporca al sottaceto JSON

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()

1
Questo non è un buon approccio. Inizialmente to_json e from_json non devono essere inseriti nella tua classe. Al secondo non funzionerà per le classi nidificate.
Jurass

17

Per oggetti complessi, è possibile utilizzare Pickup JSON

Libreria Python per serializzare qualsiasi grafico a oggetti arbitrario in JSON. Può prendere quasi qualsiasi oggetto Python e trasformarlo in JSON. Inoltre, può ricostituire l'oggetto in Python.


6
Penso che jsonstruct sia migliore. jsonstruct originally a fork of jsonpickle (Thanks guys!). The key difference between this library and jsonpickle is that during deserialization, jsonpickle requires Python types to be recorded as part of the JSON. This library intends to remove this requirement, instead, requires a class to be passed in as an argument so that its definition can be inspected. It will then return an instance of the given class. This approach is similar to how Jackson (of Java) works.
Abhishek Gupta,

3
Il problema con jsonstruct è che non sembra essere mantenuto (in effetti, sembra abbandonato) e non riesce a convertire un elenco di oggetti, come '[{"name":"object1"},{"name":"object2"}]'. jsonpickle non lo gestisce neanche molto bene.
LS

1
Non ho idea del perché questa risposta non ottenga più voti. La maggior parte delle altre soluzioni sono abbastanza in circolazione. Qualcuno ha sviluppato un'ottima libreria per la de / serializzazione JSON: perché non usarla? Inoltre, sembra funzionare bene con le liste - qual è stato il tuo problema con esso @LS?
Guyarad,

1
@guyarad, il problema è: x = jsonpickle.decode ('[{"name": "object1"}, {"name": "object2"}]') fornisce un elenco di dizionari ([{'name': ' object1 '}, {' name ':' object2 '}]), non un elenco di oggetti con proprietà (x [0] .name ==' object1 '), che è ciò che la domanda originale richiedeva. Per ottenere ciò, ho finito per usare l'approccio object_hook / Namespace suggerito da eddygeek, ma anche l'approccio quick / dirty di ubershmekel sembra buono. Penso che potrei usare object_hook con set_encoder_options () di jsonpickle (non documentato!), Ma richiederebbe più codice del modulo json di base. Mi piacerebbe essere smentito!
LS

@LS se non hai alcun controllo sull'input, che è veramente ciò che l'OP ha chiesto, jsonpickle non è l'ideale poiché si aspetta il tipo effettivo in ogni livello (e assumerà i tipi di base se manca). Entrambe le soluzioni sono "carine".
Guyarad,

12

Se stai usando Python 3.5+, puoi usare jsonsper serializzare e deserializzare su semplici oggetti Python vecchi:

import jsons

response = request.POST

# You'll need your class attributes to match your dict keys, so in your case do:
response['id'] = response.pop('user_id')

# Then you can load that dict into your class:
user = jsons.load(response, FbApiUser)

user.save()

Puoi anche FbApiUserereditare da jsons.JsonSerializableper maggiore eleganza:

user = FbApiUser.from_json(response)

Questi esempi funzioneranno se la tua classe è composta da tipi predefiniti di Python, come stringhe, numeri interi, elenchi, orari, ecc. La jsonslib richiederà comunque suggerimenti di tipo per tipi personalizzati.


7

Se si utilizza python 3.6+, è possibile utilizzare marshmallow-dataclass . Contrariamente a tutte le soluzioni sopra elencate, è sia semplice che sicuro:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})

TypeError: make_data_class() got an unexpected keyword argument 'many'
Giovanni

@John: dovresti aprire un problema con un caso di test riproducibile in github.com/lovasoa/marshmallow_dataclass/issues
lovasoa,

6

Migliorare l'ottima risposta del lovasoa.

Se stai usando python 3.6+, puoi usare:
pip install marshmallow-enume
pip install marshmallow-dataclass

È semplice e sicuro.

Puoi trasformare la tua classe in uno string-json e viceversa:

Dall'oggetto alla stringa Json:

    from marshmallow_dataclass import dataclass
    user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
    user_json = User.Schema().dumps(user)
    user_json_str = user_json.data

Da String Json a Object:

    json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
    user, err = User.Schema().loads(json_str)
    print(user,flush=True)

Definizioni di classe:

class OrderStatus(Enum):
    CREATED = 'Created'
    PENDING = 'Pending'
    CONFIRMED = 'Confirmed'
    FAILED = 'Failed'

@dataclass
class User:
    def __init__(self, name, orderId, productName, quantity, status):
        self.name = name
        self.orderId = orderId
        self.productName = productName
        self.quantity = quantity
        self.status = status

    name: str
    orderId: str
    productName: str
    quantity: int
    status: OrderStatus

1
Non hai bisogno del costruttore, basta passare init = True alla dataclass e sei a posto.
Josef Korbel,

5

Ho scritto un piccolo (de) serializzazione chiamato any2any che aiuta a fare trasformazioni complesse tra due tipi di Python.

Nel tuo caso, immagino che tu voglia trasformarti da un dizionario (ottenuto con json.loads) in un oggetto complesso response.education ; response.name, con una struttura nidificata response.education.id, ecc ... Quindi è esattamente per questo che è fatto questo framework. La documentazione non è ancora eccezionale, ma utilizzando any2any.simple.MappingToObject, dovresti essere in grado di farlo molto facilmente. Per favore chiedi se hai bisogno di aiuto.


Sebpiq, ha installato any2any e sto riscontrando problemi nel comprendere la sequenza prevista di chiamate al metodo. Potresti dare un semplice esempio di conversione di un dizionario in un oggetto Python con una proprietà per ogni chiave?
sansjoe,

Ciao @sansjoe! Se lo hai installato da Pypi, la versione è completamente obsoleta, ho effettuato un refactoring completo alcune settimane fa. Dovresti usare la versione di github (devo fare una versione corretta!)
sebpiq

L'ho installato da Pypy perché il Github ha detto di installarlo da Pypy. Inoltre, hai detto PyPy era fuori mesi di data fa .. Non ha funzionato :( ho presentato un bug report tho! Github.com/sebpiq/any2any/issues/11
sneilan

4

Poiché nessuno ha fornito una risposta simile alla mia, la pubblicherò qui.

È una classe solida che può facilmente convertire avanti e indietro tra json stre dictche ho copiato dalla mia risposta a un'altra domanda :

import json

class PyJSON(object):
    def __init__(self, d):
        if type(d) is str:
            d = json.loads(d)

        self.from_dict(d)

    def from_dict(self, d):
        self.__dict__ = {}
        for key, value in d.items():
            if type(value) is dict:
                value = PyJSON(value)
            self.__dict__[key] = value

    def to_dict(self):
        d = {}
        for key, value in self.__dict__.items():
            if type(value) is PyJSON:
                value = value.to_dict()
            d[key] = value
        return d

    def __repr__(self):
        return str(self.to_dict())

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __getitem__(self, key):
        return self.__dict__[key]

json_str = """... json string ..."""

py_json = PyJSON(json_str)

2

Modifica della risposta @DS un po ', per caricare da un file:

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)

Una cosa: non è possibile caricare articoli con numeri in anticipo. Come questo:

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

Perché "1_first_item" non è un nome campo python valido.


2

Durante la ricerca di una soluzione, mi sono imbattuto in questo post del blog: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

Utilizza la stessa tecnica indicata nelle risposte precedenti ma con un uso di decoratori. Un'altra cosa che ho trovato utile è il fatto che restituisce un oggetto tipizzato alla fine della deserializzazione

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)

Uso:

@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)

2

Espandendo un po 'la risposta di DS, se hai bisogno che l'oggetto sia mutabile (quale nametuple non lo è), puoi usare la libreria recordclass invece di namedtuple:

import json
from recordclass import recordclass

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

# Parse into a mutable object
x = json.loads(data, object_hook=lambda d: recordclass('X', d.keys())(*d.values()))

L'oggetto modificato può quindi essere riconvertito molto facilmente in json usando simplejson :

x.name = "John Doe"
new_json = simplejson.dumps(x)

1

Se stai usando Python 3.6 o versioni successive, potresti dare un'occhiata a squema , un modulo leggero per strutture di dati tipizzate staticamente. Semplifica la lettura del codice e allo stesso tempo fornisce una semplice convalida, conversione e serializzazione dei dati senza ulteriore lavoro. Puoi pensarlo come un'alternativa più sofisticata e supponente alle coppie di nomi e agli occhiali. Ecco come puoi usarlo:

from uuid import UUID
from squema import Squema


class FbApiUser(Squema):
    id: UUID
    age: int
    name: str

    def save(self):
        pass


user = FbApiUser(**json.loads(response))
user.save()

Questo è anche più simile ai modi di linguaggio JVM per farlo.
javadba,

1

Ero alla ricerca di una soluzione che funzionasse recordclass.RecordClass, supporti oggetti nidificati e funzionasse sia per la serializzazione json che per la deserializzazione json.

Espandendo la risposta di DS e espandendo la soluzione di BeneStr, ho pensato a quanto segue che sembra funzionare:

Codice:

import json
import recordclass

class NestedRec(recordclass.RecordClass):
    a : int = 0
    b : int = 0

class ExampleRec(recordclass.RecordClass):
    x : int       = None
    y : int       = None
    nested : NestedRec = NestedRec()

class JsonSerializer:
    @staticmethod
    def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
        return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)

    @staticmethod
    def loads(s, klass):
        return JsonSerializer.__dict_to_obj(klass, json.loads(s))

    @staticmethod
    def __obj_to_dict(obj):
        if hasattr(obj, "_asdict"):
            return obj._asdict()
        else:
            return json.JSONEncoder().default(obj)

    @staticmethod
    def __dict_to_obj(klass, s_dict):
        kwargs = {
            key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
                for key,cls in klass.__annotations__.items() \
                    if s_dict is not None and key in s_dict
        }
        return klass(**kwargs)

Uso:

example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )

#Serialize to JSON

json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
#  "x": 10,
#  "y": 20,
#  "nested": {
#    "a": 30,
#    "b": 40
#  }
#}

# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1

json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
#  "x": 11,
#  "y": 21,
#  "nested": {
#    "a": 31,
#    "b": 41
#  }
#}

1

Le risposte fornite qui non restituiscono il tipo di oggetto corretto, quindi ho creato questi metodi di seguito. Inoltre falliscono se provi ad aggiungere più campi alla classe che non esiste nel JSON dato:

def dict_to_class(class_name: Any, dictionary: dict) -> Any:
    instance = class_name()
    for key in dictionary.keys():
        setattr(instance, key, dictionary[key])
    return instance


def json_to_class(class_name: Any, json_string: str) -> Any:
    dict_object = json.loads(json_string)
    return dict_to_class(class_name, dict_object)

0

Python3.x

Il miglior approccio che sono riuscito a raggiungere con le mie conoscenze è stato questo.
Nota che questo codice tratta anche set ().
Questo approccio è generico e richiede solo l'estensione della classe (nel secondo esempio).
Nota che lo sto solo facendo ai file, ma è facile modificare il comportamento secondo i tuoi gusti.

Tuttavia questo è un CoDec.

Con un po 'più di lavoro puoi costruire la tua classe in altri modi. Presumo un costruttore predefinito per istanziarlo, quindi aggiorno il dict di classe.

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)

modificare

Con un po 'più di ricerca ho trovato un modo per generalizzare senza la necessità della chiamata al metodo del registro SUPERCLASS , usando una metaclasse

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

Puoi usare

x = Map(json.loads(response))
x.__class__ = MyClass

dove

class Map(dict):
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v
                    if isinstance(v, dict):
                        self[k] = Map(v)

        if kwargs:
            # for python 3 use kwargs.items()
            for k, v in kwargs.iteritems():
                self[k] = v
                if isinstance(v, dict):
                    self[k] = Map(v)

    def __getattr__(self, attr):
        return self.get(attr)

    def __setattr__(self, key, value):
        self.__setitem__(key, value)

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

    def __delitem__(self, key):
        super(Map, self).__delitem__(key)
        del self.__dict__[key]

Per una soluzione generica, a prova di futuro.


-5

Usa il jsonmodulo ( nuovo in Python 2.6 ) o il simplejsonmodulo che è quasi sempre installato.


2
Ehi, grazie per aver risposto. Potete per favore pubblicare un esempio di come decodificare JSON e quindi accedere a tali dati?
Sai Krishna,

Ehi, ora hai un punto, ma in qualche modo, preferisco farlo senza saperlo e poi decodificarlo: D.
Sai Krishna,

1
@Zach: ci sono esempi proprio nella parte superiore dei documenti a cui ho collegato.
Chris Morgan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.