Jak przekonwertować dane JSON na obiekt Python

281

Chcę użyć języka Python do konwersji danych JSON na obiekt języka Python.

Otrzymuję obiekty danych JSON z interfejsu API Facebooka, które chcę przechowywać w mojej bazie danych.

Mój obecny widok w Django (Python) ( request.POSTzawiera JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Działa to dobrze, ale jak obsługiwać złożone obiekty danych JSON?

  • Czy nie byłoby znacznie lepiej, gdybym mógł jakoś przekonwertować ten obiekt JSON na obiekt Python dla łatwego użycia?

Sai Kryszna
źródło
Zazwyczaj JSON jest konwertowany na listy waniliowe lub dyktanda. Czy tego chcesz? A może chcesz przekonwertować JSON bezpośrednio na niestandardowy typ?
Shakakai,
Chcę przekonwertować go na obiekt, do którego mogę uzyskać dostęp za pomocą „.” . Jak w powyższym przykładzie -> reponse.name, response.education.id itp ....
Sai Krishna
44
Używanie dicts to słaby sposób na programowanie obiektowe. Słowniki to bardzo słaby sposób komunikowania oczekiwań czytelnikom twojego kodu. Używając słownika, w jaki sposób możesz jasno i wielokrotnie określić, że niektóre pary kluczy słownik-wartość są wymagane, a inne nie? Co powiesz na potwierdzenie, że dana wartość mieści się w dopuszczalnym zakresie lub jest ustawiona? Co z funkcjami specyficznymi dla typu obiektu, z którym pracujesz (inaczej metody)? Słowniki są przydatne i wszechstronne, ale zbyt wielu programistów zachowuje się tak, jakby zapomniało, że Python jest językiem zorientowanym obiektowo z jakiegoś powodu.
Gulasz
1
Istnieje biblioteka python dla tego github.com/jsonpickle/jsonpickle (komentowanie, ponieważ odpowiedź jest zbyt niska w wątku i nie będzie dostępna).
najlepsze życzenia

Odpowiedzi:

355

Możesz to zrobić w jednym wierszu, używając namedtuplei 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

lub, aby ponownie łatwo to wykorzystać:

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)

Jeśli chcesz go obsługiwać klawisze, które nie są dobre nazwy atrybutów, sprawdź namedtuple„s renameparametr .

DS.
źródło
8
może to spowodować błąd wartości, błąd wartości: nazwy typów i nazwy pól nie mogą zaczynać się cyfrą: „123”
PvdL
3
Jako nowicjusz Pythona interesuje mnie, czy jest to opcja oszczędzania również wtedy, gdy problemem jest bezpieczeństwo.
benjist
8
To tworzy nową inną klasę za każdym razem, gdy napotyka obiekt JSON podczas analizowania, prawda?
fikr4n
2
Ciekawy. Myślałem, że poleganie d.keys()i d.values()powtarzanie w tej samej kolejności nie jest gwarantowane, ale się myliłem. W docs powiedzieć: „Jeśli klucze, wartości i poglądy są przedmioty powtórzyć nad bez interweniujących modyfikacji do słownika, kolejność rzeczy bezpośrednio odpowiadają.”. Dobrze wiedzieć o tak małych lokalnych blokach kodu. Dodałbym jednak komentarz, aby wyraźnie powiadomić opiekunów kodu o takiej zależności.
cfi
1
Nie znam żadnej ładnej operacji odwrotnej ogólnego zastosowania. Każda indywidualna nazwa może zostać przekształcona w dykt x._asdict(), co może pomóc w prostych przypadkach.
DS.
127

Sprawdź sekcję Specjalizowanie dekodowania obiektów JSON w json dokumentacji modułu . Możesz go użyć do zdekodowania obiektu JSON na określony typ Pythona.

Oto przykład:

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

Aktualizacja

Jeśli chcesz uzyskać dostęp do danych w słowniku za pomocą modułu json:

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

Podobnie jak zwykły słownik.

