问题:如何将JSON数据转换为Python对象
我想使用Python将JSON数据转换成Python对象。
我从Facebook API接收了JSON数据对象,我想将其存储在数据库中。
我当前在Django(Python)中的视图(request.POST
包含JSON):
response = request.POST
user = FbApiUser(user_id = response['id'])
user.name = response['name']
user.username = response['username']
user.save()
这可以正常工作,但是如何处理复杂的JSON数据对象?
如果我能以某种方式将这个JSON对象转换为Python对象以便于使用,会不会更好呢?
回答 0
您可以使用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
parameter。
回答 1
检查出标题为专业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']
就像普通字典一样。
回答 2
这不是代码编程,但这是我最短的技巧,types.SimpleNamespace
用作JSON对象的容器。
与领先的namedtuple
解决方案相比,它是:
- 可能更快/更小,因为它不会为每个对象创建一个类
- 短一点
- 没有
rename
选项,并且对于无效标识符的密钥可能有相同的限制(在幕后使用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)
回答 3
您可以尝试以下方法:
class User(object):
def __init__(self, name, username, *args, **kwargs):
self.name = name
self.username = username
import json
j = json.loads(your_json)
u = User(**j)
只需创建一个新的对象,然后将参数作为映射传递即可。
回答 4
这是一种快速而肮脏的JSON泡菜替代品
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()
回答 5
对于复杂的对象,可以使用JSON Pickle
用于将任意对象图序列化为JSON的Python库。它几乎可以使用任何Python对象并将该对象转换为JSON。此外,它可以将对象重新构造回Python。
回答 6
如果您使用的是Python 3.5+,则可以用于jsons
序列化和反序列化为普通的旧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将需要自定义类型的类型提示。
回答 7
如果您使用的是Python 3.6+,则可以使用marshmallow-dataclass。与上面列出的所有解决方案相反,它既简单又可安全输入:
from marshmallow_dataclass import dataclass
@dataclass
class User:
name: str
user, err = User.Schema().load({"name": "Ramirez"})
回答 8
我编写了一个名为any2any的小型(反序列化)框架,该有助于在两种Python类型之间进行复杂的转换。
在您的情况下,我想您想将字典(由获取json.loads
)转换为response.education ; response.name
具有嵌套结构response.education.id
等的复杂对象……所以这正是该框架的目的。该文档尚不完善,但是通过使用any2any.simple.MappingToObject
,您应该可以很轻松地做到这一点。请询问是否需要帮助。
回答 9
改善lovasoa的很好答案。
如果您使用的是python 3.6+,则可以使用:pip install marshmallow-enum
和pip install marshmallow-dataclass
它简单且类型安全。
您可以使用string-json转换类,反之亦然:
从对象到字符串Json:
from marshmallow_dataclass import dataclass
user = User("Danilo","50","RedBull",15,OrderStatus.CREATED)
user_json = User.Schema().dumps(user)
user_json_str = user_json.data
从String Json到Object:
json_str = '{"name":"Danilo", "orderId":"50", "productName":"RedBull", "quantity":15, "status":"Created"}'
user, err = User.Schema().loads(json_str)
print(user,flush=True)
类定义:
class OrderStatus(Enum):
CREATED = 'Created'
PENDING = 'Pending'
CONFIRMED = 'Confirmed'
FAILED = 'Failed'
@dataclass
class User:
def __init__(self, name, orderId, productName, quantity, status):
self.name = name
self.orderId = orderId
self.productName = productName
self.quantity = quantity
self.status = status
name: str
orderId: str
productName: str
quantity: int
status: OrderStatus
回答 10
由于没有人像我一样提供答案,因此我将在此处发布。
这是一个强大的类,它可以很容易地来回转换JSON之间str
和dict
我从复制我的回答另一个问题:
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)
回答 11
稍微修改@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字段名称。
回答 12
在寻找解决方案时,我偶然发现了此博客文章: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)
回答 13
稍微扩展一下DS的答案,如果您需要对象可变(不包括namedtuple),则可以使用recordclass库而不是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)
回答 14
如果您使用的是Python 3.6或更高版本,则可以看看squema-一种用于静态类型数据结构的轻量级模块。它使您的代码易于阅读,同时无需任何额外工作即可提供简单的数据验证,转换和序列化。您可以将其视为命名元组和数据类的更复杂,更自以为是的替代方案。使用方法如下:
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()
回答 15
我正在寻找一种可与recordclass.RecordClass
,支持嵌套对象同时适用于json序列化和json反序列化的解决方案。
扩展DS的答案,并扩展BeneStr的解决方案,我想到了以下似乎可行的方法:
码:
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
# }
#}
回答 16
此处给出的答案未返回正确的对象类型,因此我在下面创建了这些方法。如果您尝试将更多字段添加到给定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)
回答 17
Python3.x
我所能达到的最好的方法就是这个。
注意,此代码也对待set()。
这种方法是通用的,只需要扩展类(在第二个示例中)。
请注意,我只是对文件进行处理,但是可以根据自己的喜好修改行为。
但是,这是CoDec。
通过更多的工作,您可以用其他方式构造您的类。我假定使用默认的构造函数来实例化它,然后更新类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)
编辑
通过更多的研究,我找到了一种使用元类进行泛化而无需SUPERCLASS寄存器方法调用的方法。
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
回答 18
您可以使用
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]
一个通用的,面向未来的解决方案。
回答 19
使用simplejson
几乎始终安装的模块。