如何将JSON数据转换为Python对象
我想将JSON数据转换成Python对象 我从Facebook API接收JSON数据对象,我想将其存储在数据库中 我在Django(Python)中的当前视图(如何将JSON数据转换为Python对象,python,json,django,Python,Json,Django,我想将JSON数据转换成Python对象 我从Facebook API接收JSON数据对象,我想将其存储在数据库中 我在Django(Python)中的当前视图(request.POST包含JSON): 这很好,但是如何处理复杂的JSON数据对象呢 如果我能以某种方式将这个JSON对象转换成Python对象以便于使用,不是更好吗 使用()或几乎总是安装的simplejson模块。对于复杂对象,可以使用 Python库,用于将任意对象图序列化为JSON。 它可以接受几乎任何Python对象并将该
request.POST
包含JSON):
- 这很好,但是如何处理复杂的JSON数据对象呢
- 如果我能以某种方式将这个JSON对象转换成Python对象以便于使用,不是更好吗
simplejson
模块。对于复杂对象,可以使用
Python库,用于将任意对象图序列化为JSON。
它可以接受几乎任何Python对象并将该对象转换为JSON。
此外,它还可以将对象重新构造回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'>
就像普通字典一样。我编写了一个名为的小型(反)序列化框架,它可以帮助在两种Python类型之间进行复杂的转换
在您的例子中,我猜您希望将字典(通过json.loads
)转换为复杂对象response.education;response.name
,具有嵌套结构response.education.id
等。。。
这就是这个框架的目的。文档还不是很好,但是通过使用
any2any.simple.MappingToObject
,您应该能够非常轻松地完成这项工作。请询问您是否需要帮助。更新
使用Python3,您可以使用SimpleNamespace
和object\u 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)
旧答案(蟒蛇2)
在Python2中,您可以使用namedtuple
和object\u hook
在一行中完成这项工作(但对于许多嵌套对象来说速度非常慢):
或者,为了方便地重复使用:
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
。您可以尝试以下方法:
class User(object):
def __init__(self, name, username):
self.name = name
self.username = username
import json
j = json.loads(your_json)
u = User(**j)
只需创建一个新对象,并将参数作为贴图传递
注意:它不适用于嵌套类。这里有一个快速而肮脏的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()
这不是代码高尔夫,但这里是我的最短技巧,将其用作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\u first\u item”不是有效的python字段名。Python3.x
用我的知识,我能找到的最好的办法就是这个。请注意,此代码也处理set()。
这种方法是通用的,只需要类的扩展(在第二个示例中)。
请注意,我只是对文件执行此操作,但很容易根据您的喜好修改行为 然而,这是一个编解码器 只要多做一点工作,您就可以用其他方式构建类。 我假设一个默认构造函数来实例它,然后更新类dict
import json
import collections
class JsonClassSerializable(json.JSONEncoder):
REGISTERED_CLASS = {}
def register(ctype):
JsonClassSerializable.REGISTERED_CLASS[ctype.__name__] = ctype
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in self.REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = self.REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
JsonClassSerializable.register(C)
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
JsonClassSerializable.register(B)
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
JsonClassSerializable.register(A)
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
print(b.b)
print(b.c.a)
编辑 通过更多的研究,我发现了一种方法,可以使用元类来进行泛化,而不需要调用超类register方法
import json
import collections
REGISTERED_CLASS = {}
class MetaSerializable(type):
def __call__(cls, *args, **kwargs):
if cls.__name__ not in REGISTERED_CLASS:
REGISTERED_CLASS[cls.__name__] = cls
return super(MetaSerializable, cls).__call__(*args, **kwargs)
class JsonClassSerializable(json.JSONEncoder, metaclass=MetaSerializable):
def default(self, obj):
if isinstance(obj, collections.Set):
return dict(_set_object=list(obj))
if isinstance(obj, JsonClassSerializable):
jclass = {}
jclass["name"] = type(obj).__name__
jclass["dict"] = obj.__dict__
return dict(_class_object=jclass)
else:
return json.JSONEncoder.default(self, obj)
def json_to_class(self, dct):
if '_set_object' in dct:
return set(dct['_set_object'])
elif '_class_object' in dct:
cclass = dct['_class_object']
cclass_name = cclass["name"]
if cclass_name not in REGISTERED_CLASS:
raise RuntimeError(
"Class {} not registered in JSON Parser"
.format(cclass["name"])
)
instance = REGISTERED_CLASS[cclass_name]()
instance.__dict__ = cclass["dict"]
return instance
return dct
def encode_(self, file):
with open(file, 'w') as outfile:
json.dump(
self.__dict__, outfile,
cls=JsonClassSerializable,
indent=4,
sort_keys=True
)
def decode_(self, file):
try:
with open(file, 'r') as infile:
self.__dict__ = json.load(
infile,
object_hook=self.json_to_class
)
except FileNotFoundError:
print("Persistence load failed "
"'{}' do not exists".format(file)
)
class C(JsonClassSerializable):
def __init__(self):
self.mill = "s"
class B(JsonClassSerializable):
def __init__(self):
self.a = 1230
self.c = C()
class A(JsonClassSerializable):
def __init__(self):
self.a = 1
self.b = {1, 2}
self.c = B()
A().encode_("test")
b = A()
b.decode_("test")
print(b.a)
# 1
print(b.b)
# {1, 2}
print(b.c.a)
# 1230
print(b.c.c.mill)
# s
在寻找解决方案的过程中,我偶然发现了以下博文: 它使用与前面答案中所述相同的技术,但使用了装饰器。 我发现另一件有用的事情是,它在反序列化结束时返回一个类型化对象
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)
如果您使用的是Python 3.5+,则可以使用来序列化和反序列化旧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将需要自定义类型的类型提示。如果您需要对象是可变的(名为tuple的对象不是可变的),请在DS的答案上稍微扩展一下,您可以使用库而不是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()))
然后,可以使用以下方法非常轻松地将修改后的对象转换回json:
因为没有人提供了一个和我完全一样的答案,所以我将把它贴在这里 它是一个健壮的类,可以轻松地在json
str
和dict
之间来回转换,我从中复制了:
如果您使用的是python 3.6+,那么可以使用。与上面列出的所有解决方案相反,它既简单又类型安全:
从棉花糖\u数据类导入数据类
@数据类
类用户:
姓名:str
user=user.Schema().load({“name”:“Ramirez”})
改进lovasoa非常好的答案
如果您使用的是python 3.6+,则可以使用:
pip安装棉花糖枚举
和pip安装棉花糖数据类
它简单,类型安全
您可以将类转换为字符串json,反之亦然:
从对象到字符串Json:
从棉花糖\u数据类导入数据类
用户=用户(“Danilo”,“50”,“RedBull”,15,OrderStatus.CREATED)
user_json=user.Schema().dumps(用户)
user_json_str=user_json.data
从字符串Json到对象:
json_str='{“name”:“Danilo”,“orderId”:“50”,“productName”:“RedBull”,“quantity”:15,“status”:“Created”}
user,err=user.Schema().load(json_str)
打印(用户,刷新=真)
类别定义:
类订单状态(枚举):
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()
user = FbApiUser.from_json(response)
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()))
x.name = "John Doe"
new_json = simplejson.dumps(x)
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)
from uuid import UUID
from squema import Squema
class FbApiUser(Squema):
id: UUID
age: int
name: str
def save(self):
pass
user = FbApiUser(**json.loads(response))
user.save()
x = Map(json.loads(response))
x.__class__ = MyClass
class Map(dict):
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
if isinstance(arg, dict):
for k, v in arg.iteritems():
self[k] = v
if isinstance(v, dict):
self[k] = Map(v)
if kwargs:
# for python 3 use kwargs.items()
for k, v in kwargs.iteritems():
self[k] = v
if isinstance(v, dict):
self[k] = Map(v)
def __getattr__(self, attr):
return self.get(attr)
def __setattr__(self, key, value):
self.__setitem__(key, value)
def __setitem__(self, key, value):
super(Map, self).__setitem__(key, value)
self.__dict__.update({key: value})
def __delattr__(self, item):
self.__delitem__(item)
def __delitem__(self, key):
super(Map, self).__delitem__(key)
del self.__dict__[key]
import json
import recordclass
class NestedRec(recordclass.RecordClass):
a : int = 0
b : int = 0
class ExampleRec(recordclass.RecordClass):
x : int = None
y : int = None
nested : NestedRec = NestedRec()
class JsonSerializer:
@staticmethod
def dumps(obj, ensure_ascii=True, indent=None, sort_keys=False):
return json.dumps(obj, default=JsonSerializer.__obj_to_dict, ensure_ascii=ensure_ascii, indent=indent, sort_keys=sort_keys)
@staticmethod
def loads(s, klass):
return JsonSerializer.__dict_to_obj(klass, json.loads(s))
@staticmethod
def __obj_to_dict(obj):
if hasattr(obj, "_asdict"):
return obj._asdict()
else:
return json.JSONEncoder().default(obj)
@staticmethod
def __dict_to_obj(klass, s_dict):
kwargs = {
key : JsonSerializer.__dict_to_obj(cls, s_dict[key]) if hasattr(cls,'_asdict') else s_dict[key] \
for key,cls in klass.__annotations__.items() \
if s_dict is not None and key in s_dict
}
return klass(**kwargs)
example_0 = ExampleRec(x = 10, y = 20, nested = NestedRec( a = 30, b = 40 ) )
#Serialize to JSON
json_str = JsonSerializer.dumps(example_0)
print(json_str)
#{
# "x": 10,
# "y": 20,
# "nested": {
# "a": 30,
# "b": 40
# }
#}
# Deserialize from JSON
example_1 = JsonSerializer.loads(json_str, ExampleRec)
example_1.x += 1
example_1.y += 1
example_1.nested.a += 1
example_1.nested.b += 1
json_str = JsonSerializer.dumps(example_1)
print(json_str)
#{
# "x": 11,
# "y": 21,
# "nested": {
# "a": 31,
# "b": 41
# }
#}
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)