Python을 사용하여 객체 Enceode/Decode를 JSON에 연결

22763 단어 PythonJSONtech

개시하다


파이톤에는 json이라는 모듈이 있는데, 일반적으로 사용하면 Enceode/Decode는 dict 형식으로 진행된다.
이 상태에서도 충분히 활용할 수 있지만 자바스크립트 오브젝트 노트인데 대상이 안 되는 건 좀 불편하지 않나요?따라서 개체 JSON 문자열의 변환이 요약됩니다.
기본 사전 형식의 Enceode/Decode에 관해서는 여기를 참조하십시오. 하십시오.

TL;DR


어쨌든 간단한 대상은 다음과 같은 요령에 따라 처리할 수 있다.
import json
from types import SimpleNamespace

# オブジェクト定義
# コンストラクタのある場合
class Foo():
    def __init__(self, hoge1: str, hogeNum: int, hoge_list: list) -> None:
        self.hoge1 = hoge1
        self.hogeNum = hogeNum
        self.hoge_list = hoge_list

f = Foo("hoge1", 192, [1, 2, 3])

# コンストラクタの無い場合
class Hoge():
    pass

h = Hoge()
h.hoge = "hogehoge"
h.hoga = "hogahoga"
h.num = 123
h.num_list = [1, 2, 3]

# Encode時
# オブジェクトから __dict__ を通じてAttributeの辞書を取得し、JSONに
json_str = json.dumps(f.__dict__)
print(json_str)
# >> {"hoge1": "hoge1", "hogeNum": 192, "hoge_list": [1, 2, 3]}

h_str = json.dumps(h.__dict__)
print(h_str)
# >> {"hoge": "hogehoge", "hoga": "hogahoga", "num": 123, "num_list": [1, 2, 3]}

# Decode時
# object_hookにコンストラクタをいれる(辞書で受けた引数を展開する)
# コンストラクタが未定義の場合:
#   types モジュールのSimpleNamespaceを使う

f = json.loads(json_str, object_hook=lambda x: Foo(**x))
print(f.hoge1) # >> hoge1

h = json.loads(h_str, object_hook=lambda x: SimpleNamespace(**x))
print(h.num_list) # >> [1, 2, 3]

도서관 없어요?


'json tricks'라는 모듈이 있는데 자신이 노력하지 않아도 간단하게 대상을 JSON화할 수 있다.
그러나 이는 이 모듈을 통해 Enceode와 Decode 쌍방을 진행하는 것을 전제로 하기 때문에 이미 정해진 JSON 대상을 처리할 때 스스로 써야 한다.
다른 것은'ijson'이라는 도서관도 있다.수십 MB가 넘는 데이터를 모두 메모리에 읽지 않고도 사용할 수 있는 대규모 데이터 분석 도구지만, 목적 대상과는 읽기와 쓰기가 조금 다르다.
결론: 자신에게 맞는 것은 스스로 할 수밖에 없다.

기본 이념


json 모듈의 loads(load)에서 obj 매개 변수는 Any이지만 일반적으로 대상을 넣으면 Typereror가 나타납니다.

class Hoge():
    pass

h = Hoge()
h.hoge = "hogehoge"
h.hoga = "hogahoga"
h.num = 123
h.num_list = [1, 2, 3]

h_str = json.dumps(h) 
# TypeError: Object of type Hoge is not JSON serializable

개체가 이미 정의된 Enceoder에서 참조하는 변환 테이블에 변환 개체로 들어가지 않았기 때문입니다.
Python→JSON의 변환표 (공식)
따라서 __dict__를 사용하여 대상이 정의한 속성을 사전 형식으로 가져오고 전달한다.__dict__ 상세 정보여기(공식)
h_str = json.dumps(h.__dict__)
print(h_str)
# {"hoge": "hogehoge", "hoga": "hogahoga", "num": 123, "num_list": [1, 2, 3]}
직접 두드릴 수 없으면 dict 형식으로 변환하는 함수를 만드십시오.
클래스에서 개인 변수__dict__를 정의하면 내용이 특수해지기 때문에 이런 상황에서도 함수를 만들어야 한다.
class Foo():
    def __init__(self, hoge:str):
        self.hoge = hoge
        self.__hoge = "private value"

