我有一个要序列化的Python对象树。它的结构是这样的:
{
"regular_object": {
...
},
"regular_field": ...
"special_object": {
"f1": "v1",
"f2": v2,
...
"fn": "vn"
}
}
我想序列化这样的对象树,使其以JSON表示形式进行格式化:
{
"regular_object": {
...
},
"regular_field": ...
"special_object": { "f1": "v1", "f2": v2, ..., "fn": "vn" }
}
换句话说,我想要基于被格式化对象的内容的混合格式。
我尝试通过覆盖json.JSONEncoder
方法和使用自定义类对象来存储default
的内容来使用special_object
的方法,以便调用自定义default
,就像这样:
class SpecialObject:
def __init__(self, attrs: dict):
self.attrs = attrs
class SpecialEncoder(json.JSONEncoder):
def default(self, o):
JE = json.JSONEncoder
if isinstance(o, SpecialObject):
return f"{{ ', '.join([f'{JE.encode(self, el[0])}: {JE.encode(el[1])}' for el in o.attrs.items()]) }}"
else:
return super().default(o)
但是看起来JSONEncoder
再次编码default
的返回值 ,所以我为自定义类型生成的字符串被序列化为字符串,并带有输出是这样的:
{
...
"special_object": "{ \"f1\": \"v1\", ..., \"fn\": \"vn\" }"
}
这显然不是我的初衷。我还尝试覆盖JSONEncoder.encode
方法,就像这里的一些建议一样,但是该方法从未在JSONEncoder
本身内部被调用。
我确实找到了several approaches来解决看似相似的问题,但是对于像我这样的任务,那里提出的解决方案看起来太怪异而且过于复杂。
有没有一种方法可以仅用十二行代码来实现我所需要的而又不深入JSONEncoder
的肠子?它不一定是通用的解决方案,而只是我可以快速解决的问题,并且只适用于单个情况。
答案 0 :(得分:1)
实际上,您可以执行类似的操作,但是您看到的是 ... 。
def custom_json_dumps(obj, *args, keys_to_skip_indent=(), **kwargs):
if isinstance(obj, dict):
indent = kwargs.pop("indent", 0)
separators = kwargs.get("separators", (", ", ": "))
res = "{" + "\n" * int(indent != 0)
for k, v in obj.items():
if k in keys_to_skip_indent or indent == 0:
encoded = json.dumps(v, *args, **kwargs)
else:
encoded = json.dumps(v, *args, indent=indent, **kwargs)
res += "\"{}\"".format(k) + separators[1] + encoded + separators[0] + "\n" * int(indent != 0)
return res[:res.rindex(separators[0])].replace("\n", "\n" + " " * indent) + "\n" * int(indent != 0) + "}"
else:
return json.dumps(obj, *args, **kwargs)
测试:
o = {
"regular_object": {
"a": "b"
},
"regular_field": 100000,
"float_test": 1.0000001,
"bool_test": True,
"list_test": ["1", 0, 1.32, {"a": "b"}],
"special_object": {
"f1": "v1",
"f2": "v2",
"fn": "vn"
}
}
print(custom_json_dumps(o, keys_to_skip_indent={"special_object", "list_test"}, indent=4))
输出:
{
"regular_object": {
"a": "b"
},
"regular_field": 100000,
"float_test": 1.0000001,
"bool_test": true,
"list_test": ["1", 0, 1.32, {"a": "b"}],
"special_object": {"f1": "v1", "f2": "v2", "fn": "vn"}
}