Как преобразовать данные JSON в объект Python

282

Я хочу использовать 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 (комментируя, так как ответ слишком
наилучшие пожелания

Ответы:

357

Вы можете сделать это в одну строку, используя 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параметр .

DS.
источник
8
это может привести к ошибке Value, ValueError: Имена типов и имена полей не могут начинаться с цифры: «123»
PvdL
3
Как новичок в Python, меня интересует, будет ли это сохранением, когда проблема безопасности.
Benjist
8
Это создает новый РАЗЛИЧНЫЙ класс каждый раз , встречая объект JSON во время разбора, верно?
fikr4n
2
Интересный. Я думал, что полагаться d.keys()и d.values()повторять в одном и том же порядке не гарантируется, но я ошибся. В документах говорится: «Если представления ключей, значений и элементов повторяются без внесения изменений в словарь, порядок элементов будет напрямую соответствовать». Полезно знать для таких маленьких, локальных блоков кода. Я бы добавил комментарий, чтобы явно предупредить разработчиков кода о такой зависимости.
CFI
1
Я не знаю ни о какой хорошей обратной операции общего назначения. Любой отдельный именованный кортеж может быть превращен в диктовку x._asdict(), что может помочь в простых случаях.
DS.
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']

Так же, как обычный словарь.

Shakakai
источник
1
Эй, я только что прочитал и понял, что словари вполне подойдут, только мне было интересно, как преобразовать объекты JSON в словари и как получить доступ к этим данным из словаря?
Саи Кришна
Удивительно, это почти понятно, просто хотелось узнать еще одну маленькую вещь, что если есть этот объект -> {'education': {'name1': 456, 'name2': 567}}, как мне получить доступ к этим данным?
Саи Кришна
это просто будет topLevelData ['education'] ['name1'] ==> 456. имеет смысл?
Шакакай
1
@Ben: Я думаю, что ваш комментарий неуместен. Из всех ответов здесь в настоящее время это единственный способ правильно понять классы. Это означает: это однопроходная операция, и результат использует правильные типы. Pickle сам по себе предназначен для приложений, отличных от JSON (бинарный или текстовый повтор), а jsonpickle - нестандартная библиотека. Мне было бы интересно посмотреть, как вы решите проблему,
заключающуюся в том, что библиотека
Я должен согласиться с @Ben по этому вопросу. Это действительно плохое решение. Не масштабируется вообще. Вам нужно будет поддерживать имена полей в виде строки и поля. Если вы захотите провести рефакторинг ваших полей, декодирование не удастся (конечно, уже сериализованные данные больше не будут актуальны). Та же концепция уже реализована jsonpickle
guyarad
99

Это не кодовый гольф, но вот мой самый короткий трюк, использующий 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)
eddygeek
источник
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)

Просто создайте новый объект и передайте параметры в виде карты.

cmaluenda
источник
1
Я получаю TypeError: объект «Пользователь» не может быть
Махди
1
Это должен быть принятый ответ. работал для меня объявление намного проще, чем все остальные.
Izik
Я не использовал * args, ** kwargs, но решение работало.
Малкавиано
1
Пользователь (** j) говорит, что ему не хватает параметров имени и имени пользователя, а также как инициализируется dict ?
Аарон Стейнбек
40

Вот быстрая и грязная альтернатива 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
источник
1
Это не очень хороший подход. Сначала to_json и from_json не должны быть размещены в вашем классе. Во-вторых, это не будет работать для вложенных классов.
Юра
17

Для сложных объектов вы можете использовать JSON Pickle

Библиотека Python для сериализации любого произвольного графа объектов в JSON. Он может взять практически любой объект Python и превратить его в JSON. Кроме того, он может восстановить объект обратно в Python.

