Як конвертувати дані JSON в об’єкт Python


281

Я хочу використовувати Python для перетворення даних JSON в об’єкт Python.

Я отримую об’єкти даних JSON від API Facebook, який я хочу зберігати у своїй базі даних.

Мій поточний погляд у Django (Python) ( request.POSTмістить JSON):

response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
  • Це добре працює, але як я обробляю складні об'єкти даних JSON?

  • Не було б набагато краще, якби я міг якось перетворити цей об’єкт JSON в об’єкт Python для зручного використання?


Зазвичай JSON перетворюється на ванільні списки чи дикти. Це те, чого ти хочеш? Або ви сподіваєтесь перетворити JSON прямо у користувацький тип?
Шакакай

Я хочу перетворити його в об'єкт, до чого я можу отримати доступ, використовуючи "." . Як і у наведеному вище прикладі -> reponse.name, response.education.id тощо ....
Сай Кришна

44
Використання dicts - це слабкий соус для об'єктно-орієнтованого програмування. Словники - дуже поганий спосіб донести очікування до читачів вашого коду. Як можна за допомогою словника чітко та багаторазово вказати, що потрібні деякі пари ключових значень-значень, а інші - ні? Як щодо підтвердження того, що задане значення знаходиться у прийнятному діапазоні чи встановленому? А як щодо функцій, специфічних для типу об’єкта, з яким ви працюєте (він же методи)? Словники зручні та універсальні, але занадто багато розробників діють так, ніби вони забули, що Python є об'єктно орієнтованою мовою.
Тушковане

1
Є бібліотека python для цього github.com/jsonpickle/jsonpickle (коментуючи, оскільки відповідь занадто внизу в темі і не буде доступною.)
найкращі побажання

Відповіді:


355

Ви можете зробити це в один рядок, використовуючи namedtupleта 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

або, щоб легко використати це:

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)

Якщо ви хочете, щоб впоратися з ключами, які не є хорошими іменами атрибутів, перевірити namedtuple«s renameпараметр .


8
це може призвести до помилки значення. ValueError: Імена та назви полів не можуть починатися з числа: '123'
PvdL

3
Як новачок у Python, мені цікаво, чи це заощаджує і те, коли безпека є проблемою.
benjist

8
Це створює новий інший клас щоразу, коли стикається з об'єктом JSON під час розбору, правда?
fikr4n

2
Цікаво. Я думав, що покладатися на d.keys()та d.values()повторювати в одному порядку не гарантується, але я помилявся. У документах кажуть: «Якщо ключі, цінності і погляди пунктів повторюються через без проміжних змін в словник, порядок елементів буде безпосередньо відповідати.». Добре знати про такі невеликі локальні блоки кодів. Я хотів би додати коментар, хоча явно попередив би утримувачів коду такої залежності.
cfi

1
Я не знаю жодної приємної операції зворотного зв'язку загального призначення. Будь-яка окрема назва з назвою може бути перетворена на дік за x._asdict()допомогою простих випадків.
ДС.

127

Перегляньте розділ Спеціалізоване декодування об’єктів JSON в json документації модуля . Ви можете використовувати це для декодування об'єкта JSON у певний тип Python.

Ось приклад:

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

Оновлення

Якщо ви хочете отримати доступ до даних у словнику через модуль json, зробіть це:

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

Так само, як звичайний словник.


1
Гей, я щойно читав і зрозумів, що словники цілком будуть робити, тільки мені було цікаво, як перетворити JSON-об'єкти в словники та як мені отримати доступ до цих даних зі словника?
Сай Крішна

Дивовижно, це майже зрозуміло, просто хотілося дізнатися ще одну дрібницю, що якщо є цей об’єкт -> {'education': {'name1': 456, 'name2': 567}}, як мені отримати доступ до цих даних?
Сай Крішна

це було б просто topLevelData ['освіта'] ['name1'] ==> 456. має сенс?
Shakakai

1
@Ben: Я вважаю, що ваш коментар є недоречним. З усіх відповідей на даний момент, це єдиний, щоб правильно отримати заняття. Що означає: це операція в один прохід, і в результаті використовуються правильні типи. Сам Pickle призначений для різних застосувань, ніж JSON (бінарний проти текстового представника), а jsonpickle - це нестандартна вібрація. Мені буде цікаво побачити, як ви вирішите проблему, що lib std json не надає верхньому дереву розбору об'єкта гачка
cfi

Я з цим повинен погодитися з @Ben. Це дійсно погане рішення. Не масштабується зовсім. Вам потрібно буде підтримувати імена полів як рядок і як поле. Якщо ви хочете перефактурувати свої поля, декодування не вдасться (звичайно, вже серіалізовані дані більше не будуть актуальними). Ця ж концепція вже добре реалізована з jsonpickle
guyarad

98

Це не кодовий гольф, але ось мій найкоротший трюк, який використовується types.SimpleNamespaceяк контейнер для об'єктів JSON.

