Cómo convertir datos JSON en un objeto Python

282

Quiero usar Python para convertir datos JSON en un objeto Python.

Recibo objetos de datos JSON de la API de Facebook, que quiero almacenar en mi base de datos.

Mi vista actual en Django (Python) ( request.POSTcontiene el JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Esto funciona bien, pero ¿cómo manejo objetos de datos JSON complejos?

  • ¿No sería mucho mejor si de alguna manera pudiera convertir este objeto JSON en un objeto Python para un uso fácil?

Sai Krishna
fuente
Por lo general, JSON se convierte en listas o dictados de vainilla. ¿Es eso lo que quieres? ¿O espera convertir JSON directamente a un tipo personalizado?
Shakakai
Quiero convertirlo en un objeto, algo a lo que puedo acceder usando el "." . Como en el ejemplo anterior -> reponse.name, response.education.id etc ....
Sai Krishna
44
Usar dicts es una forma débil de hacer programación orientada a objetos. Los diccionarios son una forma muy pobre de comunicar las expectativas a los lectores de su código. Usando un diccionario, ¿cómo puede especificar de manera clara y reutilizable que se requieren algunos pares clave-valor del diccionario, mientras que otros no? ¿Qué hay de confirmar que un valor dado está en el rango o conjunto aceptable? ¿Qué pasa con las funciones que son específicas del tipo de objeto con el que está trabajando (también conocido como métodos)? Los diccionarios son prácticos y versátiles, pero demasiados desarrolladores actúan como si hubieran olvidado que Python es un lenguaje orientado a objetos por una razón.
Estofado
1
Hay una biblioteca de Python para este github.com/jsonpickle/jsonpickle (comentando desde respuesta es muy por debajo de la rosca y No será alcanzable.)
los mejores deseos

Respuestas:

357

Puedes hacerlo en una línea, usando namedtupley 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, para reutilizar esto fácilmente:

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 desea que maneje claves que no son buenos nombres de atributos, consulte namedtupleel renameparámetro .

DS.
fuente
8
esto puede provocar un error de valor, ValueError: los nombres de tipo y los nombres de campo no pueden comenzar con un número: '123'
PvdL
3
Como novato en Python, estoy interesado si esto es algo seguro también cuando la seguridad es un problema.
benjist
8
Esto crea una nueva clase diferente cada vez que encuentra un objeto JSON mientras analiza, ¿verdad?
fikr4n
2
Interesante. Pensé que confiar d.keys()e d.values()iterar en el mismo orden no está garantizado, pero estaba equivocado. Los documentos dicen: "Si las vistas de claves, valores y elementos se repiten sin modificaciones intermedias en el diccionario, el orden de los elementos se corresponderá directamente". Es bueno saberlo para bloques de código locales tan pequeños. Sin embargo, agregaría un comentario para alertar explícitamente a los mantenedores del código de dicha dependencia.
cfi
1
No conozco ninguna operación inversa agradable de uso general. Cualquier persona nombrada tupla puede convertirse en un dict usando x._asdict(), lo que podría ayudar en casos simples.
DS.
127

Consulte la sección titulada Especialización en decodificación de objetos JSON en la json documentación del módulo . Puede usar eso para decodificar un objeto JSON en un tipo específico de Python.

Aquí hay un ejemplo:

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

Actualizar

Si desea acceder a los datos en un diccionario a través del módulo json, haga esto:

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

Al igual que un diccionario normal.

Shakakai
fuente
1
Hola, estaba leyendo y me di cuenta de que los diccionarios funcionarían totalmente, solo que me preguntaba cómo convertir objetos JSON en diccionarios y cómo accedo a estos datos desde el diccionario.
Sai Krishna
Impresionante, está casi claro, solo quería saber una cosita más que si existe este objeto -> {'educación': {'nombre1': 456, 'nombre2': 567}}, ¿cómo accedo a estos datos?
Sai Krishna
solo sería topLevelData ['education'] ['name1'] ==> 456. ¿tiene sentido?
Shakakai
1
@Ben: Creo que tu comentario es inapropiado. De todas las respuestas aquí actualmente, es la única que acerta las clases. Lo que significa: es una operación de una pasada y el resultado utiliza los tipos correctos. Pickle en sí es para diferentes aplicaciones que JSON (binary versus text rep) y jsonpickle es una lib no estándar. Me interesaría ver cómo resuelve el problema de que std json lib no proporciona el árbol de análisis superior al gancho del objeto
cfi
Tengo que estar de acuerdo con @Ben en esto. Esta es una muy mala solución. No escalable en absoluto. Deberá mantener los nombres de los campos como cadena y como campo. Si desea refactorizar sus campos, la decodificación fallará (por supuesto, los datos ya serializados ya no serán relevantes de todos modos). El mismo concepto ya está bien implementado con jsonpickle
guyarad
99

Este no es el código de golf, pero aquí está mi truco más corto, usar types.SimpleNamespacecomo contenedor para objetos JSON.

En comparación con la namedtuplesolución líder , es:

  • probablemente más rápido / más pequeño ya que no crea una clase para cada objeto
  • corta
  • sin renameopción, y probablemente la misma limitación en las claves que no son identificadores válidos (usos setattrdebajo de las cubiertas)

Ejemplo:

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)
eddygeek
fuente
2
Por cierto, la biblioteca de serialización Marshmallow ofrece una característica similar con su @post_loaddecorador. marshmallow.readthedocs.io/en/latest/…
Taylor Edmiston
3
Para evitar la dependencia de argparse: reemplace la importación de argparse con from types import SimpleNamespacey use:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig
8
Esta es la solución más elegante, debe estar en la parte superior.
ScalaWilliam
44
Editado para usar la solución de @ maxschlepzig cuando se ejecuta bajo Python 3.x ( types.SimpleNamespacedesafortunadamente no existe en 2.7).
Dan Lenski
1
¿Por qué print_function?
chwi
90

