source

python에서 json 문자열을 개체로 역직렬화합니다.

manysource 2023. 2. 16. 22:09

python에서 json 문자열을 개체로 역직렬화합니다.

다음과 같은 문자열이 있습니다.

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

클래스 객체로 역직렬화하려고 합니다.

class payload
    string action
    string method
    string data

python 2.6과 2.7을 사용하고 있습니다.

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

Sami의 답변을 자세히 설명하자면:

문서에서:

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가 사용될 때까지 오류가 생성되지 않습니다.

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 메서드로 역직렬화해야 하는 클래스를 오염시키지는 않습니다.

파이썬의 최신 버전에서는 마시멜로 데이터 클래스를 사용할 수 있습니다.

from marshmallow_dataclass import dataclass

@dataclass
class Payload
    action:str
    method:str
    data:str

Payload.Schema().load({"action":"print","method":"onData","data":"Madan Mohan"})

나는 이 '도전'을 해결하느라 머리가 다 빠졌다고 생각했다.다음과 같은 문제에 직면했습니다.

  1. 중첩된 개체, 목록 등을 역직렬화하는 방법
  2. 지정된 필드를 가진 생성자가 좋습니다.
  3. 동적 필드를 좋아하지 않습니다.
  4. 나는 진부한 해결책을 좋아하지 않는다.

나는 라는 도서관을 찾았다.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 문자열을 역직렬화할 수 있습니다.

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


>>> p.action
출력: u'print'

>>> 페이지120
출력: u'on Data'

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}

예를 들어 잘못된 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"}}}'튜플이나 더 가능한 할 수 .

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'

또 다른 방법은 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를 키워드 인수로 해석하고 모든 적절한 필드를 설정합니다.

오브젝트 작성 전용 인코더를 지정할 수 있습니다.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)

알렉스의 답변은 우리에게 좋은 기술을 알려주지만, 그가 제공한 구현은 중첩된 개체가 있을 때 문제에 부딪힙니다.

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은 임의의 오브젝트 리터럴 디코딩 결과(dict)로 호출되는 옵션 함수입니다.dict 대신 object_hook 반환값이 사용됩니다.

그 때문에, 이하의 솔루션을 제안하고 싶습니다.

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)

json 문자열을 개체로 역직렬화하는 방법은 여러 가지가 있습니다.위의 모든 방법을 사용할 수 있지만 중복된 키 문제를 방지하거나 중첩된 개체를 직렬화/비직렬화하는 것을 방지하기 위해 라이브러리를 사용할 것을 권장합니다.

Pykson은 Python용 JSON SerializerDiserializer입니다.이것에 의해서, 실현을 지원합니다.Payload 클래스 모델을 JsonObject로 정의한 다음 Pikson을 사용하여 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)

언급URL : https://stackoverflow.com/questions/15476983/deserialize-a-json-string-to-an-object-in-python