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

时间:2013-03-18 12:34:38

标签: python json

我有以下字符串

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

我想反序列化为类

的对象
class payload
    string action
    string method
    string data

我正在使用python 2.6和2.7

12 个答案:

答案 0 :(得分:98)

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

答案 1 :(得分:41)

详细说明萨米的回答:

来自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)

我反对

.__dict__ 

解决方案是,当它完成工作并且简洁时,Payload类变为完全泛型 - 它不会记录其字段。

例如,如果Payload消息具有意外格式,而不是在创建Payload时抛出未找到密钥错误,则在使用有效负载之前不会生成错误。

答案 2 :(得分:10)

如果你正在接受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方法反序列化的类。

答案 3 :(得分:7)

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

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


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

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

答案 4 :(得分:6)

我更喜欢添加一些字段检查,例如所以你可以捕获错误,比如当你得到无效的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"}}}' 你仍然可以使用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

给你

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

答案 5 :(得分:5)

您可以专门设计用于创建对象的编码器:http://docs.python.org/2/library/json.html

答案 6 :(得分:4)

我以为我失去了所有的头发来解决这个挑战&#39;。我遇到了以下问题:

  1. 如何反序列化嵌套对象,列表等。
  2. 我喜欢带有指定字段的构造函数
  3. 我不喜欢动态字段
  4. 我不喜欢hacky solutions
  5. 我发现了一个名为 AND IFNULL(stock.quantity, 0) > 0 的库,它已被证明非常有用。

    安装:

    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)
    

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

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

答案 7 :(得分:2)

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

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对象作为dict传递给Payload类的构造函数。 Payload类的构造函数将dict解释为关键字参数并设置所有适当的字段。

答案 8 :(得分:1)

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

答案 9 :(得分:1)

虽然Alex's的答案为我们提供了一种好的技术,但是当我们嵌套对象时,他给出的实现会遇到问题。

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)

答案 10 :(得分:1)

pydantic是一个越来越流行的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}

答案 11 :(得分:0)

有多种方法可以将json字符串反序列化为对象。以上所有方法都是可以接受的,但是我建议使用一个库来防止重复的密钥问题或对嵌套对象进行序列化/反序列化。

Pykson, is a JSON Serializer and Deserializer for Python,可以帮助您实现目标。只需将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)