Podrías probar esto:

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)

Simplemente cree un nuevo Objeto y pase los parámetros como un mapa.

cmaluenda
fuente
1
Obtengo TypeError: el objeto 'Usuario' no es subscriptable
Mahdi
1
Esta debería ser la respuesta aceptada. funcionó para mí mucho más simple que el resto.
Izik
No utilicé * args, ** kwargs, pero la solución funcionó.
Malkaviano
1
El usuario (** j) dice que le faltan los parámetros de nombre y nombre de usuario, también ¿cómo se inicializa el dict ?
Aaron Stainback el
40

Aquí hay una alternativa rápida y sucia de json pickle

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()
ubershmekel
fuente
1
Este no es un buen enfoque. Al principio, to_json y from_json no deben colocarse en su clase. En segundo lugar, no funcionará para clases anidadas.
Jurásico
17

Para objetos complejos, puede usar JSON Pickle

Biblioteca de Python para serializar cualquier gráfico de objeto arbitrario en JSON. Puede tomar casi cualquier objeto de Python y convertir el objeto en JSON. Además, puede reconstituir el objeto nuevamente en Python.

sputnikus
fuente
66
Creo que jsonstruct es mejor. 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
El problema con jsonstruct es que no parece mantenerse (de hecho, parece abandonado) y no puede convertir una lista de objetos, como '[{"name":"object1"},{"name":"object2"}]'. jsonpickle tampoco lo maneja muy bien.
LS
1
No tengo idea de por qué esta respuesta no obtiene más votos. La mayoría de las otras soluciones están bastante por ahí. Alguien desarrolló una gran biblioteca para la des / serialización de JSON, ¿por qué no usarla? Además, parece estar funcionando bien con las listas: ¿cuál fue su problema con @LS?
guyarad
1
@guyarad, el problema es: x = jsonpickle.decode ('[{"name": "object1"}, {"name": "object2"}]') da una lista de diccionarios ([{'name': ' object1 '}, {' name ':' object2 '}]), no una lista de objetos con propiedades (x [0] .name ==' object1 '), que es lo que requería la pregunta original. Para obtener eso, terminé usando el enfoque object_hook / Namespace sugerido por eddygeek, pero el enfoque rápido / sucio de ubershmekel también se ve bien. Creo que podría usar object_hook con set_encoder_options () de jsonpickle (¡indocumentado!), Pero tomaría más código que el módulo json básico. ¡Me encantaría que me demuestren lo contrario!
LS
@LS si no tiene control sobre la entrada, que es realmente lo que pidió el OP, jsonpickle no es ideal ya que espera el tipo real en cada nivel (y asumirá tipos básicos si faltan). Ambas soluciones son "lindas".
guyarad
12

Si está utilizando Python 3.5+, puede usarlo jsonspara serializar y deserializar objetos simples de Python:

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

También puedes hacer FbApiUserheredar de jsons.JsonSerializablepara más elegancia:

user = FbApiUser.from_json(response)

Estos ejemplos funcionarán si su clase consta de tipos predeterminados de Python, como cadenas, enteros, listas, fechas, etc. Sin jsonsembargo , la biblioteca requerirá sugerencias de tipo para tipos personalizados.

RH
fuente
7

Si está utilizando python 3.6+, puede usar marshmallow-dataclass . Contrariamente a todas las soluciones enumeradas anteriormente, es simple y seguro.

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})
lovasoa
fuente
TypeError: make_data_class() got an unexpected keyword argument 'many'
JUAN
@JOhn: Debería abrir un problema con un caso de prueba reproducible en github.com/lovasoa/marshmallow_dataclass/issues
lovasoa
6

Mejorando la muy buena respuesta de lovasoa.

Si está utilizando Python 3.6+, puede usar:
pip install marshmallow-enumy
pip install marshmallow-dataclass

Es simple y seguro de tipo.

Puedes transformar tu clase en un string-json y viceversa:

Del objeto a la cadena 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

De la cadena Json al objeto:

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

