파이썬에서 json 문자열을 객체로 역 직렬화
다음 문자열이 있습니다.
{"action":"print","method":"onData","data":"Madan Mohan"}
클래스 개체로 역 직렬화하고 싶습니다.
class payload
string action
string method
string data
파이썬 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 클래스는 완전히 일반화 되어 필드를 문서화하지 않습니다.
예를 들어, 페이로드 메시지에 예기치 않은 형식이있는 경우 페이로드가 생성 될 때 키를 찾을 수 없음 오류가 발생하는 대신 페이로드가 사용될 때까지 오류가 생성되지 않습니다.
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을 필요로하며 모든 경우를 포함하지는 않습니다-예를 들어, 타이핑 지원 .Any ... 그러나 최소한 추가 init / tojson 메소드로 역 직렬화해야하는 클래스를 오염 시키지는 않습니다.
코드 줄을 저장하고 가장 유연한 솔루션을 유지하려면 json 문자열을 동적 객체로 역 직렬화 할 수 있습니다.
p = lambda:None
p.__dict__ = json.loads('{"action": "print", "method": "onData", "data": "Madan Mohan"}')
>>>> p.action
출력 : u'print '
>>>> p.method
출력 : u'onData '
예를 들어 잘못된 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'
객체 생성을위한 인코더를 전문화 할 수 있습니다. http://docs.python.org/2/library/json.html
이 '도전'을 풀기 위해 머리카락을 다 잃은 줄 알았다. 다음과 같은 문제에 직면했습니다.
- 중첩 된 개체, 목록 등을 역 직렬화하는 방법
- 지정된 필드가있는 생성자를 좋아합니다.
- 동적 필드가 마음에 들지 않습니다.
- 나는 해키 솔루션을 좋아하지 않는다
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/
시간과 머리카락을 절약 할 수 있기를 바랍니다.
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.
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"})
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)
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)
참고URL : https://stackoverflow.com/questions/15476983/deserialize-a-json-string-to-an-object-in-python
'IT Share you' 카테고리의 다른 글
Java ArrayList-목록이 비어 있는지 확인 (0) | 2020.12.09 |
---|---|
.htaccess에서 cors 활성화 (0) | 2020.12.09 |
StreamReader를 사용하여 외국 문자 읽기 C # 도움말 (0) | 2020.12.09 |
Play!를 사용하여 Java 클래스에서 application.conf 속성에 액세스 (0) | 2020.12.09 |
Pydoc으로 문서를 어떻게 만드나요? (0) | 2020.12.09 |