Shakakai
źródło
1
Hej, właśnie czytałem i zdałem sobie sprawę, że słowniki całkowicie wystarczą, tylko zastanawiałem się, jak przekształcić obiekty JSON w słowniki i jak uzyskać dostęp do tych danych ze słownika?
Sai Krishna,
Niesamowite, jest prawie jasne, chciałem tylko wiedzieć coś jeszcze, że jeśli istnieje ten obiekt -> {'education': {'name1': 456, 'name2': 567}}, w jaki sposób mogę uzyskać dostęp do tych danych?
Sai Krishna,
byłoby to po prostu topLevelData ['edukacja'] ['nazwa1'] ==> 456. Czy ma to sens?
Shakakai,
1
@Ben: Myślę, że twój komentarz jest nieodpowiedni. Spośród wszystkich odpowiedzi tutaj obecnie jest to jedyna, która poprawia zajęcia. Co oznacza: Jest to operacja jednoprzebiegowa, a wynik wykorzystuje prawidłowe typy. Sam Pickle jest przeznaczony do różnych aplikacji niż JSON (repozytorium binarne kontra tekstowe), a jsonpickle jest niestandardową biblioteką. Chciałbym zobaczyć, jak rozwiązujesz problem polegający na tym, że biblioteka std json lib nie zapewnia górnego drzewa analizy składniowej dla obiektu hook
cfi
Muszę się z tym zgodzić z @Ben. To naprawdę złe rozwiązanie. W ogóle nie jest skalowalny. Musisz zachować nazwy pól jako ciąg znaków i jako pole. Jeśli będziesz chciał zmienić swoje pola, dekodowanie zakończy się niepowodzeniem (oczywiście już zserializowane dane i tak nie będą już istotne). Ta sama koncepcja jest już dobrze zaimplementowana w jsonpickle
guyarad,
98

To nie jest kod golfowy, ale oto moja najkrótsza sztuczka, wykorzystująca types.SimpleNamespacejako pojemnik na obiekty JSON.

W porównaniu do wiodącego namedtuplerozwiązania jest to:

  • prawdopodobnie szybszy / mniejszy, ponieważ nie tworzy klasy dla każdego obiektu
  • krótszy
  • brak renameopcji i prawdopodobnie takie same ograniczenie dotyczące kluczy, które nie są prawidłowymi identyfikatorami (zastosowania setattrpod osłonami)

Przykład:

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
źródło
2
Nawiasem mówiąc, biblioteka serializacji Marshmallow oferuje podobną funkcję ze swoim @post_loaddekoratorem. marshmallow.readthedocs.io/en/latest/…
Taylor Edmiston
3
Aby uniknąć zależności od argparse: zamień import from types import SimpleNamespacex = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
argparse na
8
To najbardziej eleganckie rozwiązanie, powinno być na górze.
ScalaWilliam
4
Edytowane, aby używać rozwiązania @ maxschlepzig, gdy działa pod Pythonem 3.x ( types.SimpleNamespaceniestety nie istnieje w 2.7).
Dan Lenski
1
dlaczego print_function?
chwi
90

Możesz spróbować:

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)

Wystarczy utworzyć nowy obiekt i przekazać parametry jako mapę.

cmaluenda
źródło
1
Otrzymuję TypeError: Obiekt „User” nie podlega indeksowi
Mahdi
1
To powinna być zaakceptowana odpowiedź. działało dla mnie znacznie prostsze niż cała reszta.
Izik
Nie używałem * args, ** kwargs, ale rozwiązanie działało.
Malkaviano,
1
Użytkownik (** j) mówi, że brakuje nazwisko i nazwę użytkownika parametrów, a także w jaki sposób DICT się zainicjowana?
Aaron Stainback,
40

Oto szybka i brudna alternatywa dla marynaty

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
źródło
1
To nie jest dobre podejście. Najpierw to_json i from_json nie powinny być umieszczane w twojej klasie. Po drugie nie będzie działać dla zagnieżdżonych klas.
Jurass
17

W przypadku złożonych obiektów możesz użyć JSON Pickle

Biblioteka Pythona do szeregowania dowolnych grafów obiektowych w JSON. Może zająć prawie każdy obiekt w języku Python i zmienić go w JSON. Dodatkowo może odtworzyć obiekt z powrotem w Pythonie.

