python - 在 JSON 序列化期间仅为部分数据自定义格式
问题描述
我有一个要序列化的 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
's 的内容,以便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
。
我确实找到了几种 解决看似相似问题的方法,但是那里提出的解决方案看起来太怪异,而且对于像我这样的任务来说过于复杂。有没有办法只用一两打代码就可以实现我需要的东西,而不会深入到JSONEncoder
's 的肠子里?它不需要是一个通用的解决方案,只是我可以快速修复并且可能仅适用于单个案例的东西。
解决方案
实际上,你可以做这样的事情,但它是......你看它的样子。
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"}
}
推荐阅读
- jquery - 如何从 jQuery 中添加的输入字段中获取值
- python - 在 IE 浏览器的 python 中的 selenium 中设置代理
- sql - SQL:选择一个分类值并排除其他值
- java - 这是进行冒泡排序的正确方法吗?
- python - 将包含 unicodes 的 Pandas 字符串列转换为 ascii 以加载 url
- javascript - Javascript - 模式选择选项(无法选择模式中的项目)
- python - 在 pd.read.sql 中使用变量
- php - 在 CodeIgniter 中查看谁在同一页面上
- java - 外包 Junit Before 方法
- xml - XSLT 按优先级在相同节点之间进行选择