직렬화 가능한 Python 객체의 JSON 인코딩 동작을 변경하는 방법은 무엇입니까?
JSON 직렬화가 불가능한 객체의 형식 (예 : datetime.datetime)을 변경하는 것은 쉽습니다.
디버깅 목적으로 내 요구 사항은 dict
및과 같은 기본 개체에서 확장 된 일부 사용자 지정 개체 list
가 json 형식으로 직렬화되는 방식을 변경하는 것 입니다. 코드 :
import datetime
import json
def json_debug_handler(obj):
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return None
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(test_json,default=json_debug_handler))
if __name__ == '__main__':
test_debug_json()
데모 : http://ideone.com/hQJnLy
산출:
{"date": "2013-05-07T01:03:13.098727", "games": ["mario", "contra", "tetris"], "scores": {"pk": 45, "dp": 10}}
원하는 출력 :
{"date": "2013-05-07T01:03:13.098727", "games": { "orig": ["mario", "contra", "tetris"] ,"attrs" : { "src":"console"}} , "scores": { "orig": {"pk": 45, "dp": 10},"attrs": "processed":"unprocessed }}
않습니다 default
직렬화 개체에 대한 처리기 작동하지? 그렇지 않은 경우 확장 클래스에 toJSON 메서드를 추가하지 않고 어떻게 재정의 할 수 있습니까?
또한 작동하지 않는 JSON 인코더 버전이 있습니다.
class JsonDebugEncoder(json.JSONEncoder):
def default(self,obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return json.JSONEncoder.default(self, obj)
해킹이 pickle,__getstate__,__setstate__,
있고 pickle.loads 객체를 통해 json.dumps를 사용 하는 경우 나는 그것에 열려 있고 시도했지만 작동하지 않았습니다.
주어진 제한 사항으로 원하는 동작을 달성하려면 JSONEncoder
수업을 조금씩 탐구해야 할 것 같습니다 . 나는 정의 기입 한 아래의 JSONEncoder
재정의 iterencode
사용자 정의 전달하는 방법 isinstance
에 방법을 _make_iterencode
. 세상에서 가장 깨끗한 것은 아니지만 옵션을 감안할 때 최고로 보이며 사용자 정의를 최소한으로 유지합니다.
# customencoder.py
from json.encoder import (_make_iterencode, JSONEncoder,
encode_basestring_ascii, FLOAT_REPR, INFINITY,
c_make_encoder, encode_basestring)
class CustomObjectEncoder(JSONEncoder):
def iterencode(self, o, _one_shot=False):
"""
Most of the original method has been left untouched.
_one_shot is forced to False to prevent c_make_encoder from
being used. c_make_encoder is a funcion defined in C, so it's easier
to avoid using it than overriding/redefining it.
The keyword argument isinstance for _make_iterencode has been set
to self.isinstance. This allows for a custom isinstance function
to be defined, which can be used to defer the serialization of custom
objects to the default method.
"""
# Force the use of _make_iterencode instead of c_make_encoder
_one_shot = False
if self.check_circular:
markers = {}
else:
markers = None
if self.ensure_ascii:
_encoder = encode_basestring_ascii
else:
_encoder = encode_basestring
if self.encoding != 'utf-8':
def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
if isinstance(o, str):
o = o.decode(_encoding)
return _orig_encoder(o)
def floatstr(o, allow_nan=self.allow_nan,
_repr=FLOAT_REPR, _inf=INFINITY, _neginf=-INFINITY):
if o != o:
text = 'NaN'
elif o == _inf:
text = 'Infinity'
elif o == _neginf:
text = '-Infinity'
else:
return _repr(o)
if not allow_nan:
raise ValueError(
"Out of range float values are not JSON compliant: " +
repr(o))
return text
# Instead of forcing _one_shot to False, you can also just
# remove the first part of this conditional statement and only
# call _make_iterencode
if (_one_shot and c_make_encoder is not None
and self.indent is None and not self.sort_keys):
_iterencode = c_make_encoder(
markers, self.default, _encoder, self.indent,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, self.allow_nan)
else:
_iterencode = _make_iterencode(
markers, self.default, _encoder, self.indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot, isinstance=self.isinstance)
return _iterencode(o, 0)
이제를 하위 클래스로 CustomObjectEncoder
지정하여 사용자 지정 개체를 올바르게 직렬화 할 수 있습니다. 는 CustomObjectEncoder
또한 핸들 중첩 객체처럼 멋진 물건을 할 수 있습니다.
# test.py
import json
import datetime
from customencoder import CustomObjectEncoder
class MyEncoder(CustomObjectEncoder):
def isinstance(self, obj, cls):
if isinstance(obj, (mList, mDict)):
return False
return isinstance(obj, cls)
def default(self, obj):
"""
Defines custom serialization.
To avoid circular references, any object that will always fail
self.isinstance must be converted to something that is
deserializable here.
"""
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, mDict):
return {"orig": dict(obj), "attrs": vars(obj)}
elif isinstance(obj, mList):
return {"orig": list(obj), "attrs": vars(obj)}
else:
return None
class mList(list):
pass
class mDict(dict):
pass
def main():
zelda = mList(['zelda'])
zelda.src = "oldschool"
games = mList(['mario', 'contra', 'tetris', zelda])
games.src = 'console'
scores = mDict({'dp': 10, 'pk': 45})
scores.processed = "unprocessed"
test_json = {'games': games, 'scores': scores,
'date': datetime.datetime.now()}
print(json.dumps(test_json, cls=MyEncoder))
if __name__ == '__main__':
main()
FastTurtle의 대답은 훨씬 더 깨끗한 솔루션 일 수 있습니다.
다음은 내 질문 / 답변에 설명 된 기술에 따라 원하는 것과 가까운 것입니다. dict, list와 같은 상속 된 기본 지원 객체의 중첩 된 JSON 인코딩 재정의
import json
import datetime
class mDict(dict):
pass
class mList(list):
pass
class JsonDebugEncoder(json.JSONEncoder):
def _iterencode(self, o, markers=None):
if isinstance(o, mDict):
yield '{"__mDict__": '
# Encode dictionary
yield '{"orig": '
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers):
yield chunk
yield ', '
# / End of Encode dictionary
# Encode attributes
yield '"attr": '
for key, value in o.__dict__.iteritems():
yield '{"' + key + '": '
for chunk in super(JsonDebugEncoder, self)._iterencode(value, markers):
yield chunk
yield '}'
yield '}'
# / End of Encode attributes
yield '}'
elif isinstance(o, mList):
yield '{"__mList__": '
# Encode list
yield '{"orig": '
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers):
yield chunk
yield ', '
# / End of Encode list
# Encode attributes
yield '"attr": '
for key, value in o.__dict__.iteritems():
yield '{"' + key + '": '
for chunk in super(JsonDebugEncoder, self)._iterencode(value, markers):
yield chunk
yield '}'
yield '}'
# / End of Encode attributes
yield '}'
else:
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers=markers):
yield chunk
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
class JsonDebugDecoder(json.JSONDecoder):
def decode(self, s):
obj = super(JsonDebugDecoder, self).decode(s)
obj = self.recursiveObjectDecode(obj)
return obj
def recursiveObjectDecode(self, obj):
if isinstance(obj, dict):
decoders = [("__mList__", self.mListDecode),
("__mDict__", self.mDictDecode)]
for placeholder, decoder in decoders:
if placeholder in obj: # We assume it's supposed to be converted
return decoder(obj[placeholder])
else:
for k in obj:
obj[k] = self.recursiveObjectDecode(obj[k])
elif isinstance(obj, list):
for x in range(len(obj)):
obj[x] = self.recursiveObjectDecode(obj[x])
return obj
def mDictDecode(self, o):
res = mDict()
for key, value in o['orig'].iteritems():
res[key] = self.recursiveObjectDecode(value)
for key, value in o['attr'].iteritems():
res.__dict__[key] = self.recursiveObjectDecode(value)
return res
def mListDecode(self, o):
res = mList()
for value in o['orig']:
res.append(self.recursiveObjectDecode(value))
for key, value in o['attr'].iteritems():
res.__dict__[key] = self.recursiveObjectDecode(value)
return res
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games, 'scores' : scores ,'date': datetime.datetime.now() }
jsonDump = json.dumps(test_json, cls=JsonDebugEncoder)
print jsonDump
test_pyObject = json.loads(jsonDump, cls=JsonDebugDecoder)
print test_pyObject
if __name__ == '__main__':
test_debug_json()
결과는 다음과 같습니다.
{"date": "2013-05-06T22:28:08.967000", "games": {"__mList__": {"orig": ["mario", "contra", "tetris"], "attr": {"src": "console"}}}, "scores": {"__mDict__": {"orig": {"pk": 45, "dp": 10}, "attr": {"processed": "unprocessed"}}}}
이런 식으로 인코딩하고 가져온 python 객체로 다시 디코딩 할 수 있습니다.
편집하다:
실제로 원하는 출력으로 인코딩하고 디코딩 할 수있는 버전이 있습니다. 사전에 'orig'와 'attr'이 포함되어있을 때마다 'orig'에 사전 또는 목록이 포함되어 있는지 확인하고, 그렇다면 각각 개체를 mDict 또는 mList로 다시 변환합니다.
import json
import datetime
class mDict(dict):
pass
class mList(list):
pass
class JsonDebugEncoder(json.JSONEncoder):
def _iterencode(self, o, markers=None):
if isinstance(o, mDict): # Encode mDict
yield '{"orig": '
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers):
yield chunk
yield ', '
yield '"attr": '
for key, value in o.__dict__.iteritems():
yield '{"' + key + '": '
for chunk in super(JsonDebugEncoder, self)._iterencode(value, markers):
yield chunk
yield '}'
yield '}'
# / End of Encode attributes
elif isinstance(o, mList): # Encode mList
yield '{"orig": '
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers):
yield chunk
yield ', '
yield '"attr": '
for key, value in o.__dict__.iteritems():
yield '{"' + key + '": '
for chunk in super(JsonDebugEncoder, self)._iterencode(value, markers):
yield chunk
yield '}'
yield '}'
else:
for chunk in super(JsonDebugEncoder, self)._iterencode(o, markers=markers):
yield chunk
def default(self, obj):
if isinstance(obj, datetime.datetime): # Encode datetime
return obj.isoformat()
class JsonDebugDecoder(json.JSONDecoder):
def decode(self, s):
obj = super(JsonDebugDecoder, self).decode(s)
obj = self.recursiveObjectDecode(obj)
return obj
def recursiveObjectDecode(self, obj):
if isinstance(obj, dict):
if "orig" in obj and "attr" in obj and isinstance(obj["orig"], list):
return self.mListDecode(obj)
elif "orig" in obj and "attr" in obj and isinstance(obj['orig'], dict):
return self.mDictDecode(obj)
else:
for k in obj:
obj[k] = self.recursiveObjectDecode(obj[k])
elif isinstance(obj, list):
for x in range(len(obj)):
obj[x] = self.recursiveObjectDecode(obj[x])
return obj
def mDictDecode(self, o):
res = mDict()
for key, value in o['orig'].iteritems():
res[key] = self.recursiveObjectDecode(value)
for key, value in o['attr'].iteritems():
res.__dict__[key] = self.recursiveObjectDecode(value)
return res
def mListDecode(self, o):
res = mList()
for value in o['orig']:
res.append(self.recursiveObjectDecode(value))
for key, value in o['attr'].iteritems():
res.__dict__[key] = self.recursiveObjectDecode(value)
return res
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games, 'scores' : scores ,'date': datetime.datetime.now() }
jsonDump = json.dumps(test_json, cls=JsonDebugEncoder)
print jsonDump
test_pyObject = json.loads(jsonDump, cls=JsonDebugDecoder)
print test_pyObject
print test_pyObject['games'].src
if __name__ == '__main__':
test_debug_json()
다음은 출력에 대한 추가 정보입니다.
# Encoded
{"date": "2013-05-06T22:41:35.498000", "games": {"orig": ["mario", "contra", "tetris"], "attr": {"src": "console"}}, "scores": {"orig": {"pk": 45, "dp": 10}, "attr": {"processed": "unprocessed"}}}
# Decoded ('games' contains the mList with the src attribute and 'scores' contains the mDict processed attribute)
# Note that printing the python objects doesn't directly show the processed and src attributes, as seen below.
{u'date': u'2013-05-06T22:41:35.498000', u'games': [u'mario', u'contra', u'tetris'], u'scores': {u'pk': 45, u'dp': 10}}
잘못된 명명 규칙에 대해 죄송합니다. 빠른 설정입니다. ;)
참고 : datetime은 파이썬 표현으로 다시 디코딩되지 않습니다. '날짜'라고 불리는 딕셔너리 키를 확인하여 구현할 수 있으며 날짜 / 시간의 유효한 문자열 표현을 포함합니다.
다른 사람들이 이미 지적했듯이 기본 처리기는 인식 된 유형 중 하나가 아닌 값에 대해서만 호출됩니다. 이 문제에 대한 제가 제안한 해결책은 직렬화하려는 객체를 전처리하고 목록, 튜플 및 사전을 반복하지만 다른 모든 값을 사용자 정의 클래스로 래핑하는 것입니다.
이 같은:
def debug(obj):
class Debug:
def __init__(self,obj):
self.originalObject = obj
if obj.__class__ == list:
return [debug(item) for item in obj]
elif obj.__class__ == tuple:
return (debug(item) for item in obj)
elif obj.__class__ == dict:
return dict((key,debug(obj[key])) for key in obj)
else:
return Debug(obj)
객체를 json.dumps에 전달하기 전에 다음 과 같이이 함수를 호출 합니다.
test_json = debug(test_json)
print(json.dumps(test_json,default=json_debug_handler))
이 코드는 클래스가 목록, 튜플 또는 사전과 정확히 일치하는 객체를 검사하므로 해당 유형에서 확장 된 모든 사용자 정의 객체는 구문 분석되지 않고 래핑됩니다. 결과적으로 일반 목록, 튜플 및 사전은 평소와 같이 직렬화되지만 다른 모든 값은 기본 처리기로 전달됩니다.
이 모든 것의 최종 결과는 기본 핸들러에 도달하는 모든 값이 이러한 Debug 클래스 중 하나에 래핑된다는 것입니다. 따라서 가장 먼저 할 일은 다음과 같이 원본 객체를 추출하는 것입니다.
obj = obj.originalObject
그런 다음 원본 객체의 유형을 확인하고 특수 처리가 필요한 유형을 처리 할 수 있습니다. 그 밖의 모든 경우에는 원래 객체를 반환해야합니다 (따라서 핸들러의 마지막 반환은이 return obj
아니 어야합니다 return None
).
def json_debug_handler(obj):
obj = obj.originalObject # Add this line
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj, 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return obj # Change this line
이 코드는 직렬화 할 수없는 값을 확인하지 않습니다. 이것들은 final을 return obj
통과 한 다음 serializer에 의해 거부되고 다시 기본 핸들러로 다시 전달됩니다. 이번에는 Debug 래퍼가 없습니다.
해당 시나리오를 처리해야하는 경우 다음과 같이 처리기 상단에 확인을 추가 할 수 있습니다.
if not hasattr(obj, 'originalObject'):
return None
Ideone 데모 : http://ideone.com/tOloNq
기본 함수는 덤프되는 노드가 기본적으로 직렬화 가능하지 않고 mDict 클래스가있는 그대로 직렬화 될 때만 호출됩니다. 다음은 default가 호출되는시기와 호출되지 않은 경우를 보여주는 간단한 데모입니다.
import json
def serializer(obj):
print 'serializer called'
return str(obj)
class mDict(dict):
pass
class mSet(set):
pass
d = mDict(dict(a=1))
print json.dumps(d, default=serializer)
s = mSet({1, 2, 3,})
print json.dumps(s, default=serializer)
그리고 출력 :
{"a": 1}
serializer called
"mSet([1, 2, 3])"
집합은 기본적으로 직렬화 할 수 없지만 사전은 가능합니다.
m___ 클래스는 직렬화 가능하므로 핸들러가 호출되지 않습니다.
업데이트 # 1 -----
JSON 인코더 코드를 변경할 수 있습니다. 이를 수행하는 방법에 대한 세부 사항은 사용중인 JSON 구현에 따라 다릅니다. 예를 들어 simplejson에서 관련 코드는 encode.py에서 다음과 같습니다.
def _iterencode(o, _current_indent_level):
...
for_json = _for_json and getattr(o, 'for_json', None)
if for_json and callable(for_json):
...
elif isinstance(o, list):
...
else:
_asdict = _namedtuple_as_object and getattr(o, '_asdict', None)
if _asdict and callable(_asdict):
for chunk in _iterencode_dict(_asdict(),
_current_indent_level):
yield chunk
elif (_tuple_as_array and isinstance(o, tuple)):
...
elif isinstance(o, dict):
...
elif _use_decimal and isinstance(o, Decimal):
...
else:
...
o = _default(o)
for chunk in _iterencode(o, _current_indent_level):
yield chunk
...
즉, 인코딩중인 노드가 인식 된 기본 유형 중 하나가 아닌 경우에만 default를 호출하는 고정 연결 동작이 있습니다. 다음 중 한 가지 방법으로이를 재정의 할 수 있습니다.
1-위에서 수행 한대로 JSONEncoder를 하위 클래스로 지정하지만, 기준을 충족하는 클래스에 대해 기본값을 호출하는 테스트를 추가하는 표준 _make_iterencode 대신 사용할 함수를 지정하는 매개 변수를 초기화 프로그램에 추가합니다. 이것은 JSON 모듈을 변경하지 않기 때문에 깨끗한 접근 방식이지만 원래 _make_iterencode에서 많은 코드를 반복하게됩니다. (이 접근 방식의 다른 변형에는 monkeypatching _make_iterencode 또는 하위 기능 _iterencode_dict가 포함됩니다).
2-JSON 모듈 소스 __debug__
를 변경 하고 상수를 사용하여 동작을 변경합니다.
def _iterencode(o, _current_indent_level):
...
for_json = _for_json and getattr(o, 'for_json', None)
if for_json and callable(for_json):
...
elif isinstance(o, list):
...
## added code below
elif __debug__:
o = _default(o)
for chunk in _iterencode(o, _current_indent_level):
yield chunk
## added code above
else:
...
이상적으로 JSONEncoder 클래스는 "모든 유형에 기본값 사용"을 지정하는 매개 변수를 제공하지만 그렇지 않습니다. 위의 내용은 원하는 작업을 수행하는 간단한 일회성 변경입니다.
아래를 시도하십시오. 원하는 출력을 생성하고 비교적 간단하게 보입니다. 인코더 클래스와의 유일한 차이점은 디코딩 및 인코딩 메서드를 모두 재정의해야한다는 것입니다 (인코더가 처리하는 방법을 알고있는 유형에 대해 후자가 여전히 호출되기 때문입니다).
import json
import datetime
class JSONDebugEncoder(json.JSONEncoder):
# transform objects known to JSONEncoder here
def encode(self, o, *args, **kw):
for_json = o
if isinstance(o, mDict):
for_json = { 'orig' : o, 'attrs' : vars(o) }
elif isinstance(o, mList):
for_json = { 'orig' : o, 'attrs' : vars(o) }
return super(JSONDebugEncoder, self).encode(for_json, *args, **kw)
# handle objects not known to JSONEncoder here
def default(self, o, *args, **kw):
if isinstance(o, datetime.datetime):
return o.isoformat()
else:
return super(JSONDebugEncoder, self).default(o, *args, **kw)
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(test_json,cls=JSONDebugEncoder))
if __name__ == '__main__':
test_debug_json()
인코더에 전달할 새 개체 유형을 만들 수없는 이유는 무엇입니까? 시험:
class MStuff(object):
def __init__(self, content):
self.content = content
class mDict(MStuff):
pass
class mList(MStuff):
pass
def json_debug_handler(obj):
print("object received:")
print(type(obj))
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,MStuff):
attrs = {}
for key in obj.__dict__:
if not ( key.startswith("_") or key == "content"):
attrs[key] = obj.__dict__[key]
return {'orig':obj.content , 'attrs': attrs}
else:
return None
원하는 경우 mDict 및 mList에 유효성 검사를 추가 할 수 있습니다.
재정의하도록__instancecheck__
정의 하는 경우 :
def strict_check(builtin):
'''creates a new class from the builtin whose instance check
method can be overridden to renounce particular types'''
class BuiltIn(type):
def __instancecheck__(self, other):
print 'instance', self, type(other), other
if type(other) in strict_check.blacklist:
return False
return builtin.__instancecheck__(other)
# construct a class, whose instance check method is known.
return BuiltIn('strict_%s' % builtin.__name__, (builtin,), dict())
# for safety, define it here.
strict_check.blacklist = ()
다음 json.encoder
과 같이 패치 하여 재정의하십시오_make_iterencode.func_defaults
.
# modify json encoder to use some new list/dict attr.
import json.encoder
# save old stuff, never know when you need it.
old_defaults = json.encoder._make_iterencode.func_defaults
old_encoder = json.encoder.c_make_encoder
encoder_defaults = list(json.encoder._make_iterencode.func_defaults)
for index, default in enumerate(encoder_defaults):
if default in (list, dict):
encoder_defaults[index] = strict_check(default)
# change the defaults for _make_iterencode.
json.encoder._make_iterencode.func_defaults = tuple(encoder_defaults)
# disable C extension.
json.encoder.c_make_encoder = None
... 귀하의 예제는 거의 그대로 작동합니다.
import datetime
import json
def json_debug_handler(obj):
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
# degrade obj to more primitive dict()
# to avoid cycles in the encoding.
return {'orig': dict(obj) , 'attrs': vars(obj)}
elif isinstance(obj,mList):
# degrade obj to more primitive list()
# to avoid cycles in the encoding.
return {'orig': list(obj), 'attrs': vars(obj)}
else:
return None
class mDict(dict):
pass
class mList(list):
pass
# set the stuff we want to process differently.
strict_check.blacklist = (mDict, mList)
def test_debug_json():
global test_json
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(test_json,default=json_debug_handler))
if __name__ == '__main__':
test_debug_json()
변경해야 할 것은주기가 없는지 확인하는 것이 었습니다.
elif isinstance(obj,mDict):
# degrade obj to more primitive dict()
# to avoid cycles in the encoding.
return {'orig': dict(obj) , 'attrs': vars(obj)}
elif isinstance(obj,mList):
# degrade obj to more primitive list()
# to avoid cycles in the encoding.
return {'orig': list(obj), 'attrs': vars(obj)}
전에 어딘가에 추가하십시오 test_debug_json
.
# set the stuff we want to process differently.
strict_check.blacklist = (mDict, mList)
다음은 내 콘솔 출력입니다.
>>> test_debug_json()
instance <class '__main__.strict_list'> <type 'dict'> {'date': datetime.datetime(2013, 7, 17, 12, 4, 40, 950637), 'games': ['mario', 'contra', 'tetris'], 'scores': {'pk': 45, 'dp': 10}}
instance <class '__main__.strict_dict'> <type 'dict'> {'date': datetime.datetime(2013, 7, 17, 12, 4, 40, 950637), 'games': ['mario', 'contra', 'tetris'], 'scores': {'pk': 45, 'dp': 10}}
instance <class '__main__.strict_list'> <type 'datetime.datetime'> 2013-07-17 12:04:40.950637
instance <class '__main__.strict_dict'> <type 'datetime.datetime'> 2013-07-17 12:04:40.950637
instance <class '__main__.strict_list'> <type 'datetime.datetime'> 2013-07-17 12:04:40.950637
instance <class '__main__.strict_dict'> <type 'datetime.datetime'> 2013-07-17 12:04:40.950637
object received:
<type 'datetime.datetime'>
instance <class '__main__.strict_list'> <class '__main__.mList'> ['mario', 'contra', 'tetris']
instance <class '__main__.strict_dict'> <class '__main__.mList'> ['mario', 'contra', 'tetris']
instance <class '__main__.strict_list'> <class '__main__.mList'> ['mario', 'contra', 'tetris']
instance <class '__main__.strict_dict'> <class '__main__.mList'> ['mario', 'contra', 'tetris']
object received:
<class '__main__.mList'>
instance <class '__main__.strict_list'> <type 'dict'> {'attrs': {'src': 'console'}, 'orig': ['mario', 'contra', 'tetris']}
instance <class '__main__.strict_dict'> <type 'dict'> {'attrs': {'src': 'console'}, 'orig': ['mario', 'contra', 'tetris']}
instance <class '__main__.strict_list'> <type 'dict'> {'src': 'console'}
instance <class '__main__.strict_dict'> <type 'dict'> {'src': 'console'}
instance <class '__main__.strict_list'> <type 'list'> ['mario', 'contra', 'tetris']
instance <class '__main__.strict_list'> <class '__main__.mDict'> {'pk': 45, 'dp': 10}
instance <class '__main__.strict_dict'> <class '__main__.mDict'> {'pk': 45, 'dp': 10}
instance <class '__main__.strict_list'> <class '__main__.mDict'> {'pk': 45, 'dp': 10}
instance <class '__main__.strict_dict'> <class '__main__.mDict'> {'pk': 45, 'dp': 10}
object received:
<class '__main__.mDict'>
instance <class '__main__.strict_list'> <type 'dict'> {'attrs': {'processed': 'unprocessed'}, 'orig': {'pk': 45, 'dp': 10}}
instance <class '__main__.strict_dict'> <type 'dict'> {'attrs': {'processed': 'unprocessed'}, 'orig': {'pk': 45, 'dp': 10}}
instance <class '__main__.strict_list'> <type 'dict'> {'processed': 'unprocessed'}
instance <class '__main__.strict_dict'> <type 'dict'> {'processed': 'unprocessed'}
instance <class '__main__.strict_list'> <type 'dict'> {'pk': 45, 'dp': 10}
instance <class '__main__.strict_dict'> <type 'dict'> {'pk': 45, 'dp': 10}
{"date": "2013-07-17T12:04:40.950637", "games": {"attrs": {"src": "console"}, "orig": ["mario", "contra", "tetris"]}, "scores": {"attrs": {"processed": "unprocessed"}, "orig": {"pk": 45, "dp": 10}}}
길을 바꿀 수 있다면 json.dumps
불린다. JSON 인코더가 손을 잡기 전에 필요한 모든 처리를 수행 할 수 있습니다. 이 버전은 어떤 종류의 복사도 사용하지 않으며 구조를 내부에서 편집합니다. copy()
필요한 경우 추가 할 수 있습니다 .
import datetime
import json
import collections
def json_debug_handler(obj):
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, collections.Mapping):
for key, value in obj.iteritems():
if isinstance(value, (collections.Mapping, collections.MutableSequence)):
value = json_debug_handler(value)
obj[key] = convert(value)
elif isinstance(obj, collections.MutableSequence):
for index, value in enumerate(obj):
if isinstance(value, (collections.Mapping, collections.MutableSequence)):
value = json_debug_handler(value)
obj[index] = convert(value)
return obj
def convert(obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return obj
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "qunprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(json_debug_handler(test_json)))
if __name__ == '__main__':
test_debug_json()
당신은 전화를 json_debug_handler
당신이 그것을 전달하기 전에 직렬화 된 객체 json.dumps
. 이 패턴을 사용하면 변경 사항을 쉽게 되돌 리거나 추가 변환 규칙을 추가 할 수도 있습니다.
편집하다:
json.dumps
호출 방법을 변경할 수없는 경우 원하는 작업을 수행하기 위해 언제든지 monkeypatch 할 수 있습니다. 이렇게하는 것과 같은 :
json.dumps = lambda obj, *args, **kwargs: json.dumps(json_debug_handler(obj), *args, **kwargs)
우리 test_json
는 당신의 요구 사항에 적합하도록 사전 처리 할 수 있습니까 ? 쓸모없는 Encode를 작성하는 것보다 파이썬 사전을 조작하는 것이 더 쉽습니다.
import datetime
import json
class mDict(dict):
pass
class mList(list):
pass
def prepare(obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj, mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj, mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return obj
def preprocessor(toJson):
ret ={}
for key, value in toJson.items():
ret[key] = prepare(value)
return ret
if __name__ == '__main__':
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games, 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(preprocessor(test_json)))
test_debug_json()
JSONEncoder.encode () 를 재정의 할 수 있어야합니다 .
class MyEncoder(JSONEncoder):
def encode(self, o):
if isinstance(o, dict):
# directly call JSONEncoder rather than infinite-looping through self.encode()
return JSONEncoder.encode(self, {'orig': o, 'attrs': vars(o)})
elif isinstance(o, list):
return JSONEncoder.encode(self, {'orig': o, 'attrs': vars(o)})
else:
return JSONEncoder.encode(self, o)
그런 다음 패치 하려면 http://docs.buildbot.net/latest/reference/json-pysrc.htmljson.dumps
에서 .NETjson._default_encoder
인스턴스 로 교체해야합니다 MyEncoder
.
직렬화 만 찾고 deserialization하지 않는 경우으로 보내기 전에 개체를 처리 할 수 있습니다 json.dumps
. 아래 예 참조
import datetime
import json
def is_inherited_from(obj, objtype):
return isinstance(obj, objtype) and not type(obj).__mro__[0] == objtype
def process_object(data):
if isinstance(data, list):
if is_inherited_from(data, list):
return process_object({"orig": list(data), "attrs": vars(data)})
new_data = []
for d in data:
new_data.append(process_object(d))
elif isinstance(data, tuple):
if is_inherited_from(data, tuple):
return process_object({"orig": tuple(data), "attrs": vars(data)})
new_data = []
for d in data:
new_data.append(process_object(d))
return tuple(new_data)
elif isinstance(data, dict):
if is_inherited_from(data, dict):
return process_object({"orig": list(data), "attrs": vars(data)})
new_data = {}
for k, v in data.items():
new_data[k] = process_object(v)
else:
return data
return new_data
def json_debug_handler(obj):
print("object received:")
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario', 'contra', 'tetris'])
games.src = 'console'
scores = mDict({'dp': 10, 'pk': 45})
scores.processed = "unprocessed"
test_json = {'games': games, 'scores': scores, 'date': datetime.datetime.now()}
new_object = process_object(test_json)
print(json.dumps(new_object, default=json_debug_handler))
if __name__ == '__main__':
test_debug_json()
동일한 출력은
{ "games": { "orig": [ "mario", "contra", "tetris"], "attrs": { "src": "console"}}, "scores": { "orig": [ " dp ","pk "],"attrs ": {"processed ":"unprocessed "}},"date ":"2018-01-24T12 : 59 : 36.581689 "}
JSONEncoder를 재정의 할 수도 있지만 중첩 된 메서드를 사용하기 때문에 복잡하고 아래에서 설명하는 기술이 필요합니다.
클로저로 중첩 된 함수를 * 단지 * 패치 할 수 있습니까? 아니면 전체 외부 함수를 반복해야합니까?
당신이 일을 단순하게 유지하고 싶기 때문에 나는 그 길로가는 것을 제안하지 않을 것입니다
FastTurtle의 제안에 따라 다소 적은 코드와 훨씬 더 깊은 원숭이 작업이 필요하면 isinstance
전역 적으로 자신 을 재정의 할 수 있습니다 . 이것은 아마도 좋은 생각이 아니며 무언가를 깨뜨릴 수도 있습니다. 그러나 필요한 출력을 생성한다는 점에서 작동하며 매우 간단합니다.
첫째, json을 어디로 든 가져 오기 전에isinstance
특정 컨텍스트에서만 약간만 있는 내장 모듈로 대체하기 위해 내장 모듈을 원숭이 패치합니다 .
_original_isinstance = isinstance
def _isinstance(obj, class_or_tuple):
if '_make_iterencode' in globals():
if not _original_isinstance(class_or_tuple, tuple):
class_or_tuple = (class_or_tuple,)
for custom in mList, mDict:
if _original_isinstance(obj, custom):
return custom in class_or_tuple
return _original_isinstance(obj, class_or_tuple)
try:
import builtins # Python 3
except ImportError:
import __builtin__ as builtins # Python 2
builtins.isinstance = _isinstance
그런 다음 사용자 지정 인코더를 만들어 사용자 지정 직렬화를 구현하고 강제로 사용하도록합니다 _make_iterencode
(c 버전은 monkeypatching의 영향을받지 않기 때문입니다).
class CustomEncoder(json.JSONEncoder):
def iterencode(self, o, _one_shot = False):
return super(CustomEncoder, self).iterencode(o, _one_shot=False)
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':dict(obj) , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':list(obj), 'attrs': vars(obj)}
else:
return None
그리고 그게 전부입니다! 아래 Python 3 및 Python 2의 출력.
Python 3.6.3 (default, Oct 10 2017, 21:06:48)
...
>>> from test import test_debug_json
>>> test_debug_json()
{"games": {"orig": ["mario", "contra", "tetris"], "attrs": {"src": "console"}}, "scores": {"orig": {"dp": 10, "pk": 45}, "attrs": {"processed": "unprocessed"}}, "date": "2018-01-27T13:56:15.666655"}
Python 2.7.13 (default, May 9 2017, 12:06:13)
...
>>> from test import test_debug_json
>>> test_debug_json()
{"date": "2018-01-27T13:57:04.681664", "games": {"attrs": {"src": "console"}, "orig": ["mario", "contra", "tetris"]}, "scores": {"attrs": {"processed": "unprocessed"}, "orig": {"pk": 45, "dp": 10}}}
기본 리졸버 우선 순위를 변경하고 기본 반복기 출력을 변경하여 목적을 달성하려고합니다.
모든 표준 유형 확인 전에 실행되는 기본 확인자 우선 순위를 변경합니다.
json.JSONEncoder를 상속하고 메서드를 재정의합니다
iterencode()
.모든 값은 ValueWrapper 유형 으로 래핑되어야하며 값이 기본 표준 확인자에 의해 확인되지 않도록해야합니다.
기본 반복기 출력을 변경하십시오.
세 가지 사용자 지정 래퍼 클래스 ValueWrapper , ListWrapper 및 DictWrapper를 구현 합니다. ListWrapper 구현
__iter__()
하고 DictWrapper 구현__iter__()
,items()
및iteritems()
.
import datetime
import json
class DebugJsonEncoder(json.JSONEncoder):
def iterencode(self, o, _one_shot=False):
default_resolver = self.default
# Rewrites the default resolve, self.default(), with the custom resolver.
# It will process the Wrapper classes
def _resolve(o):
if isinstance(o, ValueWrapper):
# Calls custom resolver precede others. Due to the _make_iterencode()
# call the custom resolver following by all standard type verifying
# failed. But we want custom resolver can be executed by all standard
# verifying.
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L442
result = default_resolver(o.data)
if (o.data is not None) and (result is not None):
return result
elif isinstance(o.data, (list, tuple)):
return ListWrapper(o.data)
elif isinstance(o.data, dict):
return DictWrapper(o.data)
else:
return o.data
else:
return default_resolver(o)
# re-assign the default resolver self.default with custom resolver.
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L161
self.default = _resolve
# The input value must be wrapped by ValueWrapper, avoid the values are
# resolved by the standard resolvers.
# The last one arguemnt _one_shot must be False, we want to encode with
# _make_iterencode().
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L259
return json.JSONEncoder.iterencode(self, _resolve(ValueWrapper(o)), False)
class ValueWrapper():
"""
a wrapper wrapped the given object
"""
def __init__(self, o):
self.data = o
class ListWrapper(ValueWrapper, list):
"""
a wrapper wrapped the given list
"""
def __init__(self, o):
ValueWrapper.__init__(self, o)
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L307
def __iter__(self):
for chunk in self.data:
yield ValueWrapper(chunk)
class DictWrapper(ValueWrapper, dict):
"""
a wrapper wrapped the given dict
"""
def __init__(self, d):
dict.__init__(self, d)
def __iter__(self):
for key, value in dict.items(self):
yield key, ValueWrapper(value)
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L361
def items(self):
for key, value in dict.items(self):
yield key, ValueWrapper(value)
# see https://github.com/python/cpython/blob/2.7/Lib/json/encoder.py#L363
def iteritems(self):
for key, value in dict.iteritems(self):
yield key, ValueWrapper(value)
def json_debug_handler(obj):
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return None
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now(), 'default': None}
print(json.dumps(test_json,cls=DebugJsonEncoder,default=json_debug_handler))
if __name__ == '__main__':
test_debug_json()
'developer tip' 카테고리의 다른 글
업로드하기 전에 브라우저에서 이미지 자르기 (0) | 2020.12.13 |
---|---|
CSS 변환이 인라인 요소에서 작동하지 않습니다. (0) | 2020.12.13 |
adb에서 인식되지 않는 Android 기기 (0) | 2020.12.13 |
Chrome 인증서 / HSTS 오류를 우회하기 위해 'badidea'또는 'thisisunsafe'를 사용하는 것은 현재 사이트에만 적용 되나요? (0) | 2020.12.13 |
.NET 4.0의 주요 변경 사항 (0) | 2020.12.13 |