오브젝트 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감당할 수 없다.그러나 그 접근법은 여전히 유효하다.하기 위한 은 " " " 입니다.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 |