Порівняно з провідним namedtupleрішенням, це:

  • ймовірно, швидше / менше, оскільки це не створює клас для кожного об'єкта
  • коротше
  • жоден renameваріант, і, мабуть, те саме обмеження для ключів, які не є дійсними ідентифікаторами (використовує setattrпід обкладинками)

Приклад:

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
До речі, бібліотека серіалізації Marshmallow пропонує подібну особливість зі своїм @post_loadдекоратором. marshmallow.readthedocs.io/en/latest/…
Тейлор Едмістон

3
Щоб уникнути залежності від argparse: замініть імпорт argparse на from types import SimpleNamespaceта використовуйте:x = json.loads(data, object_hook=lambda d: SimpleNamespace(**d))
maxschlepzig

8
Це найелегантніше рішення, повинно бути вгорі.
ScalaWilliam

4
Відредаговано для використання рішення @ maxschlepzig під час роботи під Python 3.x ( types.SimpleNamespaceна жаль, не існує в 2.7).
Дан Ленський

1
чому print_function?
chwi

90

Ви можете спробувати це:

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)

Просто створіть новий Об'єкт і передайте параметри як карту.


1
Я отримую TypeError: "Користувач" об'єкт не піддається підписки
Махді

1
Це має бути прийнятою відповіддю. працювала для мене реклама набагато простіше, ніж усі інші.
Ізик

Я не використовував * args, ** kwargs, але рішення спрацювало.
Malkaviano

1
Користувач (** к) говорить , що він відсутня назва та ім'я користувача параметри, а також яким чином диктує не започатковано?
Аарон Штайнбек

40

Ось швидка та брудна альтернатива соління 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
Це не гарний підхід. Спочатку to_json та from_json не слід розміщувати у вашому класі. По-друге, це не буде працювати для вкладених класів.
Юра

17

Для складних об'єктів можна використовувати JSON Pickle

Бібліотека Python для серіалізації будь-якого довільного графіка об'єктів у JSON. Він може прийняти практично будь-який об’єкт Python і перетворити об'єкт у JSON. Крім того, він може відновити об'єкт назад в Python.


6
Я думаю, що jsonstruct краще. 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.
Абхішек Гупта

3
Проблеми з jsonstruct полягають у тому, що він, здається, не підтримується (насправді він виглядає покинутим), і він не в змозі перетворити список об’єктів, як-от '[{"name":"object1"},{"name":"object2"}]'. jsonpickle теж не дуже добре впорається.
LS

1
Я поняття не маю, чому ця відповідь не отримує більше голосів. Більшість інших рішень цілком там. Хтось розробив чудову бібліотеку для де / серіалізації JSON - чому б не використати її? Окрім того, схоже, це добре працює зі списками - у чому була ваша проблема з ним @LS?
guyarad

