如何修改Python的JSON编码器将NaN转换为None
问题描述
当使用Python 3.11的json模块编码包含NaN的对象时,会生成无效的JSON字面量NaN:
import json not_a_number = float("NaN") # 该值实际来自外部输入 json.dumps([not_a_number]) # 输出无效JSON:"[NaN]"
尝试通过子类化JSONEncoder处理NaN,最初重写default()方法无效(因为float是已知类型,不会触发default());后续重写encode()方法仅对顶层单个NaN有效,无法处理嵌套在列表、字典中的NaN:
import math import json class NanEncoder(json.JSONEncoder): def encode(self, obj): if isinstance(obj, float): if math.isnan(obj): return None return super(NanEncoder, self).encode(obj) # 单个NaN有效 not_a_number = float("NaN") print(json.dumps(not_a_number, cls=NanEncoder)) # 输出: null # 列表中的NaN无效 not_a_number_list = [not_a_number] print(json.dumps(not_a_number_list, cls=NanEncoder)) # 输出: "[NaN]"
需求:在无法从源头避免NaN混入的情况下,能否通过JSONEncoder子类将所有NaN转换为null(对应Python的None)?避免直接替换JSON字符串中的NaN(可能误改其他数据)。
解决方案
可以通过先递归遍历并预处理对象,替换所有NaN为None,再执行编码的方式实现JSONEncoder子类。这种方法能处理所有嵌套层级的NaN,且不会误改数据:
import json import math def _replace_nan(obj): """递归遍历对象,将所有NaN/Inf转换为None""" if isinstance(obj, float): if math.isnan(obj) or math.isinf(obj): return None elif isinstance(obj, list): return [_replace_nan(item) for item in obj] elif isinstance(obj, dict): return {key: _replace_nan(value) for key, value in obj.items()} elif isinstance(obj, tuple): return tuple(_replace_nan(item) for item in obj) # 可根据需要扩展其他容器类型(如set等) return obj class NanEncoder(json.JSONEncoder): def encode(self, obj): # 先预处理对象,替换所有NaN processed_obj = _replace_nan(obj) return super().encode(processed_obj)
测试验证
# 测试单个NaN not_a_number = float("NaN") print(json.dumps(not_a_number, cls=NanEncoder)) # 输出: "null" # 测试嵌套结构 nested_data = [ float("NaN"), 1.5, {"name": "test", "value": float("NaN")}, [2.0, float("inf"), float("-inf")] ] print(json.dumps(nested_data, cls=NanEncoder)) # 输出: "[null, 1.5, {"name": "test", "value": null}, [2.0, null, null]]"
原理说明
- 之前的
encode()方法仅处理顶层对象,当对象是列表/字典等容器时,父类的encode()会递归处理内部元素,但不会再次调用子类的encode()方法,因此嵌套的NaN无法被捕获。 - 预处理函数
_replace_nan会遍历整个对象结构,将所有NaN(包括正/负无穷大inf,同样是无效JSON值)替换为None,再交给默认的JSONEncoder编码为合法的null。
不推荐的方案
直接对JSON输出字符串执行replace("NaN", "null")存在风险:如果输入数据中包含字符串值"NaN"(比如某个字段内容就是字符串"NaN"),会被误替换为"null",破坏原始数据。
内容的提问来源于stack exchange,提问作者machomeautoguy
相关产品推荐
相关产品推荐

