Deserialize a json string to an object in python

后端 未结 12 2277
囚心锁ツ
囚心锁ツ 2020-11-28 05:01

I have the following string

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

I Want to deserialize to a object of cla

相关标签:
12条回答
  • 2020-11-28 05:15

    You can specialize an encoder for object creation: 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)
    
    0 讨论(0)
  • 2020-11-28 05:17
    >>> 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'
    
    0 讨论(0)
  • 2020-11-28 05:17

    In recent versions of python, you can use 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"})
    
    0 讨论(0)
  • 2020-11-28 05:18

    There are different methods to deserialize json string to an object. All above methods are acceptable but I suggest using a library to prevent duplicate key issues or serializing/deserializing of nested objects.

    Pykson, is a JSON Serializer and Deserializer for Python which can help you achieve. Simply define Payload class model as JsonObject then use Pykson to convert json string to object.

    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)
    
    0 讨论(0)
  • 2020-11-28 05:21

    While Alex's answer 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_info will also be treated as an instance of payload which will lead to parsing errors.

    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.

    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)
    
    
    0 讨论(0)
  • 2020-11-28 05:23

    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:

    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 = {"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

    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'
    
    0 讨论(0)
提交回复
热议问题