f = Foo("foo")
print(f.__dict__)
# >> {'hoge': 'foo', '_Foo__hoge': 'private value'}
# 属性名が一致していないので再現が難しい

기본 설계


Decode에서 매개변수__dict__를 사용합니다.
함수를 여기에 전달하면 Enceod에서 Object 요소가 감지될 때 함수가 호출됩니다.
매개 변수로서 내용이 dict 형식으로 전달되기 때문에 이를 구조기에 전개한다.
매개 변수에 대한 사전 전개여기.
정의된 구조기가 없는 클래스에서도 types 모듈에서 가져오면 object_hook 대상으로 간단하게 복원할 수 있습니다.
그러나 완성된 대상은 원래의 종류와 다르기 때문에 방법과 속성을 호출할 수 없다.
from types import SimpleNamespace
h = json.loads(h_str, object_hook=lambda x: SimpleNamespace(**x))
print(h.num_list) # >> [1, 2, 3]

복잡한 속성을 가진 클래스 처리


위의 예에서 문자, 수치, 배열 등 JSON에서도 이미 정해진 요소로 구성된 대상만 처리한다.
그러나 실제 대상에는 다양한 요소가 포함될 수 있다.
이럴 때는 상술한 방법만으로는 해결할 수 없기 때문에 스스로 좀 더 실시해야 한다.

Enceode 시


json 모듈 내의 JSONI Coder를 계승한 단독 반을 생성하고dupp/dumps를 칠 때cls 매개 변수를 건네줍니다.
클래스에서defaut 방법을 다시 쓰고 대상 형식에 따라 복구 가능한 dict 형식으로 변환합니다.
구체적으로 말하면 키에 설정된 문자열이 속성 (attrabute) 의 이름으로 덮어쓰지 않는다는 것을 복구할 수 있다.
Decode에서 읽은 dict의 키는 원래 대상으로 되돌아갑니다. 그러나 속성 이름이 덮어쓰이면 복원에 실패합니다.
다만 언어는 이해하기 어렵지만 다음과 같은 느낌이 든다.
class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            # 属性名と被らないようなKeyを設定して辞書形式で返す。
            return {"__uuid__": str(obj)}        
        elif isinstance(obj, object) and hasattr(obj, '__dict__'):
            # 一般的なクラスオブジェクトは__dict__で返す。
            return obj.__dict__
        # 数値や文字列など元のJSONに含まれるものは元のEncoderを使う。        
        return json.JSONEncoder.default(self, obj)        
json.dumps(h, cls=MyEncoder)

Decode 시


Enceode 때와 마찬가지로 JSOND ecoder를 계승하는 독립반을 만들 필요가 없고 SimpleNamespace에 전달되는 의식을 정의하면 된다.
Encode 에서 설정한 키 값에 따라 읽기 값을 복원하는 처리를 추가합니다.
def decode(d: dict):
    if "__uuid__" in d: 
        return uuid.UUID(d["__uuid__"])
    return SimpleNamespace(**d)
    # return d にすると最終的に特定のkeyに対応するオブジェクトが変換した状態のdictが返される。
json.loads(h_json, object_hook=decode)

Example


이상의 총결은 아래와 같다.
import json
import uuid

class Hoge():
    pass

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            return {"__uuid__": str(obj)}
        elif isinstance(obj, object) and hasattr(obj, '__dict__'):
            return obj.__dict__
        return json.JSONEncoder.default(self, obj)

def decode(d: dict):
    if "__uuid__" in d:
        return uuid.UUID(d["__uuid__"])
    return SimpleNamespace(**d)

h = Hoge()
h.id = uuid.uuid1()
h.hoge_str = "hoge"
c = Hoge()
c.c_str = "child"
h.child = c

# cls引数に作成したクラスを渡してやり、obj引数にはオブジェクトをそのまま渡す。
h_json = json.dumps(h, cls=MyEncoder)
print(h_json) 
# >> {"id": {"__uuid__": "生成されたuuidの値"}, "hoge_str": "hoge", "child": {"c_str": "child"}}
h = json.loads(h_json, object_hook=decode)
print(h.id) # >> (生成されたuuidの値)
print(h.child.c_str) # >> child

좋은 웹페이지 즐겨찾기