1
@guyarad, проблема: x = jsonpickle.decode ('[{"ім'я": "object1"}, {"name": "object2"}]') дає список словників ([{'name': ' object1 '}, {' name ':' object2 '}]), не перелік об'єктів із властивостями (x [0] .name ==' object1 '), що вимагає оригінального запитання. Щоб досягти цього, я в кінцевому підсумку використовував підхід object_hook / Namespace, запропонований eddygeek, але швидкий / брудний підхід ubershmekel теж виглядає добре. Я думаю, що я міг би використовувати object_hook з set_encoder_options () jsonpickle () (без документації!), Але це займе більше коду, ніж основний модуль json. Я б хотів, щоб я виявився неправильним!
LS

@LS якщо у вас немає контролю над входом, що справді те, що запитував ОП, jsonpickle не є ідеальним, оскільки він очікує фактичного типу на кожному рівні (і передбачає базові типи, якщо вони відсутні). Обидва рішення "милі".
guyarad

12

Якщо ви використовуєте Python 3.5+, ви можете використовувати jsonsсеріалізацію та деріаріалізацію для простих старих об’єктів 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()

Ви також можете зробити FbApiUserспадщину від jsons.JsonSerializableдля більшої елегантності:

user = FbApiUser.from_json(response)

Ці приклади спрацьовують, якщо ваш клас складається з типів за замовчуванням Python, наприклад, рядків, цілих чисел, списків, дат тощо та ін. jsonsЛіб вимагатиме підказки щодо типів для власних типів.


7

Якщо ви використовуєте python 3.6+, ви можете використовувати клас зефірних даних . На відміну від усіх перерахованих вище рішень, це і просто, і безпечно для типу:

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'
Джон

@JOhn: Вам слід відкрити проблему з відтворюваною тестовою справою в github.com/lovasoa/marshmallow_dataclass/isissue
lovasoa

6

Удосконалення дуже доброї відповіді ловасоа.

Якщо ви використовуєте python 3.6+, ви можете використовувати:
pip install marshmallow-enumі
pip install marshmallow-dataclass

Його простий і типовий сейф.

Ви можете перетворити свій клас у рядок-json і навпаки:

Від об'єкта до рядка 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

Від String Json до Object:

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

Визначення класів:

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
Вам не потрібен конструктор, просто передайте init = True в клас даних, і ви готові йти.
Йозеф Корбель

5

Я написав невелику рамку (де) серіалізації під назвою any2any, яка допомагає робити складні перетворення між двома типами Python.

У вашому випадку, я думаю, ви хочете перетворити зі словника (отриманого з json.loads) в складний об'єкт response.education ; response.name, з вкладеною структурою response.education.idтощо ... Отже, саме для цього створено цю рамку. Документація ще не велика, але, використовуючи any2any.simple.MappingToObject, ви зможете це зробити дуже легко. Запитайте, чи потрібна вам допомога.


Sebpiq, встановили any2any і у мене виникають проблеми з розумінням призначеної послідовності викликів методу. Чи можете ви навести простий приклад перетворення словника в об’єкт Python із властивістю для кожного ключа?
sansjoe

Привіт @sansjoe! Якщо ви встановили його з pypi, версія повністю застаріла, я зробив повний рефакторинг кілька тижнів тому. Вам слід скористатися версією github (мені потрібно зробити належний реліз!)
sebpiq

Я встановив його з pypy, тому що github сказав встановити його з pypy. Крім того, ви сказали, що pypy застарів місяць тому .. Це не спрацювало :( Я подав звіт про помилку tho! Github.com/sebpiq/any2any/isissue/11
sneilan

4

Оскільки ніхто не дав відповіді такої, як моя, я збираюся її опублікувати тут.

Це надійний клас, який легко перетворює назад і назад між json strі dictякий я скопіював із своєї відповіді на інше питання :

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

Трохи змінивши відповідь @DS для завантаження з файлу:

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)

Одне: це не може завантажувати предмети з цифрами вперед. Подобається це:

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

Тому що "1_first_item" не є дійсною назвою поля python.


2

Шукаючи рішення, я натрапив на цю публікацію в блозі: https://blog.mosthege.net/2016/11/12/json-deserialization-of-nested-objects/

Він використовує ту саму техніку, що і в попередніх відповідях, але з використанням декораторів. Ще одна річ, яку я вважав корисною, це той факт, що він повертає набраний об’єкт наприкінці десеріалізації

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)

Використання:

@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

Трохи розгорнувши відповідь DS, якщо вам потрібен об'єкт, який потрібно змінювати (якого nametuple немає), ви можете використовувати бібліотеку записів класу замість nametuple:

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

Потім модифікований об'єкт може бути легко перетворений назад у json за допомогою simplejson :

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

1

Якщо ви використовуєте Python 3.6 або новішу версію, ви можете ознайомитись із squema - легким модулем для статичної структури даних. Це робить ваш код легким для читання, в той же час забезпечує просту перевірку даних, перетворення та серіалізацію без зайвих робіт. Ви можете розглядати це як більш досконалу та впевнену альтернативу названим парам і класам даних. Ось як ви могли ним скористатися:

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

Це також більше схоже на способи мовлення JVM.
javadba

1

Я шукав рішення, з яким працював recordclass.RecordClass, підтримує вкладені об'єкти і працює як для серіалізації json, так і для деріаріалізації json.

Розширюючи відповідь DS і розширюючи рішення від BeneStr, я придумав таке, що, здається, працює:

Код:

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)

Використання:

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

Надані тут відповіді не повертають правильний тип об'єкта, отже, я створив ці методи нижче. Вони також не вдається, якщо ви спробуєте додати більше класів до класу, який не існує в даному 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)

0

Python3.x

Найкращий підхід, якого я міг досягти зі своїми знаннями, це такий.
Зауважте, що цей код трактується також як set ().
Цей підхід є загальним, що просто потребує розширення класу (у другому прикладі).
Зауважте, що я просто роблю це у файлах, але легко змінити поведінку на свій смак.

Однак це CoDec.

Трохи більше роботи ви можете сконструювати свій клас іншими способами. Я припускаю конструктор за замовчуванням, щоб застосувати його, тоді я оновлюю клас клас.

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)

Редагувати

За допомогою деяких інших досліджень я знайшов спосіб узагальнити без необхідності виклику методу реєстру SUPERCLASS , використовуючи метаклас

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

Можна використовувати

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

де

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]

Для загального, надійного рішення.


-5

Використовуйте jsonмодуль ( новий у Python 2.6 ) або simplejsonмодуль, який майже завжди встановлюється.


2
Гей, дякую за відповідь. Чи можете ви опублікувати приклад, як розшифрувати JSON, а потім отримати доступ до цих даних?
Сай Крішна

Гей, тепер у вас є питання, але я якось вважаю за краще робити це, не знаючи, а потім реверсувати його: D.
Сай Кришна,

1
@Zach: у верхній частині документів, з якими я пов’язаний, є приклади.
Кріс Морган
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.