将json字符串反序列化为python中的对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15476983/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:14:52  来源:igfitidea点击:

Deserialize a json string to an object in python

pythonjson

提问by

I have the following string

我有以下字符串

{"action":"print","method":"onData","data":"Madan Mohan"}

I Want to deserialize to a object of class

我想反序列化为类的对象

class payload
    string action
    string method
    string data

I am using python 2.6 and 2.7

我正在使用 python 2.6 和 2.7

采纳答案by John La Rooy

>>> 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'

回答by Sami N

You can specialize an encoder for object creation: http://docs.python.org/2/library/json.html

您可以专门用于对象创建的编码器: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)

回答by Alex

To elaborate on Sami's answer:

详细说明萨米的回答:

From the docs:

文档

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)

My objection to the

我的反对意见

.__dict__ 

solution is that while it does the job and is concise, the Payload class becomes totally generic- it doesn't document its fields.

解决方案是,虽然它可以完成工作并且简洁,但 Payload 类变得完全通用- 它不记录其字段。

For example, if the Payload message had an unexpected format, instead of throwing a key not found error when the Payload was created, no error would be generated until the payload was used.

例如,如果 Payload 消息具有意外格式,那么在创建 Payload 时不会抛出 key not found 错误,而是在使用 Payload 之前不会生成错误。

回答by Nery Jr

If you want to save lines of code and leave the most flexible solution, we can deserialize the json string to a dynamic object:

如果你想节省代码行并留下最灵活的解决方案,我们可以将json字符串反序列化为动态对象:

p = lambda:None
p.__dict__ = json.loads('{"action": "print", "method": "onData", "data": "Madan Mohan"}')


>>>> p.action
output: u'print'


>>>> p.action
输出: u'print'

>>>> p.method
output: u'onData'

>>>> p.method
输出:u'onData'

回答by Jens Timmerman

I prefer to add some checking of the fields, e.g. so you can catch errors like when you get invalid json, or not the json you were expecting, so I used namedtuples:

我更喜欢添加一些字段检查,例如,这样您就可以捕获错误,例如当您获得无效的 json 或不是您期望的 json 时,所以我使用了命名元组:

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)

this will let give you nice errors when the json you are parsing does not match the thing you want it to parse

当您解析的 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'

if you want to parse nested relations, e.g. '{"parent":{"child":{"name":"henry"}}}'you can still use the namedtuples, and even a more reusable function

如果你想解析嵌套关系,例如'{"parent":{"child":{"name":"henry"}}}'你仍然可以使用namedtuples,甚至是一个更可重用的函数

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

giving you

给你

>>> 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'

回答by GaspardP

If you are embracing the type hints in Python 3.6, you can do it like this:

如果您正在接受 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

Which then allows you do instantiate typed objects like this:

然后允许您像这样实例化类型化对象:

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)

This syntax requires Python 3.6 though and does not cover all cases - for example, support for typing.Any... But at least it does not pollute the classes that need to be deserialized with extra init/tojson methods.

虽然这种语法需要 Python 3.6 并且不能涵盖所有情况——例如,支持typing.Any...但至少它不会污染需要用额外的 init/tojson 方法反序列化的类。

回答by LukaszTaraszka

I thought I lose all my hairs for solving this 'challenge'. I faced following problems:

我以为我为解决这个“挑战”而失去了所有的头发。我遇到了以下问题:

  1. How to deserialize nested objects, lists etc.
  2. I like constructors with specified fields
  3. I don't like dynamic fields
  4. I don't like hacky solutions
  1. 如何反序列化嵌套对象、列表等。
  2. 我喜欢具有指定字段的构造函数
  3. 我不喜欢动态字段
  4. 我不喜欢hacky的解决方案

I found a library called jsonpicklewhich is has proven to be really useful.

我发现了一个名为的库jsonpickle,它已被证明非常有用。

Installation:

安装:

pip install jsonpickle

Here is a code example with writing nested objects to file:

这是将嵌套对象写入文件的代码示例:

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)

Output:

输出:

John
21
John jr.

Website: http://jsonpickle.github.io/

网站:http: //jsonpickle.github.io/

Hope it will save your time (and hairs).

希望它能节省您的时间(和头发)。

回答by rouble

Another way is to simply pass the json string as a dict to the constructor of your object. For example your object is:

另一种方法是简单地将 json 字符串作为 dict 传递给对象的构造函数。例如你的对象是:

class Payload(object):
    def __init__(self, action, method, data, *args, **kwargs):
        self.action = action
        self.method = method
        self.data = data

And the following two lines of python code will construct it:

下面两行python代码将构造它:

j = json.loads(yourJsonString)
payload = Payload(**j)

Basically, we first create a generic json object from the json string. Then, we pass the generic json object as a dict to the constructor of the Payload class. The constructor of Payload class interprets the dict as keyword arguments and sets all the appropriate fields.

基本上,我们首先从 json 字符串创建一个通用的 json 对象。然后,我们将通用 json 对象作为 dict 传递给 Payload 类的构造函数。Payload 类的构造函数将 dict 解释为关键字参数并设置所有适当的字段。

回答by lovasoa

In recent versions of python, you can use marshmallow-dataclass:

在最新版本的 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"})

回答by Gulats

While Alex'sanswer points us to a good technique, the implementation that he gave runs into a problem when we have nested objects.

虽然亚历克斯的回答为我们指出了一个很好的技术,但当我们有嵌套对象时,他给出的实现会遇到问题。

class more_info
    string status

class payload
    string action
    string method
    string data
    class more_info

with the below code:

使用以下代码:

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_infowill also be treated as an instance of payloadwhich will lead to parsing errors.

payload.more_info也将被视为payload会导致解析错误的实例。

From the official docs:

来自官方文档:

object_hook is an optional function that will be called with the result of any object literal decoded (a dict). The return value of object_hook will be used instead of the dict.

object_hook 是一个可选函数,将使用任何对象文字解码的结果(字典)调用。将使用 object_hook 的返回值而不是 dict。

Hence, I would prefer to propose the following solution instead:

因此,我更愿意提出以下解决方案:

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)