如何更改可序列化python对象的json编码行为?
很容易改变不是JSON序列化对象的格式,例如datetime.datetime。
我的要求,为了debugging的目的,是改变一些自定义对象的扩展从基本的像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
处理程序不能用于可序列化的对象吗? 如果没有,我怎么能重写这个,而不增加JSON方法的扩展类?
另外,这个版本的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__,
然后使用json.dumps超过pickle.loads对象,我也打开,我试过了,但没有奏效。
看来,要实现你想要的行为,在给定的限制下,你必须深入研究一下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的答案可能是一个更干净的解决scheme。
这里是接近你想要的东西基于我的问题/答案中解释的技术: 覆盖inheritance的默认支持的对象,如字典,列表的嵌套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}}
对不起,任何错误的命名约定,这是一个快速的设置。 ;)
注意:date时间不会被解码回python表示。 实现这一点可以通过检查任何被称为'date'的字典关键字并且包含一个有效的date时间的string表示来完成。
正如其他人已经指出,默认处理程序只被调用的值不是公认的types之一。 我build议解决这个问题的方法是预处理你想要序列化的对象,recursion遍历列表,元组和字典,但是把每一个其他值包装在一个自定义的类中。
像这样的东西:
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))
请注意,此代码正在检查其类与列表,元组或字典完全匹配的对象,因此从这些types扩展的任何自定义对象将被打包而不是分析。 因此,常规列表,元组和字典将像往常一样被序列化,但所有其他值将被传递到默认处理程序。
所有这一切的最终结果是,到达默认处理程序的每个值都保证被包装在这些Debug类之一中。 所以你要做的第一件事是提取原始对象,如下所示:
obj = obj.originalObject
然后,您可以检查原始对象的types,并处理需要特殊处理的types。 对于其他的东西,你应该只返回原来的对象(所以处理程序的最后一个返回应该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
请注意,此代码不会检查不可序列化的值。 这些将通过最终return obj
,然后将被序列化程序拒绝,并再次返回到默认处理程序 – 只有这一次没有debugging包装。
如果你需要处理这种情况,你可以像这样在处理程序的顶部添加一个检查:
if not hasattr(obj, 'originalObject'): return None
Ideone演示: http ://ideone.com/tOloNq
默认函数只在被转储的节点不是本地可序列化的时候被调用,而你的mDict类是按原样序列化的。 这是一个小的演示,显示何时默认调用,什么时候不是:
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 ...
换句话说,只有在被编码的节点不是公认的基本types之一时,才会有一个硬连线的行为。 您可以通过以下几种方式之一来覆盖它:
1 – 子类JSONEncoder,正如你在上面所做的那样,但是为它的初始值设定项添加一个参数,指定要使用的函数来代替标准的_make_iterencode,在这个参数中添加一个testing,可以调用符合条件的类的默认值。 这是一个干净的方法,因为你并没有改变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类将提供一个参数来指定“使用所有types的默认值”,但是不会。 以上是一个简单的一次性更改,可以做你正在寻找的东西。
为什么你不能创build一个新的对象types传递给编码器? 尝试:
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上添加validation。
如果您定义这些来覆盖__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编码器上手之前完成所有处理。 此版本不使用任何forms的复制,并将就地编辑结构。 如果需要,您可以添加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.dumps
之前,您可以在要序列化的对象上调用json.dumps
。 使用这种模式,您还可以轻松地反转更改和/或添加额外的转换规则。
编辑:
如果你不能改变如何json.dumps
,你可以随时修改它来做你想做的事情。 比如这样做:
json.dumps = lambda obj, *args, **kwargs: json.dumps(json_debug_handler(obj), *args, **kwargs)
你应该能够覆盖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)
然后如果你想要补丁到json.dumps
它看起来像http://docs.buildbot.net/latest/reference/json-pysrc.html就像你需要用一个;MyEncoder
的实例replacejson._default_encoder
。
试试下面。 它产生你想要的输出,看起来相对简单。 与编码器类唯一真正的区别是,我们应该重写解码和编码方法(因为后者仍被称为编码器知道如何处理的types)。
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()
我有同样的问题,并find了快速龟答案的解决scheme。 正如我在多个项目中所要求的,我为此创build了一个名为jsonconversion的Python包,它是开源的 。 您可以直接使用它,而无需从这里复制源代码。