我想将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对象,是不是会更好?
如果你使用的是Python 3.5+,你可以使用json来序列化和反序列化到普通的旧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 lib需要自定义类型的类型提示。
这里有一个快速而肮脏的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()
这是我的办法。
特性
支持类型提示
如果缺少键则引发错误。
跳过数据中的额外值
import typing
class User:
name: str
age: int
def __init__(self, data: dict):
for k, _ in typing.get_type_hints(self).items():
setattr(self, k, data[k])
data = {
"name": "Susan",
"age": 18
}
user = User(data)
print(user.name, user.age)
# Output: Susan 18
这不是代码高尔夫,但这里是我使用类型的最短技巧。SimpleNamespace作为JSON对象的容器。
与namedtuple解决方案相比,它是:
可能更快/更小,因为它没有为每个对象创建一个类
更短的
没有重命名选项,对于不是有效标识符的键可能有相同的限制(在幕后使用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)
修改@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字段名。
查看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']
就像一本普通的字典。