如何让json.dumps()以科学计数法序列化整数与浮点数?
解决json.dumps()重载整数/浮点数序列化的问题
你遇到的核心问题是:json.JSONEncoder的default()方法只在处理默认编码器认不出来的类型时才会触发,而int、float是JSON原生支持的基础类型,所以你写的ScientificNotationNumbers里的逻辑根本不会被调用。
下面给你三种可行的解决方法:
方案一:先预处理数据,把数字转成科学计数法字符串
先递归遍历你的数据结构(不管是字典还是嵌套的列表、对象属性),把所有int/float转成指定格式的字符串,再用json.dumps()序列化就行:
import json from typing import Any def convert_numbers_to_sci(data: Any) -> Any: if isinstance(data, dict): return {k: convert_numbers_to_sci(v) for k, v in data.items()} elif isinstance(data, list): return [convert_numbers_to_sci(item) for item in data] elif isinstance(data, (int, float)): return "{:.2e}".format(data) else: return data # 你的类用法示例 class YourClass: def __init__(self): self.data = {"big_num": 123456789, "small_num": 0.0000123, "text": "示例文本"} def __str__(self) -> str: # 先转换当前对象的属性字典 processed_data = convert_numbers_to_sci(vars(self)) return json.dumps(processed_data, indent=4, ensure_ascii=False) obj = YourClass() print(obj)
方案二:重写编码器的encode方法,把转换逻辑封装进去
把数据转换逻辑直接写在自定义编码器的encode()方法里,这样调用方式和你原来的写法完全一致,不用额外处理数据:
import json from typing import Any class ScientificNotationNumbers(json.JSONEncoder): def encode(self, obj: Any) -> str: # 内部递归处理数字类型 def convert(item): if isinstance(item, (int, float)): return "{:.2e}".format(item) elif isinstance(item, dict): return {k: convert(v) for k, v in item.items()} elif isinstance(item, list): return [convert(i) for i in item] else: return item processed_obj = convert(obj) return super().encode(processed_obj) # 你的类用法示例 class YourClass: def __init__(self): self.data = {"big_num": 123456789, "small_num": 0.0000123, "text": "示例文本"} def __str__(self) -> str: return json.dumps(self, indent=4, ensure_ascii=False, cls=ScientificNotationNumbers) obj = YourClass() print(obj)
方案三:重写编码器的_iterencode方法,对接底层逻辑
如果想更贴近编码器的原生工作流程,可以重写_iterencode()方法,直接拦截数字类型的序列化过程:
import json from typing import Any, Iterator class ScientificNotationNumbers(json.JSONEncoder): def _iterencode(self, obj: Any, markers=None) -> Iterator[str]: if isinstance(obj, (int, float)): # 注意要加引号,因为要转成JSON字符串类型 yield "\"{:.2e}\"".format(obj) else: # 其他类型交给父类处理 yield from super()._iterencode(obj, markers) # 你的类用法示例 class YourClass: def __init__(self): self.data = {"big_num": 123456789, "small_num": 0.0000123, "text": "示例文本"} def __str__(self) -> str: return json.dumps(self, indent=4, ensure_ascii=False, cls=ScientificNotationNumbers) obj = YourClass() print(obj)
三种方案的特点
- 方案一逻辑简单直白,适合数据结构不复杂的场景,不用修改编码器。
- 方案二把转换逻辑封装在编码器内部,保持调用方式不变,符合面向对象的封装思路。
- 方案三直接对接编码器的底层迭代逻辑,效率稍高,但要注意手动拼接JSON字符串的引号(因为要把数字转成字符串类型)。
内容的提问来源于stack exchange,提问作者UsayHi
相关产品推荐
相关产品推荐