sputnikus
źródło
6
Myślę, że jsonstruct jest lepszy. 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
Problem z jsonstruct polega na tym, że nie wydaje się być utrzymywany (w rzeczywistości wygląda na opuszczony) i nie może przekonwertować listy obiektów, takich jak '[{"name":"object1"},{"name":"object2"}]'. jsonpickle też nie radzi sobie z tym dobrze.
LS
1
Nie mam pojęcia, dlaczego ta odpowiedź nie ma więcej głosów. Większość innych rozwiązań jest już dostępna. Ktoś opracował świetną bibliotekę dla JSON de / serialization - dlaczego jej nie użyć? Ponadto wydaje się, że działa dobrze z listami - jaki był z tym problem @LS?
guyarad
1
@ guyarad, problem polega na: x = jsonpickle.decode ('[{"name": "object1"}, {"name": "object2"}]') podaje listę słowników ([{'name': ' object1 '}, {' name ':' object2 '}]), a nie lista obiektów o właściwościach (x [0] .name ==' object1 '), co wymagało pierwotnego pytania. Aby to uzyskać, skończyłem na metodzie object_hook / Namespace sugerowanej przez eddygeek, ale szybkie / brudne podejście ubershmekel również wygląda dobrze. Myślę, że mógłbym użyć object_hook z set_encoder_options () (nieudokumentowane!) Jsonpickle'a, ale zajęłoby to więcej kodu niż podstawowy moduł json. Chciałbym, aby udowodniono, że się mylę!
LS
@LS, jeśli nie masz kontroli nad danymi wejściowymi, co jest dokładnie tym, o co poprosił OP, jsonpickle nie jest idealny, ponieważ oczekuje rzeczywistego typu na każdym poziomie (i zakłada podstawowe typy, jeśli ich brakuje). Oba rozwiązania są „urocze”.
guyarad
12

Jeśli używasz Python 3.5+, możesz użyć jsonsdo serializacji i deserializacji do zwykłych starych obiektów 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()

Możesz także FbApiUserdziedziczyć jsons.JsonSerializablepo więcej elegancji:

user = FbApiUser.from_json(response)

Te przykłady będą działać, jeśli twoja klasa składa się z domyślnych typów języka Python, takich jak ciągi, liczby całkowite, listy, czasy danych itp. Biblioteka jsonsbędzie jednak wymagać wskazówek typów dla typów niestandardowych.

RH
źródło
7

Jeśli używasz Pythona 3.6+, możesz użyć marshmallow-dataclass . W przeciwieństwie do wszystkich wyżej wymienionych rozwiązań, jest on zarówno prosty, jak i bezpieczny:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})
lovasoa
źródło
TypeError: make_data_class() got an unexpected keyword argument 'many'
JOhn
@JOhn: Powinieneś otworzyć problem z odtwarzalnym przypadkiem testowym w github.com/lovasoa/marshmallow_dataclass/issues
lovasoa
6

Poprawa bardzo dobrej odpowiedzi lovasoa.

Jeśli używasz Pythona 3.6+, możesz użyć:
pip install marshmallow-enumi
pip install marshmallow-dataclass

Jest prosty i bezpieczny dla typu.

Możesz przekształcić swoją klasę w string-json i odwrotnie:

Od obiektu do ciągu 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

Od String Json do Object:

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

Definicje klas:

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
źródło
1
Nie potrzebujesz konstruktora, po prostu przekaż init = True do klasy danych i możesz zacząć.
Josef Korbel
5

Napisałem małą strukturę (de) serializacji o nazwie any2any która pomaga w złożonej transformacji między dwoma typami Pythona.

W twoim przypadku myślę, że chcesz przekształcić ze słownika (uzyskanego za pomocą json.loads) w złożony obiekt response.education ; response.name, z zagnieżdżoną strukturą response.education.iditp. Więc właśnie do tego służy ten framework. Dokumentacja nie jest jeszcze świetna, ale korzystając z niej any2any.simple.MappingToObject, powinieneś być w stanie to zrobić bardzo łatwo. Zapytaj, czy potrzebujesz pomocy.

Sebpiq
źródło
Sebpiq, zainstalowałem any2any i mam problemy ze zrozumieniem zamierzonej sekwencji wywołań metod. Czy możesz podać prosty przykład konwersji słownika na obiekt Python z właściwością dla każdego klucza?
sansjoe
Cześć @sansjoe! Jeśli zainstalowałeś go z pypi, wersja jest całkowicie nieaktualna, kilka tygodni temu dokonałem pełnego refaktoryzacji. Powinieneś użyć wersji github (muszę dokonać właściwego wydania!)
sebpiq
Zainstalowałem go z pypy, ponieważ github powiedział, aby zainstalować go z pypy. Powiedziałeś też, że pypy był nieaktualny kilka miesięcy temu .. To nie działało :( Złożyłem
sneilan 1'12
4

Ponieważ nikt nie udzielił odpowiedzi podobnej do mojej, zamieszczę ją tutaj.

Jest to solidna klasa, która może z łatwością konwertować między jsonem stri dictktórą skopiowałem z mojej odpowiedzi na inne pytanie :

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ć
źródło
2

Trochę modyfikując odpowiedź @DS, aby załadować z pliku:

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)

