`json.JSONEncoder`를 서브클래싱하지 않고 사용자 정의 개체 JSON을 직렬화할 수 있도록 하려면 어떻게 해야 합니까?

Barbara Streisand
풀어 주다: 2024-10-30 20:25:30
원래의
351명이 탐색했습니다.

How can I make custom objects JSON serializable without subclassing `json.JSONEncoder`?

일반 인코더를 사용하여 JSON 객체를 직렬화 가능하게 만들기

직렬화할 수 없는 사용자 정의 객체를 JSON으로 직렬화하는 기본 방법은 json.JSONEncoder를 하위 클래스로 분류하고 사용자 정의 인코더를 전달하는 것입니다. json.dumps(). 이는 일반적으로 다음과 같습니다.

<code class="python">class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Foo):
            return obj.to_json()

        return json.JSONEncoder.default(self, obj)

print(json.dumps(obj, cls=CustomEncoder))</code>
로그인 후 복사

그러나 기본 인코더를 사용하여 객체를 직렬화할 수 있도록 하려면 어떻게 해야 할까요? json 모듈의 소스 코드를 검토한 결과 인코더를 직접 확장하는 것은 이 요구 사항을 충족하지 못하는 것으로 보입니다.

대신 패키지의 __init__.py 초기화 스크립트 내에서 "monkey-patching"이라는 기술을 사용할 수 있습니다. 모듈은 일반적으로 한 번만 로드되고 결과는 sys.modules에 캐시되므로 이는 모든 후속 JSON 모듈 직렬화에 영향을 미칩니다.

패치는 고유한 "to_json" 메서드를 확인하기 위해 기본 JSON 인코더의 기본 메서드를 수정합니다. 발견된 경우 이를 활용하여 개체를 인코딩합니다.

다음은 단순성을 위해 독립형 모듈로 구현된 예입니다.

<code class="python"># Module: make_json_serializable.py

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder.default  # Save unmodified default.
JSONEncoder.default = _default  # Replace it.</code>
로그인 후 복사

이 패치를 사용하는 것은 간단합니다. 모듈을 가져와서 원숭이를 적용하면 됩니다. -patch.

<code class="python"># Sample client script

import json
import make_json_serializable  # apply monkey-patch

class Foo(object):
    def __init__(self, name):
        self.name = name

    def to_json(self):  # New special method.
        """Convert to JSON format string representation."""
        return '{"name": "%s"}' % self.name

foo = Foo('sazpaz')
print(json.dumps(foo))  # -> '{"name": "sazpaz"}'</code>
로그인 후 복사

객체 유형 정보를 유지하기 위해 to_json 메서드는 반환된 문자열에 해당 정보를 포함할 수 있습니다.

<code class="python">def to_json(self):
    """Convert to JSON format string representation."""
    return '{"type": "%s", "name": "%s"}' % (self.__class__.__name__, self.name)</code>
로그인 후 복사

이렇게 하면 클래스 이름이 포함된 JSON이 생성됩니다.

{"type": "Foo", "name": "sazpaz"}
로그인 후 복사

Magick Lies Here

더욱 강력한 접근 방식은 대체 기본 메서드를 사용하여 고유한 메서드 없이 사용자 정의 클래스 인스턴스를 포함한 대부분의 Python 객체를 자동으로 직렬화하는 것입니다.

여러 대안을 조사한 결과 피클을 기반으로 한 다음 접근 방식이 이 이상에 가장 가까운 것으로 나타났습니다.

<code class="python"># Module: make_json_serializable2.py

from json import JSONEncoder
import pickle

def _default(self, obj):
    return {"_python_object": pickle.dumps(obj)}

JSONEncoder.default = _default  # Replace with the above.</code>
로그인 후 복사

모든 것을 피클할 수는 없지만(예: 확장 유형) 피클은 프로토콜을 통해 이를 처리하는 방법을 제공합니다. 독특한 방법을 사용합니다. 그러나 이 접근 방식은 더 많은 경우를 포괄합니다.

역직렬화

피클 프로토콜을 사용하면 "_python_object"가 발생할 때 json.loads()에 사용자 정의 object_hook 함수 인수를 제공하여 원래 Python 객체를 재구성하는 것이 단순화됩니다. 키를 사전에 넣습니다.

<code class="python">def as_python_object(dct):
    try:
        return pickle.loads(str(dct['_python_object']))
    except KeyError:
        return dct

pyobj = json.loads(json_str, object_hook=as_python_object)</code>
로그인 후 복사

이것은 래퍼 함수로 단순화될 수 있습니다.

<code class="python">json_pkloads = functools.partial(json.loads, object_hook=as_python_object)

pyobj = json_pkloads(json_str)</code>
로그인 후 복사

json.dumps()가 바이트 객체를 반환하기 때문에 이 코드는 Python 3에서 작동하지 않습니다. JSONEncoder가 처리할 수 없는 문제입니다. 그러나 다음 수정을 통해 접근 방식은 여전히 ​​유효합니다.

<code class="python">def _default(self, obj):
    return {"_python_object": pickle.dumps(obj).decode('latin1')}

def as_python_object(dct):
    try:
        return pickle.loads(dct['_python_object'].encode('latin1'))
    except KeyError:
        return dct</code>
로그인 후 복사

위 내용은 `json.JSONEncoder`를 서브클래싱하지 않고 사용자 정의 개체 JSON을 직렬화할 수 있도록 하려면 어떻게 해야 합니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