Como converter dados JSON em um objeto Python

281

Eu quero usar o Python para converter dados JSON em um objeto Python.

Recebo objetos de dados JSON da API do Facebook, que quero armazenar no meu banco de dados.

Minha visão atual no Django (Python) ( request.POSTcontém o JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Isso funciona bem, mas como lidar com objetos de dados JSON complexos?

  • Não seria muito melhor se eu pudesse, de alguma forma, converter esse objeto JSON em um objeto Python para facilitar o uso?

Sai Krishna
fonte
Normalmente, o JSON é convertido em listas ou dicionários de baunilha. É isso que você quer? Ou você deseja converter JSON diretamente para um tipo personalizado?
Shakakai
Eu quero convertê-lo em um objeto, algo que eu possa acessar usando o "." . Como no exemplo acima -> reponse.name, response.education.id etc ....
Sai Krishna
44
O uso de dicts é uma maneira simples de fazer a programação orientada a objetos. Os dicionários são uma maneira muito ruim de comunicar expectativas aos leitores do seu código. Usando um dicionário, como você pode especificar de forma clara e reutilizável que alguns pares de valor-chave do dicionário são necessários, enquanto outros não? Que tal confirmar que um determinado valor está no intervalo ou no conjunto aceitável? E as funções específicas do tipo de objeto com o qual você está trabalhando (também conhecido como métodos)? Os dicionários são úteis e versáteis, mas muitos desenvolvedores agem como se tivessem esquecido que o Python é uma linguagem orientada a objetos por um motivo.
Stew
1
Há uma biblioteca python para este github.com/jsonpickle/jsonpickle (comentando desde resposta é muito abaixo na linha e não vai ser acessível.)
os melhores desejos

Respostas:

355

Você pode fazer isso em uma linha, 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

ou, para reutilizar isso 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 você quer que ele para lidar com chaves que não são bons nomes de atributos, veja namedtuple's renameparâmetro .

DS.
fonte
8
isso pode resultar em um erro de valor, ValueError: os nomes de tipos e nomes de campos não podem começar com um número: '123'
PvdL
3
Como iniciante no Python, estou interessado se isso também é uma questão de economia quando a segurança é um problema.
21139 benjist
8
Isso cria uma nova classe diferente cada vez que encontramos um objeto JSON durante a análise, certo?
fikr4n
2
Interessante. Eu pensei que confiar d.keys()e d.values()iterar na mesma ordem não é garantido, mas eu estava errado. Os documentos dizem: "Se as exibições de chaves, valores e itens forem iteradas sem modificações no dicionário, a ordem dos itens corresponderá diretamente.". É bom saber sobre blocos de código locais tão pequenos. Eu adicionaria um comentário para alertar explicitamente os mantenedores do código dessa dependência.
TPI
1
Não conheço nenhuma operação reversa de uso geral agradável. Qualquer indivíduo nomeado duplo pode ser convertido em um ditado x._asdict(), o que pode ajudar em casos simples.
DS.
127

Confira a seção intitulada Especializando a decodificação de objetos JSON na json documentação do módulo . Você pode usar isso para decodificar um objeto JSON em um tipo específico de Python.

Aqui está um exemplo:

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

Atualizar

Se você deseja acessar dados em um dicionário através do módulo json, faça o seguinte:

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

Assim como um dicionário comum.

Shakakai
fonte
1
Ei, eu estava lendo e percebi que os dicionários funcionariam totalmente, só queria saber como converter objetos JSON em dicionários e como acessar esses dados do dicionário?
Sai Krishna
Impressionante, é quase claro, só queria saber mais uma coisa que, se houver esse objeto -> {'education': {'name1': 456, 'name2': 567}}, como faço para acessar esses dados?
Sai Krishna
seria apenas topLevelData ['education'] ['name1'] ==> 456. faz sentido?
Shakakai
1
@ Ben: Eu acho que seu comentário é inadequado. De todas as respostas aqui atualmente, é a única a acertar as aulas. O que significa: É uma operação de uma passagem e o resultado usa os tipos corretos. O próprio Pickle é para aplicativos diferentes do JSON (representante binário versus textual) e o jsonpickle é uma biblioteca não padrão. Eu estaria interessado em ver como você resolver o problema que o std json lib não fornece a árvore de análise superior ao gancho objeto
TPI
Eu tenho que concordar com a @Ben nisso. Esta é uma solução muito ruim. Não é escalável. Você precisará manter os nomes dos campos como sequência e como campo. Se você quiser refatorar seus campos, a decodificação falhará (é claro que os dados já serializados não serão mais relevantes). O mesmo conceito já está implementado bem com jsonpickle
guyarad
98

Este não é um código de golfe, mas aqui está o meu truque mais curto, usando types.SimpleNamespacecomo contêiner para objetos JSON.

Comparado com a namedtuplesolução líder , é:

  • provavelmente mais rápido / menor, pois não cria uma classe para cada objeto
  • mais curta
  • nenhuma renameopção e provavelmente a mesma limitação em chaves que não são identificadores válidos (usos setattrocultos)

Exemplo:

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
fonte
2
A propósito, a biblioteca de serialização Marshmallow oferece um recurso semelhante ao seu @post_loaddecorador. marshmallow.readthedocs.io/en/latest/…
Taylor Edmiston
3
Para evitar a dependência do argparse: substitua a importação do argparse por from types import SimpleNamespacee use:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig
8
Esta é a solução mais elegante, deve estar no topo.
ScalaWilliam
4
Editado para usar a solução do @ maxschlepzig ao executar no Python 3.x ( types.SimpleNamespaceinfelizmente não existe no 2.7).
111117 Dan Lenski
1
por que print_function?
chwi
90

Você pode tentar o seguinte:

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 criar um novo objeto e passar os parâmetros como um mapa.

cmaluenda
fonte
1
Recebo TypeError: objeto 'usuário' não é para permitir índices
Mahdi
1
Essa deve ser a resposta aceita. trabalhou para mim muito mais simples do que todo o resto.
Izik
Eu não usei * args, ** kwargs, mas a solução funcionou.
Malkaviano
1
O usuário (** j) diz que estão faltando os parâmetros de nome e nome de usuário, e também como o ditado é inicializado?
Aaron Stainback
40

Aqui está uma alternativa rápida e suja ao 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
fonte
1
Esta não é uma boa abordagem. No início, to_json e from_json não devem ser colocados em sua classe. No segundo, não funcionará trabalho para classes aninhadas.
Jurass 15/03
17

Para objetos complexos, você pode usar JSON Pickle

Biblioteca Python para serializar qualquer gráfico de objeto arbitrário no JSON. Pode levar quase qualquer objeto Python e transformá-lo em JSON. Além disso, ele pode reconstituir o objeto novamente no Python.

sputnikus
fonte
6
Eu acho que jsonstruct é melhor. 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
O problema com o jsonstruct é que ele não parece ser mantido (na verdade, parece abandonado) e falha ao converter uma lista de objetos, como '[{"name":"object1"},{"name":"object2"}]'. O jsonpickle também não lida muito bem com isso.
LS
1
Não faço ideia por que essa resposta não está recebendo mais votos. A maioria das outras soluções já existe. Alguém desenvolveu uma ótima biblioteca para JSON de / serialização - por que não usá-la? Além disso, parece estar funcionando bem com listas - qual foi o seu problema com o @LS?
guyarad
1
@guyarad, o problema é: x = jsonpickle.decode ('[{"name": "object1"}, {"name": "object2"}]' ') fornece uma lista de dicionários ([{' name ':' objeto1 '}, {' nome ':' objeto2 '}]), não uma lista de objetos com propriedades (x [0] .name ==' objeto1 '), que é o que a pergunta original exigia. Para conseguir isso, acabei usando a abordagem object_hook / Namespace sugerida por eddygeek, mas a abordagem rápida / suja da ubershmekel também parece boa. Eu acho que poderia usar object_hook com set_encoder_options () (não documentado!) Do jsonpickle, mas seria necessário mais código do que o módulo json básico. Eu adoraria ser provado errado!
LS
@LS se você não tiver controle sobre a entrada, que é realmente o que o OP pediu, o jsonpickle não é o ideal, pois espera o tipo real em cada nível (e assumirá tipos básicos se estiver ausente). Ambas as soluções são "fofas".
guyarad
12

Se você estiver usando o Python 3.5+, poderá jsonsserializar e desserializar para limpar objetos antigos do 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()

Você também pode FbApiUserherdar jsons.JsonSerializablepara obter mais elegância:

user = FbApiUser.from_json(response)

Esses exemplos funcionarão se a sua classe consistir em tipos padrão do Python, como seqüências de caracteres, números inteiros, listas, horários, etc. A jsonslib exigirá dicas de tipo para tipos personalizados.

RH
fonte
7

Se você estiver usando o python 3.6+, poderá usar marshmallow-dataclass . Ao contrário de todas as soluções listadas acima, é simples e seguro:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})
lovasoa
fonte
TypeError: make_data_class() got an unexpected keyword argument 'many'
JOhn
@ John: Você deve abrir um problema com um caso de teste reproduzível em github.com/lovasoa/marshmallow_dataclass/issues
lovasoa
5