sputnikus
источник
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 ('[{"name": "object1"}, {"name": "object2"}]') дает список словарей ([{'name': ' object1 '}, {' name ':' object2 '}]), а не список объектов со свойствами (x [0] .name ==' object1 '), что и требовалось для исходного вопроса. Чтобы получить это, я использовал подход object_hook / Namespace, предложенный eddygeek, но быстрый / грязный подход ubershmekel тоже выглядит хорошо. Я думаю, что я мог бы использовать object_hook с jsonpickle set_encoder_options () (недокументированный!), Но это заняло бы больше кода, чем базовый модуль json. Я хотел бы быть доказанным неправым!
LS
@LS, если у вас нет контроля над вводом, который действительно задан OP, 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 по умолчанию, таких как строки, целые числа, списки, даты и т. Д. Однако в jsonslib потребуются подсказки типов для пользовательских типов.

RH
источник
7

Если вы используете python 3.6+, вы можете использовать marshmallow-dataclass . Вопреки всем решениям, перечисленным выше, это и простой, и безопасный тип:

from marshmallow_dataclass import dataclass

@dataclass
class User:
    name: str

user, err = User.Schema().load({"name": "Ramirez"})
lovasoa
источник
TypeError: make_data_class() got an unexpected keyword argument 'many'
Джон
@JOhn: Вы должны открыть вопрос с воспроизводимым контрольным примером в github.com/lovasoa/marshmallow_dataclass/issues
lovasoa
6

Улучшение Lovasoa очень хороший ответ.

Если вы используете 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

От струны Джсон до объекта:

    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 в dataclass, и все готово.
Йозеф Корбель
5

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

В вашем случае, я полагаю, вы хотите преобразовать словарь (полученный с помощью json.loads) в сложный объект response.education ; response.name, с вложенной структурой response.education.idи т. Д. Так что именно для этого и создана эта структура. Документация пока не очень хорошая, но с ее помощью any2any.simple.MappingToObjectвы сможете сделать это очень легко. Пожалуйста, спросите, нужна ли вам помощь.

sebpiq
источник
Sebpiq, установил any2any и у меня возникли проблемы с пониманием предполагаемой последовательности вызовов методов. Не могли бы вы привести простой пример преобразования словаря в объект Python со свойством для каждого ключа?
Сансхо
Привет @sansjoe! Если вы установили его из pypi, версия полностью устарела, я сделал полный рефакторинг несколько недель назад. Вы должны использовать версию github (мне нужно сделать правильный релиз!)
sebpiq
Я установил его из pypy, потому что github сказал установить его из pypy. Кроме того , вы сказали PyPy были из финиковых месяцев назад .. Это не работа :( Я подал отчет об ошибке Тхо! Github.com/sebpiq/any2any/issues/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)
enazar
источник
2

Если немного расширить ответ DS, то если вам нужно, чтобы объект был изменяемым (это не namedtuple), вы можете использовать библиотеку recordclass вместо 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()))

Модифицированный объект может быть легко преобразован обратно в json с помощью simplejson :

x.name = "John Doe"
new_json = simplejson.dumps(x)
BeneStr
источник
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()
Funkatic
источник
Это также больше похоже на языковые способы JVM, чтобы сделать это.
Джавадба
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
#  }
#}
Шрирам V
источник
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 ().
Этот подход является общим, просто требуется расширение класса (во втором примере).
Обратите внимание, что я просто делаю это с файлами, но легко изменить поведение на свой вкус.

Однако это кодек.

Приложив немного больше работы, вы можете создать свой класс другими способами. Я предполагаю, что конструктор по умолчанию его создает, а затем обновляю класс 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)

редактировать

Проведя дополнительные исследования, я нашел способ обобщения без необходимости вызова метода регистра 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]

Для общего, перспективного решения.

Gulzar
источник
-5

Используйте jsonмодуль ( новый в Python 2.6 ) или simplejsonмодуль, который почти всегда установлен.

Крис Морган
источник
2
Привет, спасибо за ответ. Не могли бы вы опубликовать пример того, как декодировать JSON и затем получить доступ к этим данным?
Саи Кришна
Эй, теперь у тебя есть смысл, но почему-то я предпочитаю обходиться без знания, а затем перепроектировать его: D.
Саи Кришна
1
@ Зак: есть примеры прямо в верхней части документов, на которые я ссылаюсь.
Крис Морган