You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 03:23:31