Eu escrevi uma pequena estrutura de (des) serialização chamada any2any que ajuda a fazer transformações complexas entre dois tipos de Python.

No seu caso, acho que você deseja transformar de um dicionário (obtido com json.loads) para um objeto complexo response.education ; response.name, com uma estrutura aninhada response.education.id, etc ... Então é exatamente para isso que essa estrutura é feita. A documentação ainda não está ótima, mas, ao usar any2any.simple.MappingToObject, você poderá fazer isso com muita facilidade. Por favor, pergunte se você precisar de ajuda.

sebpiq
fonte
Sebpiq, instalou o any2any e estou tendo problemas para entender a sequência pretendida de chamadas de método. Você poderia dar um exemplo simples de conversão de um dicionário em um objeto Python com uma propriedade para cada chave?
Sansjoe
Oi @sansjoe! Se você o instalou a partir do pypi, a versão está completamente desatualizada, fiz uma refatoração completa há algumas semanas. Você deve usar a versão github (Eu preciso fazer uma liberação apropriada!)
sebpiq
Eu o instalei no pypy porque o github disse para instalá-lo no pypy. Além disso, você disse que o pypy estava desatualizado há meses atrás. Não funcionou :( Arquivei
sneilan
5

Melhorando a resposta muito boa da lovasoa.

Se você estiver usando o python 3.6+, poderá usar:
pip install marshmallow-enume
pip install marshmallow-dataclass

É simples e seguro.

Você pode transformar sua classe em uma string-json e vice-versa:

Do objeto à string 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 String Json para Object:

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

Definições de 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
danilo
fonte
1
Você não precisa do construtor, apenas passe init = True para a classe de dados e estará pronto.
Josef Korbel
4

Como ninguém forneceu uma resposta como a minha, vou publicá-la aqui.

É uma classe robusta que pode ser facilmente convertida entre json stre dictque copiei da minha resposta para outra pergunta :

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ć
fonte
2

Modificando um pouco a resposta do @DS, para carregar de um arquivo:

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)