Definiciones de clase:

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
danilo
fuente
1
No necesita el constructor, simplemente pase init = True a la clase de datos y estará listo.
Josef Korbel
5

He escrito un pequeño marco de (des) serialización llamado any2any que ayuda a realizar transformaciones complejas entre dos tipos de Python.

En su caso, supongo que desea transformar de un diccionario (obtenido con json.loads) a un objeto complejo response.education ; response.name, con una estructura anidada response.education.id, etc. Así que para eso está hecho este marco. La documentación aún no es excelente, pero al usarla any2any.simple.MappingToObject, deberías poder hacerlo fácilmente. Por favor pregunta si necesitas ayuda.

sebpiq
fuente
Sebpiq, he instalado any2any y estoy teniendo problemas para entender la secuencia prevista de las llamadas a métodos. ¿Podría dar un ejemplo simple de convertir un diccionario a un objeto Python con una propiedad para cada clave?
sansjoe
Hola @sansjoe! Si lo ha instalado desde pypi, la versión está completamente desactualizada, he hecho una refactorización completa hace unas semanas. Se debe utilizar la versión github (Necesito hacer un lanzamiento apropiado!)
sebpiq
Lo instalé desde pypy porque el github dijo que lo instalara desde pypy. Además, dijiste que Pypy estaba desactualizado hace meses. No funcionó :( ¡ Presente
sneilan
4

Como nadie proporcionó una respuesta como la mía, voy a publicarla aquí.

Es una clase robusta que puede convertir fácilmente de un lado a otro entre json stry dictque he copiado de mi respuesta a otra pregunta :

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)
Božo Stojković
fuente
2

Modificando un poco la respuesta @DS, para cargar desde un archivo:

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: esto no puede cargar elementos con números por delante. Me gusta esto:

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

Porque "1_first_item" no es un nombre de campo de Python válido.

Valtoni Boaventura
fuente
2

Mientras buscaba una solución, me topé con esta publicación de blog: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

Utiliza la misma técnica que se indicó en respuestas anteriores pero con un uso de decoradores. Otra cosa que encontré útil es el hecho de que devuelve un objeto escrito al final de la deserialización

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)
enazar
fuente
2

Ampliando un poco la respuesta de DS, si necesita que el objeto sea mutable (que namedtuple no lo es), puede usar la biblioteca recordclass en lugar de 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()))

El objeto modificado se puede convertir de nuevo a json muy fácilmente usando simplejson :

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

Si está utilizando Python 3.6 o más reciente, puede echar un vistazo a squema , un módulo liviano para estructuras de datos con tipos estáticos. Hace que su código sea fácil de leer y, al mismo tiempo, proporciona una validación, conversión y serialización de datos simples sin trabajo adicional. Puede pensarlo como una alternativa más sofisticada y obstinada a las tuplas y clases de datos con nombre. Así es como puedes 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()
Funkatic
fuente
Esto también es más similar a las formas de lenguaje JVM para hacerlo.
javadba
1

Estaba buscando una solución que funcionara recordclass.RecordClass, admitiera objetos anidados y funciona tanto para la serialización json como para la deserialización json.

Ampliando la respuesta de DS y ampliando la solución de BeneStr, se me ocurrió lo siguiente que parece funcionar:

Código:

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
#  }
#}
Shriram V
fuente
1

Las respuestas dadas aquí no devuelven el tipo de objeto correcto, por lo tanto, creé estos métodos a continuación. También fallan si intenta agregar más campos a la clase que no existe en el JSON dado:

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)
Caner
fuente
0

Python3.x

El mejor enfoque que pude alcanzar con mi conocimiento fue este.
Tenga en cuenta que este código trata también a set ().
Este enfoque es genérico y solo necesita la extensión de clase (en el segundo ejemplo).
Tenga en cuenta que solo lo estoy haciendo a los archivos, pero es fácil modificar el comportamiento a su gusto.

Sin embargo, este es un CoDec.

Con un poco más de trabajo puedes construir tu clase de otras maneras. Asumo un constructor predeterminado para instanciarlo, luego actualizo la clase 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)

Editar

Con un poco más de investigación, encontré una manera de generalizar sin la necesidad de la llamada al método de registro SUPERCLASS , usando una metaclase

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
Davi Abreu Wasserberg
fuente
0

Puedes usar

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

dónde

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]

Para una solución genérica a prueba de futuro.

Gulzar
fuente
-5

Use el jsonmódulo ( nuevo en Python 2.6 ) o el simplejsonmódulo que casi siempre está instalado.

Chris Morgan
fuente
2
Hola, gracias por responder. ¿Puedes publicar un ejemplo de cómo decodificar el JSON y luego acceder a esos datos?
Sai Krishna
Oye, ahora tienes un punto, pero de alguna manera, prefiero hacerlo sin saberlo y luego aplicarle ingeniería inversa: D.
Sai Krishna
1
@Zach: hay ejemplos justo en la parte superior de los documentos que he vinculado.
Chris Morgan