我想将JSON数据转换为Python对象。

我从Facebook API收到JSON数据对象,我想将其存储在数据库中。

我的当前视图在Django (Python)(请求。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 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)

其他回答

在寻找解决方案时,我偶然发现了这个博客: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)

如果你正在寻找将JSON或任何复杂字典的类型安全反序列化到python类中,我强烈推荐python 3.7+的pydantic。它不仅有一个简洁的API(不需要编写“helper”样板),可以与Python数据类集成,而且具有复杂和嵌套数据结构的静态和运行时类型验证。

使用示例:

from pydantic import BaseModel
from datetime import datetime

class Item(BaseModel):
    field1: str | int           # union
    field2: int | None = None   # optional
    field3: str = 'default'     # default values

class User(BaseModel):
    name: str | None = None
    username: str
    created: datetime           # default type converters
    items: list[Item] = []      # nested complex types

data = {
    'name': 'Jane Doe',
    'username': 'user1',
    'created': '2020-12-31T23:59:00+10:00',
    'items': [
        {'field1': 1, 'field2': 2},
        {'field1': 'b'},
        {'field1': 'c', 'field3': 'override'}
    ]
}

user: User = User(**data)

要了解更多细节和特性,请查看文档中的pydantic的rational部分。

我已经编写了一个名为any2any的小型(反)序列化框架,它可以帮助在两种Python类型之间进行复杂的转换。

在您的情况下,我猜您想从字典(通过json.loads获得)转换为复杂的对象response.education;Response.name,具有嵌套结构response.education.id,等等… 这就是这个框架的用途。文档还不是很好,但是通过使用any2any.simple。MappingToObject,你应该可以很容易地做到。如果需要帮助,请询问。

class SimpleClass:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            if type(v) is dict:
                setattr(self, k, SimpleClass(**v))
            else:
                setattr(self, k, v)


json_dict = {'name': 'jane doe', 'username': 'jane', 'test': {'foo': 1}}

class_instance = SimpleClass(**json_dict)

print(class_instance.name, class_instance.test.foo)
print(vars(class_instance))

查看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']

就像一本普通的字典。