Jedno: nie można załadować przedmiotów z numerami z przodu. Lubię to:

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

Ponieważ „1_first_item” nie jest prawidłową nazwą pola python.

Valtoni Boaventura
źródło
2

Podczas szukania rozwiązania natknąłem się na ten post na blogu: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

Wykorzystuje tę samą technikę, jak podano w poprzednich odpowiedziach, ale z wykorzystaniem dekoratorów. Inną rzeczą, którą uznałem za przydatną, jest fakt, że zwraca on wpisany obiekt na końcu deserializacji

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)

Stosowanie:

@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
źródło
2

Rozwijając nieco odpowiedź DS, jeśli potrzebujesz, aby obiekt był modyfikowalny (którego nazwa nie ma), możesz użyć biblioteki klas rekordów zamiast 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()))

Zmodyfikowany obiekt można następnie bardzo łatwo przekonwertować z powrotem do formatu json za pomocą simplejson :

x.name = "John Doe"
new_json = simplejson.dumps(x)
BeneStr
źródło
1

Jeśli używasz języka Python 3.6 lub nowszego, możesz rzucić okiem na squema - lekki moduł do statycznie typowanych struktur danych. Dzięki temu kod jest łatwy do odczytania, a jednocześnie zapewnia łatwą weryfikację, konwersję i serializację danych bez dodatkowej pracy. Możesz myśleć o tym jako o bardziej wyrafinowanej i upartej alternatywie dla nazwanych świątyń i klas danych. Oto jak możesz go użyć:

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()
Funkcjonalny
źródło
Jest to również bardziej podobne do tego, aby to zrobić w języku JVM.
javadba
1

Szukałem rozwiązania, które działałoby recordclass.RecordClass , obsługuje zagnieżdżone obiekty i działa zarówno dla serializacji json, jak i deserializacji json.

Rozwijając odpowiedź DS i rozwijając rozwiązanie BeneStr, opracowałem następujące rozwiązanie, które wydaje się działać:

Kod:

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)

Stosowanie:

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.
źródło
1

Podane tutaj odpowiedzi nie zwracają poprawnego typu obiektu, dlatego stworzyłem te metody poniżej. Nie powiodą się również, jeśli spróbujesz dodać więcej pól do klasy, która nie istnieje w danym JSON:

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
źródło
0

Python3.x

Najlepsze podejście, jakie mogłem osiągnąć dzięki mojej wiedzy, było takie.
Zauważ, że ten kod również traktuje set ().
To podejście jest ogólne, wymaga jedynie rozszerzenia klasy (w drugim przykładzie).
Zauważ, że robię to tylko dla plików, ale łatwo jest zmodyfikować zachowanie według własnego uznania.

Jest to jednak CoDec.

Przy odrobinie pracy możesz zbudować swoją klasę na inne sposoby. Zakładam, że domyślny konstruktor to instancja, a następnie aktualizuję dyktę klasową.

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)

Edytować

Po kilku dalszych badaniach znalazłem sposób na uogólnienie bez potrzeby wywoływania metody rejestrowania SUPERCLASS za pomocą metaklasy

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
źródło
0

Możesz użyć

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

gdzie

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]

Dla ogólnego, przyszłościowego rozwiązania.

Gulzar
źródło
-5

Użyj jsonmodułu ( nowego w Pythonie 2.6 ) lub simplejsonmodułu, który prawie zawsze jest zainstalowany.

Chris Morgan
źródło
2
Hej, dziękuję za odpowiedź. Czy możesz podać przykład dekodowania JSON, a następnie uzyskać dostęp do tych danych?
Sai Krishna,
Hej, teraz masz rację, ale w jakiś sposób wolę robić to bez wiedzy, a potem dokonać inżynierii wstecznej: D.
Sai Krishna
1
@Zach: istnieją przykłady na górze dokumentów, z którymi się łączyłem.
Chris Morgan