我有以下字符串
{"action":"print","method":"onData","data":"Madan Mohan"}
我想反序列化一个类的对象
class payload
string action
string method
string data
我正在使用python 2.6和2.7
Answers:
>>> j = '{"action": "print", "method": "onData", "data": "Madan Mohan"}'
>>> import json
>>>
>>> class Payload(object):
... def __init__(self, j):
... self.__dict__ = json.loads(j)
...
>>> p = Payload(j)
>>>
>>> p.action
'print'
>>> p.method
'onData'
>>> p.data
'Madan Mohan'
__dict__
。如果现有成员不在json字符串中,则会丢失它们。如果您需要这样做,请使用self.__dict__.update(otherdict)
要详细说明萨米的答案:
从文档:
class Payload(object):
def __init__(self, action, method, data):
self.action = action
self.method = method
self.data = data
import json
def as_payload(dct):
return Payload(dct['action'], dct['method'], dct['data'])
payload = json.loads(message, object_hook = as_payload)
我反对
.__dict__
解决方案是,虽然它可以完成工作并且简洁明了,但Payload类却变得完全通用-它没有记录其字段。
例如,如果有效载荷消息具有意外的格式,则在创建有效载荷时不会抛出任何错误,而不会在创建有效载荷时抛出键未找到的错误。
return Payload(dct['action'], dct['method'], dct['data'])
与return Payload(dct.get('action', None), dct.get('method', None), dct.get('data', None))
允许一些字段是可选的。
如果您正在使用Python 3.6中的类型提示,则可以这样做:
def from_json(data, cls):
annotations: dict = cls.__annotations__ if hasattr(cls, '__annotations__') else None
if issubclass(cls, List):
list_type = cls.__args__[0]
instance: list = list()
for value in data:
instance.append(from_json(value, list_type))
return instance
elif issubclass(cls, Dict):
key_type = cls.__args__[0]
val_type = cls.__args__[1]
instance: dict = dict()
for key, value in data.items():
instance.update(from_json(key, key_type), from_json(value, val_type))
return instance
else:
instance : cls = cls()
for name, value in data.items():
field_type = annotations.get(name)
if inspect.isclass(field_type) and isinstance(value, (dict, tuple, list, set, frozenset)):
setattr(instance, name, from_json(value, field_type))
else:
setattr(instance, name, value)
return instance
然后,您可以像这样实例化类型化的对象:
class Bar:
value : int
class Foo:
x : int
bar : List[Bar]
obj : Foo = from_json(json.loads('{"x": 123, "bar":[{"value": 3}, {"value": 2}, {"value": 1}]}'), Foo)
print(obj.x)
print(obj.bar[2].value)
尽管此语法要求使用Python 3.6,但并不涵盖所有情况-例如,支持键入。任何...但是至少它不会污染需要使用额外的init / tojson方法反序列化的类。
如果要保存代码行并保留最灵活的解决方案,我们可以将json字符串反序列化为动态对象:
p = lambda:None
p.__dict__ = json.loads('{"action": "print", "method": "onData", "data": "Madan Mohan"}')
>>>> p.action
输出:u'print'
>>>> p.method
输出:u'onData'
p.__dict__ = json.loads('{"parent":{"child":{"name":"henry"}}}')
仍然需要像字典那样访问孩子。
我以为我解决了这个“挑战”就不知所措了。我遇到以下问题:
我发现一个叫做的库被jsonpickle
证明真的很有用。
安装:
pip install jsonpickle
这是一个将嵌套对象写入文件的代码示例:
import jsonpickle
class SubObject:
def __init__(self, sub_name, sub_age):
self.sub_name = sub_name
self.sub_age = sub_age
class TestClass:
def __init__(self, name, age, sub_object):
self.name = name
self.age = age
self.sub_object = sub_object
john_junior = SubObject("John jr.", 2)
john = TestClass("John", 21, john_junior)
file_name = 'JohnWithSon' + '.json'
john_string = jsonpickle.encode(john)
with open(file_name, 'w') as fp:
fp.write(john_string)
john_from_file = open(file_name).read()
test_class_2 = jsonpickle.decode(john_from_file)
print(test_class_2.name)
print(test_class_2.age)
print(test_class_2.sub_object.sub_name)
输出:
John
21
John jr.
网站:http://jsonpickle.github.io/
希望它可以节省您的时间(和头发)。
我喜欢对字段进行一些检查,例如,这样您就可以捕获错误(例如,当您获得无效的json时),或者不是您所期望的json,因此我使用namedtuples:
from collections import namedtuple
payload = namedtuple('payload', ['action', 'method', 'data'])
def deserialize_payload(json):
kwargs = dict([(field, json[field]) for field in payload._fields])
return payload(**kwargs)
当您解析的json与您要解析的内容不匹配时,这将给您带来很好的错误
>>> json = {"action":"print","method":"onData","data":"Madan Mohan"}
>>> deserialize_payload(json)
payload(action='print', method='onData', data='Madan Mohan')
>>> badjson = {"error":"404","info":"page not found"}
>>> deserialize_payload(badjson)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in deserialize_payload
KeyError: 'action'
如果您想解析嵌套关系,例如'{"parent":{"child":{"name":"henry"}}}'
仍然可以使用namedtuple,甚至可以重用的函数
Person = namedtuple("Person", ['parent'])
Parent = namedtuple("Parent", ['child'])
Child = namedtuple('Child', ['name'])
def deserialize_json_to_namedtuple(json, namedtuple):
return namedtuple(**dict([(field, json[field]) for field in namedtuple._fields]))
def deserialize_person(json):
json['parent']['child'] = deserialize_json_to_namedtuple(json['parent']['child'], Child)
json['parent'] = deserialize_json_to_namedtuple(json['parent'], Parent)
person = deserialize_json_to_namedtuple(json, Person)
return person
给你
>>> deserialize_person({"parent":{"child":{"name":"henry"}}})
Person(parent=Parent(child=Child(name='henry')))
>>> deserialize_person({"error":"404","info":"page not found"})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in deserialize_person
KeyError: 'parent'
在最新版本的python中,您可以使用marshmallow-dataclass:
from marshmallow_dataclass import dataclass
@dataclass
class Payload
action:str
method:str
data:str
Payload.Schema().load({"action":"print","method":"onData","data":"Madan Mohan"})
您可以为对象创建专门的编码器:http : //docs.python.org/2/library/json.html
import json
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, complex):
return {"real": obj.real,
"imag": obj.imag,
"__class__": "complex"}
return json.JSONEncoder.default(self, obj)
print json.dumps(2 + 1j, cls=ComplexEncoder)
另一种方法是简单地将json字符串作为字典传递给对象的构造函数。例如,您的对象是:
class Payload(object):
def __init__(self, action, method, data, *args, **kwargs):
self.action = action
self.method = method
self.data = data
以下两行python代码将构建它:
j = json.loads(yourJsonString)
payload = Payload(**j)
基本上,我们首先从json字符串创建一个通用json对象。然后,我们将通用json对象作为字典传递给Payload类的构造函数。Payload类的构造函数将dict解释为关键字参数,并设置所有适当的字段。
有多种方法可以将json字符串反序列化为对象。所有上述方法都是可以接受的,但是我建议使用一个库来防止重复的密钥问题或对嵌套对象进行序列化/反序列化。
Pykson是Python的JSON序列化器和反序列化器,可以帮助您实现。只需将Payload类模型定义为JsonObject,然后使用Pykson将json字符串转换为对象。
from pykson import Pykson, JsonObject, StringField
class Payload(pykson.JsonObject):
action = StringField()
method = StringField()
data = StringField()
json_text = '{"action":"print","method":"onData","data":"Madan Mohan"}'
payload = Pykson.from_json(json_text, Payload)
大韩民国是python 3.6+项目越来越受欢迎的库。它主要使用类型提示进行数据验证和设置管理。
使用不同类型的基本示例:
from pydantic import BaseModel
class ClassicBar(BaseModel):
count_drinks: int
is_open: bool
data = {'count_drinks': '226', 'is_open': 'False'}
cb = ClassicBar(**data)
>>> cb
ClassicBar(count_drinks=226, is_open=False)
我喜欢lib的是,您可以免费获得许多好东西,例如
>>> cb.json()
'{"count_drinks": 226, "is_open": false}'
>>> cb.dict()
{'count_drinks': 226, 'is_open': False}
尽管Alex的答案为我们提供了一种好的技术,但是当我们嵌套对象时,他给出的实现会遇到问题。
class more_info
string status
class payload
string action
string method
string data
class more_info
使用以下代码:
def as_more_info(dct):
return MoreInfo(dct['status'])
def as_payload(dct):
return Payload(dct['action'], dct['method'], dct['data'], as_more_info(dct['more_info']))
payload = json.loads(message, object_hook = as_payload)
payload.more_info
也将被视为实例,payload
这将导致解析错误。
从官方文档:
object_hook是一个可选函数,它将被解码的任何对象文字(字典)的结果调用。将使用object_hook的返回值代替dict。
因此,我宁愿提出以下解决方案:
class MoreInfo(object):
def __init__(self, status):
self.status = status
@staticmethod
def fromJson(mapping):
if mapping is None:
return None
return MoreInfo(
mapping.get('status')
)
class Payload(object):
def __init__(self, action, method, data, more_info):
self.action = action
self.method = method
self.data = data
self.more_info = more_info
@staticmethod
def fromJson(mapping):
if mapping is None:
return None
return Payload(
mapping.get('action'),
mapping.get('method'),
mapping.get('data'),
MoreInfo.fromJson(mapping.get('more_info'))
)
import json
def toJson(obj, **kwargs):
return json.dumps(obj, default=lambda j: j.__dict__, **kwargs)
def fromJson(msg, cls, **kwargs):
return cls.fromJson(json.loads(msg, **kwargs))
info = MoreInfo('ok')
payload = Payload('print', 'onData', 'better_solution', info)
pl_json = toJson(payload)
l1 = fromJson(pl_json, Payload)