因为这里真正的问题是
json.dumps
默认编码器无法考虑
MutableMapping
(或
ruamel.yaml.comments.CommentedMap
在你的现实世界的例子)作为一个口述,而不是告诉人们设置
default
json.dumps文件
给你的
json_default
就像你提到的那样,你可以使用
functools.partial
使
json默认
违约
json.dumps文件
这样人们在使用您的软件包时就不必做任何不同的事情:
from functools import partial
json.dumps = partial(json.dumps, default=json_default)
或者如果你需要允许人们指定他们自己的
违约
参数,甚至他们自己的
json.JSONEncoder
子类,可以使用包装器
json.dumps文件
所以它把
由指定的函数
违约
参数和
违约
方法指定的自定义编码器的
cls
参数,无论指定哪一个:
import inspect
class override_json_default:
# keep track of the default methods that have already been wrapped
# so we don't wrap them again
_wrapped_defaults = set()
def __call__(self, func):
def override_default(default_func):
def default_wrapper(o):
o = default_func(o)
if isinstance(o, MutableMapping):
o = dict(o)
return o
return default_wrapper
def override_default_method(default_func):
def default_wrapper(self, o):
try:
return default_func(self, o)
except TypeError:
if isinstance(o, MutableMapping):
return dict(o)
raise
return default_wrapper
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
bound.apply_defaults()
default = bound.arguments.get('default')
if default:
bound.arguments['default'] = override_default(default)
encoder = bound.arguments.get('cls')
if not default and not encoder:
bound.arguments['cls'] = encoder = json.JSONEncoder
if encoder:
default = getattr(encoder, 'default')
if default not in self._wrapped_defaults:
default = override_default_method(default)
self._wrapped_defaults.add(default)
setattr(encoder, 'default', default)
return func(*bound.args, **bound.kwargs)
sig = inspect.signature(func)
return wrapper
json.dumps=override_json_default()(json.dumps)
所以下面的测试代码
违约
函数和处理
datetime
对象,以及没有自定义的对象
违约
或编码器:
from datetime import datetime
def datetime_encoder(o):
if isinstance(o, datetime):
return o.isoformat()
return o
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()
return super(DateTimeEncoder, self).default(o)
def dump(data):
print(list(data.items()))
try:
print('cast:', dict(**data))
except Exception as e:
print('ERROR:', e)
try:
print('json with custom default:', json.dumps(data, default=datetime_encoder))
print('json wtih custom encoder:', json.dumps(data, cls=DateTimeEncoder))
del data['c']
print('json without datetime:', json.dumps(data))
except Exception as e:
print('ERROR:', e)
t = T(a=1, b=2, c=datetime.now())
dump(t)
都能给出正确的输出:
[('a', 1), ('b', 2), ('c', datetime.datetime(2018, 9, 15, 23, 59, 25, 575642)), ('default', 'DEFAULT')]
cast: {'a': 1, 'b': 2, 'c': datetime.datetime(2018, 9, 15, 23, 59, 25, 575642), 'default': 'DEFAULT'}
json with custom default: {"a": 1, "b": 2, "c": "2018-09-15T23:59:25.575642", "default": "DEFAULT"}
json wtih custom encoder: {"a": 1, "b": 2, "c": "2018-09-15T23:59:25.575642", "default": "DEFAULT"}
json without datetime: {"a": 1, "b": 2, "default": "DEFAULT"}
正如注释中指出的,上面的代码使用
inspect.signature
,直到Python3.3才可用,即使如此,
inspect.BoundArguments.apply_defaults
funcsigs
包,Python3.3的一个后台端口
检查签名
,没有
apply_defaults
方法也一样。为了使代码尽可能向后兼容,只需复制并粘贴Python3.5+的代码
inspect.BoundArguments.apply_defaults
并将其作为
inspect.BoundArguments
导入后
必要时:
from collections import OrderedDict
if not hasattr(inspect, 'signature'):
import funcsigs
for attr in funcsigs.__all__:
setattr(inspect, attr, getattr(funcsigs, attr))
if not hasattr(inspect.BoundArguments, 'apply_defaults'):
def apply_defaults(self):
arguments = self.arguments
new_arguments = []
for name, param in self._signature.parameters.items():
try:
new_arguments.append((name, arguments[name]))
except KeyError:
if param.default is not funcsigs._empty:
val = param.default
elif param.kind is funcsigs._VAR_POSITIONAL:
val = ()
elif param.kind is funcsigs._VAR_KEYWORD:
val = {}
else:
continue
new_arguments.append((name, val))
self.arguments = OrderedDict(new_arguments)
inspect.BoundArguments.apply_defaults = apply_defaults