오브젝트 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
를 참조해 주세요.
사용방법이 떠올랐습니다.pickle
Raymond Hettinger의 답변에서 다른 JSON 시리얼라이제이션 질문으로 넘어갑니다(Python 핵심 개발자의 답변과 같이).
Python 3으로의 이식성
있는 하지 않습니다.Python 3은 Python 3의 코드이기 때문입니다.json.dumps()
를 반환하다bytes
의 목적JSONEncoder
감당할 수 없다.그러나 그 접근법은 여전히 유효하다.하기 위한 은 " " " 입니다.latin1
값pickle.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
'programing' 카테고리의 다른 글
JSON에서 TypeScript 클래스 인스턴스로? (0) | 2023.03.13 |
---|---|
Ionic 어플리케이션에서 하드웨어 되돌리기 버튼을 비활성화하시겠습니까? (0) | 2023.03.13 |
JSON 표현에서의 링크 관계 (0) | 2023.03.13 |
안심.request json에서 값을 추출할 수 있습니까? (0) | 2023.03.13 |
Backbone.js - id vs idAttribute vs cid (0) | 2023.03.13 |