我想将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对象,是不是会更好?
数据类向导是一种现代的选项,可以类似地为您工作。它支持自动键大小写转换,如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)
这似乎是一个XY问题(问A实际问题在哪里B)。
问题的根源是:如何有效地引用/修改深嵌套的JSON结构,而不必做obj['foo']['bar'][42]['quux'],这带来了键入挑战,代码膨胀问题,可读性问题和错误捕获问题?
使用抢
from glom import glom
# Basic deep get
data = {'a': {'b': {'c': 'd'}}}
print(glom(data, 'a.b.c'))
它还将处理列表项:
我已经对一个简单的实现进行了基准测试:
def extract(J, levels):
# Twice as fast as using glom
for level in levels.split('.'):
J = J[int(level) if level.isnumeric() else level]
return J
... 并且在复杂的JSON对象上返回0.14ms,而朴素的impl则返回0.06ms。
它还可以处理复杂的查询,例如取出所有foo.bar.记录,其中.name == 'Joe Bloggs'
编辑:
另一种性能方法是递归地使用覆盖__getitem__和__getattr__的类:
class Ob:
def __init__(self, J):
self.J = J
def __getitem__(self, index):
return Ob(self.J[index])
def __getattr__(self, attr):
value = self.J.get(attr, None)
return Ob(value) if type(value) in (list, dict) else value
现在你可以做:
ob = Ob(J)
# if you're fetching a final raw value (not list/dict
ob.foo.bar[42].quux.leaf
# for intermediate values
ob.foo.bar[42].quux.J
这一基准测试也出奇地好。与我之前的天真冲动相当。如果有人能找到一种方法来整理非叶查询的访问,请留下评论!
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 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 orjson # faster then json =)
from typing import NamedTuple
_j = '{"name":"Иван","age":37,"mother":{"name":"Ольга","age":58},"children":["Маша","Игорь","Таня"],"married": true,' \
'"dog":null} '
class PersonNameAge(NamedTuple):
name: str
age: int
class UserInfo(NamedTuple):
name: str
age: int
mother: PersonNameAge
children: list
married: bool
dog: str
j = orjson.loads(_j)
u = UserInfo(**j)
print(u.name, u.age, u.mother, u.children, u.married, u.dog)
>>> Ivan 37 {'name': 'Olga', 'age': 58} ['Mary', 'Igor', 'Jane'] True None