programing

오브젝트 JSON을 일반 인코더로 시리얼화

lastcode 2023. 3. 13. 20:31
반응형

오브젝트 JSON을 일반 인코더로 시리얼화

커스텀 JSON 은, JSON 의 입니다.json.JSONEncoder.json.dumps().

일반적으로 다음과 같습니다.

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

제가 하려는 것은 기본 인코더로 직렬화할 수 있는 것을 만드는 것입니다.주위를 둘러보았지만 아무것도 찾을 수 없었다.인코더가 json 부호화를 판별하기 위해 참조하는 필드가 있다고 생각합니다.__str__아마도__json__판판. 단뱀도도 단??

JSON으로 만들고 있는 모듈의 한 클래스를 패키지를 사용하는 모든 사용자가 직접 [자세한] 커스텀 인코더를 구현할 염려 없이 직렬화할 수 있도록 하고 싶습니다.

, ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★json모듈의 소스 코드, 원하는 작업을 수행하는 데 적합하지 않은 것 같습니다.그러나 그 목표는 원숭이 패칭이라고 알려진 것에 의해 달성될 수 있다(원숭이 패치가 무엇인가? 질문 참조).이 작업은 패키지의__init__.py 스크립트에 .json는 "1"에 입니다.sys.modules.

의 "json"을 합니다.default 방식: " " "default().

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

듈::make_json_serializable.py

""" Module that monkey-patches json module when it's imported so
JSONEncoder.default() automatically checks for a special "to_json()"
method and uses it to encode the object if found.
"""
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.

모듈을 Import하는 것만으로 패치가 적용되기 때문에 사용하는 것은 간단합니다.

샘플 클라이언트 스크립트:

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\"}"

개체 유형 정보를 유지하기 위해 특수 메서드는 반환되는 문자열에 개체 유형 정보를 포함할 수도 있습니다.

        return ('{"type": "%s", "name": "%s"}' %
                 (self.__class__.__name__, self.name))

그러면 클래스 이름이 포함된 다음 JSON이 생성됩니다.

"{\"type\": \"Foo\", \"name\": \"sazpaz\"}"

매직 라이즈 헤어

보다 더 .default()특별히 명명된 메서드를 찾습니다. 특별한 메서드를 추가할 필요 없이 사용자 정의 클래스 인스턴스를 포함하여 대부분의 Python 개체를 자동으로 직렬화할 수 있습니다.몇 가지 대안을 검토한 후, @Raymond Hettinger가 다른 질문에 답한 내용을 바탕으로 다음 항목을 사용합니다.pickle「 」 「 」 、 「 」 、 「 」

듈::make_json_serializable2.py

""" Module that imports the json module and monkey-patches it so
JSONEncoder.default() automatically pickles any Python objects
encountered that aren't standard JSON data types.
"""
from json import JSONEncoder
import pickle

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

JSONEncoder.default = _default  # Replace with the above.

물론 확장 유형 등 모든 것을 절일 수는 없습니다.그러나 피클 프로토콜을 통해 처리하도록 정의된 방법이 있습니다(앞에서 설명한 것과 유사함). 그러나 이러한 방법은 훨씬 적은 수의 케이스에서 필요할 수 있습니다.

역직렬화

할 수 .object_hook의 「」)json.loads()의 한 콜'_python_object'키가 있을 때마다 사전에서 키를 전달합니다.예를 들어 다음과 같습니다.

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)

많은 장소에서 이 작업을 수행해야 하는 경우 자동으로 추가 키워드 인수를 제공하는 래퍼 함수를 정의하는 것이 좋습니다.

json_pkloads = functools.partial(json.loads, object_hook=as_python_object)

pyobj = json_pkloads(json_str)

수 요, 원숭이로 수 있어요.json도 사용할 수 있습니다.이을 디폴트로 합니다.object_hook)None를 참조해 주세요.

사용방법이 떠올랐습니다.pickleRaymond Hettinger의 답변에서 다른 JSON 시리얼라이제이션 질문으로 넘어갑니다(Python 핵심 개발자의 답변과 같이).

Python 3으로의 이식성

있는 하지 않습니다.Python 3은 Python 3의 코드이기 때문입니다.json.dumps()를 반환하다bytes의 목적JSONEncoder감당할 수 없다.그러나 그 접근법은 여전히 유효하다.하기 위한 은 " " " 입니다.latin1pickle.dumps()그리고 나서, 그것을 「유효하게」에서 「스스로」latin1을 것것에 넘기다pickle.loads() as_python_object()이것 유효하기 때문합니다.latin1이것은 항상 Unicode로 디코딩된 후 원래의 문자열로 다시 인코딩할 수 있습니다(Sven Marnach가 이 답변에서 지적한 바와 같이).

만, Python 2에서는,latin1디코딩 및 부호화는 불필요합니다.)

from decimal import Decimal

class PythonObjectEncoder(json.JSONEncoder):
    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


class Foo(object):  # Some user-defined class.
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        if type(other) is type(self):  # Instances of same class?
            return self.name == other.name
        return NotImplemented

    __hash__ = None


data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'},
        Foo('Bar'), Decimal('3.141592653589793238462643383279502884197169')]
j = json.dumps(data, cls=PythonObjectEncoder, indent=4)
data2 = json.loads(j, object_hook=as_python_object)
assert data == data2  # both should be same

다음과 같이 dict 클래스를 확장할 수 있습니다.

#!/usr/local/bin/python3
import json

