我想将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对象,是不是会更好?


当前回答

这不是一个很难的事情,我看到上面的答案,他们中的大多数在“列表”中有一个性能问题

这段代码比上面的代码快得多

import json 

class jsonify:
    def __init__(self, data):
        self.jsonify = data

    def __getattr__(self, attr):
        value = self.jsonify.get(attr)
        if isinstance(value, (list, dict)):
            return jsonify(value)
        return value

    def __getitem__(self, index):
        value = self.jsonify[index]
        if isinstance(value, (list, dict)):
            return jsonify(value)
        return value

    def __setitem__(self, index, value):
        self.jsonify[index] = value

    def __delattr__(self, index):
        self.jsonify.pop(index)

    def __delitem__(self, index):
        self.jsonify.pop(index)

    def __repr__(self):
        return json.dumps(self.jsonify, indent=2, default=lambda x: str(x))

exmaple

response = jsonify(
    {
        'test': {
            'test1': [{'ok': 1}]
        }
    }
)
response.test -> jsonify({'test1': [{'ok': 1}]})
response.test.test1 -> jsonify([{'ok': 1}])
response.test.test1[0] -> jsonify({'ok': 1})
response.test.test1[0].ok -> int(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)

数据类向导是一种现代的选项,可以类似地为您工作。它支持自动键大小写转换,如camelCase或TitleCase,这两者在API响应中都很常见。

当将实例转储到dict/JSON时,默认的键转换是camelCase,但这可以很容易地使用主数据类上提供的Meta配置来覆盖。

https://pypi.org/project/dataclass-wizard/

from dataclasses import dataclass

from dataclass_wizard import fromdict, asdict


@dataclass
class User:
    name: str
    age: int
    is_active: bool


data = {
    'name': 'John',
    'age': 30,
    'isActive': True,
}

user = fromdict(User, data)
assert user == User(name='John', age=30, is_active=True)

json_dict = asdict(user)
assert json_dict == {'name': 'John', 'age': 30, 'isActive': True}

设置元配置的例子,当序列化为dict/JSON时,将字段转换为lisp-case:

DumpMeta(key_transform='LISP').bind_to(User)

这不是一个很难的事情,我看到上面的答案,他们中的大多数在“列表”中有一个性能问题

这段代码比上面的代码快得多

import json 

class jsonify:
    def __init__(self, data):
        self.jsonify = data

    def __getattr__(self, attr):
        value = self.jsonify.get(attr)
        if isinstance(value, (list, dict)):
            return jsonify(value)
        return value

    def __getitem__(self, index):
        value = self.jsonify[index]
        if isinstance(value, (list, dict)):
            return jsonify(value)
        return value

    def __setitem__(self, index, value):
        self.jsonify[index] = value

    def __delattr__(self, index):
        self.jsonify.pop(index)

    def __delitem__(self, index):
        self.jsonify.pop(index)

    def __repr__(self):
        return json.dumps(self.jsonify, indent=2, default=lambda x: str(x))

exmaple

response = jsonify(
    {
        'test': {
            'test1': [{'ok': 1}]
        }
    }
)
response.test -> jsonify({'test1': [{'ok': 1}]})
response.test.test1 -> jsonify([{'ok': 1}])
response.test.test1[0] -> jsonify({'ok': 1})
response.test.test1[0].ok -> int(1)

扩展一下DS的答案,如果你需要对象是可变的(而namedtuple不是),你可以使用记录类库而不是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()))

修改后的对象可以使用simplejson很容易地转换回json:

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

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

就像一本普通的字典。