自定义json.dumps序列化行为:优化非标准JSON生成方案
更Pythonic的非标准JSON序列化实现
我有一个字典{"a": "hello", "b": b"list"}:
'a'是字符串类型'b'是字节字符串类型
我需要将该字典序列化为如下非标准JSON格式字符串:'{"a": "hello", "b": list}'
目前我写了下面的方法,虽然能正常运行,但用字符串替换的方式显得有点取巧:
def stringify(obj): def my(obj): if isinstance(obj,bytes): return "<::%s::>" % obj.decode() return json.dumps(obj, default=my).replace('"<::',"").replace('::>"',"")
(添加<::和::>是为了序列化后通过字符串替换得到预期结果)
我想知道有没有更符合Python风格的实现方式?
补充要求
重写后的stringify函数需要满足以下断言:
assert stringify( dict(a="hello",b=b"byte") ) == '{"a": "hello", "b": byte}' assert stringify( ["hello", b"world"] ) == '["hello", world]' assert stringify( "hello" ) == '"hello"' assert stringify( b"world" ) == "world"
方案一:自定义JSONEncoder子类(标准扩展方式)
Python的json模块提供了JSONEncoder基类,允许我们通过继承它来定制序列化逻辑,这是官方推荐的扩展方式,比字符串替换更优雅可靠。
实现代码如下:
import json class CustomByteEncoder(json.JSONEncoder): def encode(self, obj): # 单独处理最外层是字节串的情况 if isinstance(obj, bytes): return obj.decode() # 先按标准JSON序列化,再替换字节串的标记 standard_json = super().encode(obj) return standard_json.replace('"__BYTE_MARKER__:', '').replace(':__BYTE_MARKER__"', '') def default(self, obj): if isinstance(obj, bytes): # 给字节串添加唯一标记,避免和普通字符串混淆 return f'__BYTE_MARKER__:{obj.decode()}:__BYTE_MARKER__' # 其他类型交给父类处理 return super().default(obj) def stringify(obj): return CustomByteEncoder().encode(obj)
测试所有断言,均能通过:
assert stringify( dict(a="hello",b=b"byte") ) == '{"a": "hello", "b": byte}' assert stringify( ["hello", b"world"] ) == '["hello", world]' assert stringify( "hello" ) == '"hello"' assert stringify( b"world" ) == "world"
这种方式的优势在于:
- 遵循Python标准的JSON扩展规范,逻辑清晰易读
- 自定义标记
__BYTE_MARKER__可以有效避免和普通字符串内容冲突,比简单的<::更安全 - 后续可以轻松扩展其他自定义类型的序列化逻辑
方案二:递归遍历构建字符串(完全自定义)
如果不想依赖json.dumps的基础序列化,也可以直接递归遍历对象结构,逐个生成对应的字符串,完全掌控序列化流程:
import json def stringify(obj): if isinstance(obj, str): return json.dumps(obj) # 字符串直接用标准JSON转义处理 elif isinstance(obj, bytes): return obj.decode() # 字节串直接转成无引号的字符串 elif isinstance(obj, dict): # 遍历字典键值对,分别序列化后拼接 items = [] for key, value in obj.items(): key_str = json.dumps(key) val_str = stringify(value) items.append(f"{key_str}: {val_str}") return "{" + ", ".join(items) + "}" elif isinstance(obj, list): # 遍历列表元素,序列化后拼接 elements = [stringify(item) for item in obj] return "[" + ", ".join(elements) + "]" else: # 其他基础类型(数字、布尔、None)用标准JSON处理 return json.dumps(obj)
这个方案完全不需要字符串替换,直接根据每个元素的类型生成对应格式的字符串,所有断言同样可以通过。
优势在于:
- 序列化流程完全可控,没有中间步骤的字符串替换操作
- 每个类型的处理逻辑一目了然,便于调试和修改
- 不会出现标记符冲突的问题
内容的提问来源于stack exchange,提问作者manatlan
相关产品推荐
相关产品推荐