class Serializable(dict):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # hack to fix _json.so make_encoder serialize properly
        self.__setitem__('dummy', 1)

    def _myattrs(self):
        return [
            (x, self._repr(getattr(self, x))) 
            for x in self.__dir__() 
            if x not in Serializable().__dir__()
        ]

    def _repr(self, value):
        if isinstance(value, (str, int, float, list, tuple, dict)):
            return value
        else:
            return repr(value)

    def __repr__(self):
        return '<%s.%s object at %s>' % (
            self.__class__.__module__,
            self.__class__.__name__,
            hex(id(self))
        )

    def keys(self):
        return iter([x[0] for x in self._myattrs()])

    def values(self):
        return iter([x[1] for x in self._myattrs()])

    def items(self):
        return iter(self._myattrs())

일반 인코더로 클래스를 직렬화하려면 '직렬화 가능'을 확장하십시오.

class MySerializableClass(Serializable):

    attr_1 = 'first attribute'
    attr_2 = 23

    def my_function(self):
        print('do something here')


obj = MySerializableClass()

print(obj)뭇매를 맞다

<__main__.MySerializableClass object at 0x1073525e8>

print(json.dumps(obj, indent=4))뭇매를 맞다

{
    "attr_1": "first attribute",
    "attr_2": 23,
    "my_function": "<bound method MySerializableClass.my_function of <__main__.MySerializableClass object at 0x1073525e8>>"
}

나는 클래스 정의에 해킹을 넣을 것을 제안한다.이와 같이 클래스가 정의되면 JSON이 지원됩니다.예를 들어:

import json

class MyClass( object ):

    def _jsonSupport( *args ):
        def default( self, xObject ):
            return { 'type': 'MyClass', 'name': xObject.name() }

        def objectHook( obj ):
            if 'type' not in obj:
                return obj
            if obj[ 'type' ] != 'MyClass':
                return obj
            return MyClass( obj[ 'name' ] )
        json.JSONEncoder.default = default
        json._default_decoder = json.JSONDecoder( object_hook = objectHook )

    _jsonSupport()

    def __init__( self, name ):
        self._name = name

    def name( self ):
        return self._name

    def __repr__( self ):
        return '<MyClass(name=%s)>' % self._name

myObject = MyClass( 'Magneto' )
jsonString = json.dumps( [ myObject, 'some', { 'other': 'objects' } ] )
print "json representation:", jsonString

decoded = json.loads( jsonString )
print "after decoding, our object is the first in the list", decoded[ 0 ]

「」의 JSONEncoder().default한 번만 할 수 있다는 거예요.우연히 그 패턴과 동작하지 않는 특수한 데이터 타입이 발견되었을 경우(이상한 인코딩을 사용하는 경우 등).아래 패턴에서는 시리얼화하려는 클래스 필드 자체가 시리얼화(python 목록에 거의 추가되지 않음)되는 경우 언제든지 클래스 JSON을 시리얼화 할 수 있습니다.그렇지 않으면 json 필드에 동일한 패턴을 재귀적으로 적용하거나 json 필드에서 직렬화 가능 데이터를 추출해야 합니다.

# base class that will make all derivatives JSON serializable:
class JSONSerializable(list): # need to derive from a serializable class.

  def __init__(self, value = None):
    self = [ value ]

  def setJSONSerializableValue(self, value):
    self = [ value ]

  def getJSONSerializableValue(self):
    return self[1] if len(self) else None


# derive  your classes from JSONSerializable:
class MyJSONSerializableObject(JSONSerializable):

  def __init__(self): # or any other function
    # .... 
    # suppose your__json__field is the class member to be serialized. 
    # it has to be serializable itself. 
    # Every time you want to set it, call this function:
    self.setJSONSerializableValue(your__json__field)
    # ... 
    # ... and when you need access to it,  get this way:
    do_something_with_your__json__field(self.getJSONSerializableValue())


# now you have a JSON default-serializable class:
a = MyJSONSerializableObject()
print json.dumps(a)

못안 돼요.serialize신의의 츠요시하고, 「수 있도록 합니다.이 함수는 으로 「사람」으로 반환됩니다.이 함수는 기본적으로 반환됩니다.self.__dict__기능을 제거했습니다.

편집:

이 질문은 가장 간단한 방법은 당신만의 방법을 써서 당신이 원하는 json 시리얼 데이터를 반환하는 것이라는 제 생각에 동의합니다.또한 jsonpickle을 사용해 보는 것도 권장되지만, 이제 올바른 솔루션이 내장되면 아름다움에 대한 의존도가 높아집니다.

가동 자체 합니다.json커스텀 인코더를 사용해, 덮어쓰기 하는 것을 명확하게 합니다.몽키패치 testenv 몽키패치

예를들면,

class JSONDatetimeAndPhonesEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, (datetime.date, datetime.datetime)):
            return obj.date().isoformat()
        elif isinstance(obj, basestring):
            try:
                number = phonenumbers.parse(obj)
            except phonenumbers.NumberParseException:
                return json.JSONEncoder.default(self, obj)
            else:
                return phonenumbers.format_number(number, phonenumbers.PhoneNumberFormat.NATIONAL)
        else:
            return json.JSONEncoder.default(self, obj)

원하는 것은 다음과 같습니다.

payload = json.data(your_data, cls=J)손타임And Phones Encoder)

또는 다음과 같이 입력합니다.

payload = your_payload(your_data)

또는 다음과 같이 입력합니다.

payload = your_json.data(your_data)

그러나 테스트 환경에서는 다음과 같이 하십시오.

@pytest.fixture(scope='session', autouse=True)
def testenv_monkey_patching():
    json._default_encoder = JSONDatetimeAndPhonesEncoder()

가 모든 에 적용됩니다.json.dumps★★★★★★ 。

언급URL : https://stackoverflow.com/questions/18478287/making-object-json-serializable-with-regular-encoder

반응형