我想将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.7,我发现下面的代码非常简单有效。在本例中,将JSON从文件加载到字典中:
class Characteristic:
def __init__(self, characteristicName, characteristicUUID):
self.characteristicName = characteristicName
self.characteristicUUID = characteristicUUID
class Service:
def __init__(self, serviceName, serviceUUID, characteristics):
self.serviceName = serviceName
self.serviceUUID = serviceUUID
self.characteristics = characteristics
class Definitions:
def __init__(self, services):
self.services = []
for service in services:
self.services.append(Service(**service))
def main():
parser = argparse.ArgumentParser(
prog="BLEStructureGenerator",
description="Taking in a JSON input file which lists all of the services, "
"characteristics and encoded properties. The encoding takes in "
"another optional template services and/or characteristics "
"file where the JSON file contents are applied to the templates.",
epilog="Copyright Brown & Watson International"
)
parser.add_argument('definitionfile',
type=argparse.FileType('r', encoding='UTF-8'),
help="JSON file which contains the list of characteristics and "
"services in the required format")
parser.add_argument('-s', '--services',
type=argparse.FileType('r', encoding='UTF-8'),
help="Services template file to be used for each service in the "
"JSON file list")
parser.add_argument('-c', '--characteristics',
type=argparse.FileType('r', encoding='UTF-8'),
help="Characteristics template file to be used for each service in the "
"JSON file list")
args = parser.parse_args()
definition_dict = json.load(args.definitionfile)
definitions = Definitions(**definition_dict)
更新
在Python3中,你可以使用SimpleNamespace和object_hook在一行中完成:
import json
from types import SimpleNamespace
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: SimpleNamespace(**d))
print(x.name, x.hometown.name, x.hometown.id)
旧答案(Python2)
在Python2中,你可以使用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的rename参数。
在寻找解决方案时,我偶然发现了这个博客: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)
已经有多种可行的答案,但有一些由个人制作的小型库可以满足大多数用户的需求。
json2object就是一个例子。给定一个已定义的类,它将json数据反序列化到您的自定义模型,包括自定义属性和子对象。
它的使用非常简单。一个来自图书馆wiki的例子:
从json2object导入jsontoobject作为Jo
类学生:
def __init__(自我):
自我。firstName =无
自我。lastName = None
自我。courses =[课程(")]
类课程:
定义__init__(self, name):
Self.name = name
数据= " '{
“firstName”:“詹姆斯”,
“姓”:“债券”,
“课程”:[{
“名称”:“战斗”},
{
“名称”:“射击”}
]
}
“‘
model = Student()
Result = jo.deserialize(数据,模型)
print (result.courses [0] . name)