Uma coisa: isso não pode carregar itens com números à frente. Como isso:

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

Como "1_first_item" não é um nome de campo python válido.

Valtoni Boaventura
fonte
2

Enquanto procurava por uma solução, eu me deparei com esta postagem do blog: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

Ele usa a mesma técnica descrita nas respostas anteriores, mas com o uso de decoradores. Outra coisa que achei útil é o fato de ele retornar um objeto digitado no final da desserialização

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
fonte
2

Expandindo resposta de DS um pouco, se você precisa do objeto a ser mutável (que namedtuple não é), você pode usar o recordclass biblioteca, em vez 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()))

O objeto modificado pode ser convertido de volta para json com muita facilidade usando simplejson :

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

Se você estiver usando o Python 3.6 ou mais recente, poderá dar uma olhada no squema - um módulo leve para estruturas de dados com tipos estatísticos. Facilita a leitura do código e, ao mesmo tempo, fornece validação, conversão e serialização simples de dados sem trabalho extra. Você pode pensar nisso como uma alternativa mais sofisticada e opinativa para os nomes nomeados e classes de dados. Veja como você pode usá-lo:

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
fonte
Isso também é mais parecido com as formas de linguagem da JVM.
javadba 21/01
1

Eu estava procurando por uma solução que funcionasse recordclass.RecordClass, suporte objetos aninhados e funcionasse para serialização json e desserialização json.

Expandindo a resposta do DS e expandindo a solução da BeneStr, vim com o seguinte 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
fonte
1

As respostas fornecidas aqui não retornam o tipo de objeto correto, portanto, criei esses métodos abaixo. Eles também falharão se você tentar adicionar mais campos à classe que não existe no JSON fornecido:

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
fonte
0

Python3.x

A melhor abordagem que pude alcançar com meu conhecimento foi essa.
Observe que esse código também trata set ().
Essa abordagem é genérica, apenas necessitando da extensão da classe (no segundo exemplo).
Observe que estou fazendo isso apenas com arquivos, mas é fácil modificar o comportamento ao seu gosto.

No entanto, este é um CoDec.

Com um pouco mais de trabalho, você pode construir sua classe de outras maneiras. Eu assumo um construtor padrão para instância, então eu atualizo o dict de 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)

Editar

Com um pouco mais de pesquisa, encontrei uma maneira de generalizar sem a necessidade da chamada do método de registro SUPERCLASS , usando uma 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
Davi Abreu Wasserberg
fonte
0

Você pode usar

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

Onde

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 uma solução genérica e à prova de futuro.

Gulzar
fonte
-5

Use o jsonmódulo ( novo no Python 2.6 ) ou o simplejsonmódulo que quase sempre está instalado.

Chris Morgan
fonte
2
Ei, obrigado por responder. Você pode postar um exemplo de como decodificar o JSON e acessar esses dados?
Sai Krishna
Ei, agora você tem razão, mas de alguma forma, prefiro ficar sem saber e depois fazer engenharia reversa: D.
Sai Krishna
1
@Zach: existem exemplos no topo dos documentos aos quais vinculei.
31711 Chris