Deserialize a json string to an object in python

后端 未结 12 2251
囚心锁ツ
囚心锁ツ 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:24

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

    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.

    0 讨论(0)
  • 2020-11-28 05:24

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

    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:

    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.

    0 讨论(0)
  • 2020-11-28 05:31

    pydantic is an increasingly popular library for python 3.6+ projects. It mainly does data validation and settings management using type hints.

    A basic example using different types:

    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)
    

    What I love about the lib is that you get a lot of goodies for free, like

    >>> cb.json()
    '{"count_drinks": 226, "is_open": false}'
    >>> cb.dict()
    {'count_drinks': 226, 'is_open': False}
    
    0 讨论(0)
  • 2020-11-28 05:37

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

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


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

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

    0 讨论(0)
  • 2020-11-28 05:37

    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

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

    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/

    Hope it will save your time (and hairs).

    0 讨论(0)
  • 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.

    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.

    0 讨论(0)
提交回复
热议问题