详解 float32 不可 JSON 序列化的原因及处理方法
引言
在现代软件开发中,数据的序列化与反序列化是跨系统、跨语言数据交换的核心环节。JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,因其简洁、易读、易于解析的特性,已成为 Web 服务、前后端通信、配置文件等领域的事实标准。
然而,在使用 JSON 进行数据序列化时,开发者有时会遇到一个令人困惑的问题:为什么某些浮点数类型,特别是像 float32
这样的数据,在尝试序列化时会出错?更具体地说,为什么一些特殊的浮点数值(如 NaN
或 Infinity
)会导致序列化失败?本文将深入探讨 float32
(以及更广泛的浮点数类型)为何在某些情况下不可直接 JSON 序列化,以及我们应如何有效地处理这些问题。
我们将从 JSON 的数据类型规范入手,然后深入理解浮点数在计算机内部的表示(特别是 IEEE 754 标准),找出规范与实现之间的冲突点,最终详细介绍多种实用的处理方法。
第一部分:理解 JSON 规范与数据类型
要理解为什么浮点数会出现序列化问题,首先需要明确 JSON 本身的数据类型规范。JSON 规范(如 RFC 8259)定义了以下几种基本数据类型:
- 对象 (Object):无序的键值对集合。键是字符串,值可以是任何 JSON 数据类型。
- 数组 (Array):有序的值列表。值可以是任何 JSON 数据类型。
- 字符串 (String):Unicode 字符序列,用双引号括起来。
- 数字 (Number):表示十进制或指数形式的数字。
- 布尔值 (Boolean):
true
或false
。 - 空值 (Null):
null
。
这里的关键是数字 (Number) 类型。JSON 规范对数字的定义相对宽松,它描述了数字的文本表示形式,包括可选的负号、整数部分、可选的小数部分和可选的指数部分。例如,123
、123.45
、-1.2e+10
都是有效的 JSON 数字。
重要的是,JSON 规范并没有指定数字的内部存储格式或精度要求。它只定义了数字的文本表示。任何遵循 JSON 规范的解析器都应该能够解析这些文本格式的数字,并将其转换为相应的内部数值表示(如编程语言中的 int
、float
、double
或 Decimal
类型),但这个转换过程可能会涉及精度丢失或范围限制,这取决于具体的实现语言和平台。
此外,JSON 规范对数字类型有一项重要的隐含限制:它只能表示有限的、确定的数值。规范中没有为非数字(NaN – Not a Number)或无穷大(Infinity)定义标准的文本表示形式。
第二部分:理解浮点数在计算机中的表示 (IEEE 754)
与 JSON 抽象的数字表示不同,计算机中的浮点数通常采用 IEEE 754 标准来表示。这个标准定义了浮点数的二进制存储格式,包括符号位、指数位和尾数位。常见的浮点数类型有:
- float32 (单精度浮点数):使用 32 位(4 字节)存储。其中1位符号位,8位指数位,23位尾数位。
- float64 (双精度浮点数):使用 64 位(8 字节)存储。其中1位符号位,11位指数位,52位尾数位。这是 Python、Java、C# 等许多高级语言中默认的
float
或double
类型。
IEEE 754 标准不仅定义了如何表示常规的有限数值,还定义了如何表示一些特殊的数值:
- 零 (Zero):包括 +0 和 -0。
- 无穷大 (Infinity):表示超出浮点数表示范围的正无穷大 (+Infinity) 和负无穷大 (-Infinity)。这通常是由于除以零等操作产生的。
- 非数字 (NaN – Not a Number):表示无效或不可表示的计算结果,例如 0/0、平方根负数等。NaN 有多种形式(静默 NaN 和信令 NaN),但它们都表示“不是一个有效的数字”。
在 IEEE 754 标准中,这些特殊值都有特定的二进制表示模式:
- 无穷大 (Infinity):指数位的二进制全为 1,尾数位的二进制全为 0。符号位决定是 +Infinity 还是 -Infinity。
- 非数字 (NaN):指数位的二进制全为 1,尾数位的二进制不全为 0。符号位通常被忽略(NaN 既不大于也不小于任何数,包括它自身)。
例如,在 Python 中,你可以使用 float('inf')
或 math.inf
表示无穷大,使用 float('nan')
或 math.nan
表示 NaN。对于 float32
,这些特殊值同样存在,只是其二进制表示遵循 32 位的 IEEE 754 规则。
第三部分:冲突的根源:特殊浮点数与 JSON 规范
现在问题来了:JSON 规范中的数字类型只能表示有限的确切数值,而 IEEE 754 浮点数标准却可以表示 NaN
和 Infinity
这些特殊值。
当一个编程语言的 JSON 序列化库尝试将一个包含 NaN
或 Infinity
的浮点数值(无论是 float32
还是 float64
)转换为 JSON 字符串时,它必须遵循 JSON 规范。由于规范中没有定义 NaN
、Infinity
或 -Infinity
的标准文本表示形式,序列化库不知道应该生成什么样的字符串来代表这些值,并且确保这个字符串能被标准的 JSON 解析器正确理解为这些特殊数值。
如果序列化库直接将这些特殊值输出为它们在编程语言中的字符串表示(例如 Python 中的 "nan"
或 "inf"
),那么生成的 JSON 字符串将是这样的:
json
{
"value": nan, // 这是无效的 JSON
"another_value": Infinity // 这也是无效的 JSON
}
或者,如果将其放在字符串引号中:
json
{
"value": "nan", // 这是有效的 JSON,但 "nan" 是字符串,不是数字
"another_value": "Infinity" // 这是有效的 JSON,但 "Infinity" 是字符串,不是数字
}
第一种情况直接违反了 JSON 数字的格式规范,导致序列化失败或生成非标准的 JSON 字符串,大多数严格遵循规范的解析器会拒绝解析这样的字符串。第二种情况虽然生成了有效的 JSON 字符串,但 NaN
和 Infinity
被表示为普通的字符串 "nan"
和 "Infinity"
。当这个 JSON 字符串被反序列化时,接收端会得到字符串而不是特殊的浮点数值。接收端需要额外的逻辑来检测这些特定的字符串并将其转换回 NaN
或 Infinity
,这增加了系统的复杂性和耦合性,并且容易出错(例如,如果一个合法的字符串恰好是 "nan"
怎么办?)。
因此,为了保证生成的 JSON 字符串严格符合规范,并且能够被任何标准的 JSON 解析器可靠地解析,大多数 JSON 序列化库在遇到 NaN
、Infinity
或 -Infinity
时,会选择抛出错误,而不是生成非标准的 JSON 或将特殊值转换为普通字符串。
这就是 float32
(以及包含这些特殊值的任何浮点数)在默认情况下不可直接 JSON 序列化的根本原因:JSON 规范的数字类型不足以表示 IEEE 754 浮点数中的所有可能值,特别是 NaN
和 Infinity
。
虽然这个问题不是 float32
特有的,但它在科学计算、机器学习等领域中更容易遇到,因为这些领域经常使用 float32
来节省内存和计算资源,并且计算结果更容易产生 NaN
或 Infinity
(例如,梯度爆炸、无效的数学操作等)。
第四部分:处理 float32 (及其他浮点数) 序列化问题的方法
由于我们不能改变 JSON 规范,也不能改变浮点数的内部表示,我们只能在序列化过程中介入,对可能导致问题的特殊浮点数值进行特殊处理。以下是几种常用的处理方法,从简单到复杂,各有优缺点:
方法一:在序列化前过滤或替换特殊值
这是最直接的方法。在将数据传递给 JSON 序列化库之前,遍历数据结构,检测所有的浮点数,如果遇到 NaN
或 Infinity
,则将其替换为 JSON 规范中允许的其他值。
常用的替换方案:
- 替换为
null
: 这是最常见且推荐的做法。null
在 JSON 中有明确的定义,并且通常可以表示“缺失”、“无效”或“不适用”的数据状态,这与NaN
和Infinity
的某些语义(如无效计算结果、超出范围)是契合的。 - 替换为特定的字符串: 将
NaN
替换为"NaN"
,Infinity
替换为"Infinity"
,-Infinity
替换为"-Infinity"
。这保留了特殊值的身份信息,但如前所述,接收端需要特别处理这些字符串。
实现示例 (Python):
假设我们有一个包含 NaN
和 Infinity
的字典,我们想将它序列化。
“`python
import json
import math
data_with_float = {
“name”: “test_data”,
“value1”: 1.23,
“value2”: float(‘nan’), # 这是一个 NaN
“value3”: float(‘inf’), # 这是一个 Infinity
“value4”: -float(‘inf’), # 这是一个 -Infinity
“value5”: 0.0,
“value6”: 42,
“nested”: {
“sub_value”: float(‘nan’)
}
}
方法 1a: 替换为 null
def replace_nan_inf_with_null(data):
if isinstance(data, float):
if math.isnan(data) or math.isinf(data):
return None # 将 NaN/Infinity 替换为 None (Python 中的 null)
else:
return data
elif isinstance(data, dict):
return {k: replace_nan_inf_with_null(v) for k, v in data.items()}
elif isinstance(data, list):
return [replace_nan_inf_with_null(item) for item in data]
else:
return data
processed_data_null = replace_nan_inf_with_null(data_with_float)
try:
json_output_null = json.dumps(processed_data_null, indent=2)
print(“— Replaced with null —“)
print(json_output_null)
except ValueError as e:
print(f”Serialization failed even after replacing with null: {e}”)
print(“\n” + “=”*30 + “\n”)
方法 1b: 替换为特定字符串
def replace_nan_inf_with_string(data):
if isinstance(data, float):
if math.isnan(data):
return “NaN” # 将 NaN 替换为字符串 “NaN”
elif math.isinf(data):
return “Infinity” if data > 0 else “-Infinity” # 将 Infinity 替换为字符串
else:
return data
elif isinstance(data, dict):
return {k: replace_nan_inf_with_string(v) for k, v in data.items()}
elif isinstance(data, list):
return [replace_nan_inf_with_string(item) for item in data]
else:
return data
processed_data_string = replace_nan_inf_with_string(data_with_float)
try:
json_output_string = json.dumps(processed_data_string, indent=2)
print(“— Replaced with string —“)
print(json_output_string)
except ValueError as e:
print(f”Serialization failed even after replacing with string: {e}”)
“`
优点:
- 简单易懂,逻辑直接。
- 生成的 JSON 字符串符合标准规范,具有良好的跨平台和跨语言兼容性。
- 替换为
null
丢失的信息最少,且null
的语义通常可以接受。
缺点:
- 需要在序列化前手动遍历和处理数据结构,如果数据结构复杂或嵌套很深,代码可能会显得繁琐。
- 替换会丢失原始的特殊值信息(例如,你无法区分原始值是
NaN
还是+Infinity
或-Infinity
,如果它们都被替换为null
)。 - 如果替换为字符串,接收端需要显式地将特定字符串解析回特殊浮点值,增加了反序列化的复杂性。
方法二:使用自定义 JSON 编码器 (Custom JSON Encoder)
许多编程语言的 JSON 库都允许用户提供一个自定义的编码器(Encoder)。通过继承标准的 JSON 编码器类并覆盖其方法,我们可以在序列化过程中拦截特定类型的数据(如 float
)并应用自定义的处理逻辑。这种方法比手动遍历数据结构更优雅,可以将处理逻辑封装起来,提高代码的复用性。
实现示例 (Python):
我们可以创建一个继承自 json.JSONEncoder
的类,并重写 default(self, obj)
方法。这个方法会在遇到标准编码器无法处理的对象类型时被调用。我们可以在这里检查对象是否是浮点数,并根据需要进行处理。
“`python
import json
import math
data_with_float = {
“name”: “test_data_encoder”,
“value1”: 1.23,
“value2”: float(‘nan’),
“value3”: float(‘inf’),
“value4”: -float(‘inf’),
“value5”: 0.0,
“nested”: {
“sub_value”: float(‘nan’)
}
}
class FloatNaNInfEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, float):
if math.isnan(obj) or math.isinf(obj):
# 这里可以根据需求返回 None (null), “NaN”, “Infinity” 等
return None # 示例:替换为 null
# 或者 return “NaN” if math.isnan(obj) else (“Infinity” if obj > 0 else “-Infinity”) # 示例:替换为字符串
else:
return obj # 普通浮点数按默认方式处理
# 对于其他类型,调用父类的 default 方法
return super(FloatNaNInfEncoder, self).default(obj)
try:
# 使用自定义编码器进行序列化
json_output_encoder = json.dumps(data_with_float, indent=2, cls=FloatNaNInfEncoder)
print(“— Using Custom Encoder (Replacing with null) —“)
print(json_output_encoder)
# 如果替换为字符串,需要另一个编码器或逻辑
class FloatNaNInfStringEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, float):
if math.isnan(obj):
return "NaN"
elif math.isinf(obj):
return "Infinity" if obj > 0 else "-Infinity"
else:
return obj
return super(FloatNaNInfStringEncoder, self).default(obj)
json_output_string_encoder = json.dumps(data_with_float, indent=2, cls=FloatNaNInfStringEncoder)
print("\n--- Using Custom Encoder (Replacing with string) ---")
print(json_output_string_encoder)
except ValueError as e:
print(f”Serialization failed using custom encoder: {e}”)
“`
优点:
- 代码封装性好,逻辑集中在一个地方。
- 易于复用,可以在多个地方使用同一个编码器类。
- 遵循标准的序列化流程,无需手动遍历整个数据结构。
缺点:
- 需要理解并继承 JSON 编码器类,相比直接替换稍微复杂一点。
- 同样存在信息丢失或需要在接收端特殊处理的问题,取决于你选择的替换策略(
null
或字符串)。
方法三:利用特定库的非标准扩展(不推荐用于通用场景)
一些 JSON 库为了方便起见,可能会提供允许序列化 NaN
和 Infinity
的选项。例如,Python 的 json
库在 json.dumps()
函数中有一个 allow_nan
参数,默认是 True
。
注意: 当 allow_nan=True
时,Python 的 json
库会直接将 NaN
序列化为文本 nan
,将 Infinity
序列化为 Infinity
,将 -Infinity
序列化为 -Infinity
。
“`python
import json
import math
data_with_float = {
“value_nan”: float(‘nan’),
“value_inf”: float(‘inf’),
“value_neg_inf”: -float(‘inf’)
}
try:
# 默认 allow_nan=True 可能会直接输出 NaN/Infinity 文本
# 但这取决于库的版本和实现,而且输出的不是标准 JSON
# 为了明确演示,我们显式设置 allow_nan=True
json_output_non_standard = json.dumps(data_with_float, indent=2, allow_nan=True)
print(“— Using allow_nan=True (Non-Standard JSON) —“)
print(json_output_non_standard)
except ValueError as e:
print(f”Serialization failed even with allow_nan=True (this is unexpected for Python json default): {e}”) # 理论上设置 True 不会抛错,但输出非标准
“`
输出示例 (取决于库版本,但格式类似):
json
--- Using allow_nan=True (Non-Standard JSON) ---
{
"value_nan": NaN, <-- 注意这里没有引号
"value_inf": Infinity, <-- 注意这里没有引号
"value_neg_inf": -Infinity <-- 注意这里没有引号
}
优点:
- 如果接收端使用的 JSON 解析库也支持这种非标准格式(某些 JavaScript 解析器可能支持),那么可以保留特殊值的身份。
缺点:
- 生成的 JSON 字符串不符合标准的 JSON 规范! 大多数严格遵循规范的 JSON 解析器(尤其是在其他语言中)会拒绝解析包含
NaN
、Infinity
、-Infinity
作为数字字面量的 JSON 字符串,并抛出解析错误。 - 极大地降低了数据的互操作性。
强烈建议: 除非你明确控制了数据的生产者和所有消费者,并且所有相关的 JSON 库都保证支持这种非标准扩展,否则不要在生产环境中使用 allow_nan=True
来生成 JSON。 对于通用的数据交换场景,这种方法是不可取的。
方法四:将所有浮点数转换为字符串表示
另一种处理方法是,不仅处理 NaN
和 Infinity
,而是将所有浮点数都序列化为字符串。
实现示例 (Python):
可以使用自定义编码器来实现这一点。
“`python
import json
import math
data_with_float = {
“name”: “test_data_string_all”,
“value1”: 1.23,
“value2”: float(‘nan’),
“value3”: float(‘inf’),
“value4”: -float(‘inf’),
“value5”: 0.0,
“value6”: 42, # 注意:整数不会被转换
“nested”: {
“sub_value”: float(‘nan’)
}
}
class FloatToStringEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, float):
# 将所有浮点数转换为字符串
if math.isnan(obj):
return “NaN” # 依然可以区分特殊值
elif math.isinf(obj):
return “Infinity” if obj > 0 else “-Infinity”
else:
# 使用足够精确的字符串格式,例如 repr() 或 format()
return repr(obj) # repr() 通常能提供足够的精度
# 或者 return f”{obj:.10f}” # 控制小数位数
# 对于其他类型,包括整数,调用父类的 default 方法
return super(FloatToStringEncoder, self).default(obj)
try:
json_output_string_all = json.dumps(data_with_float, indent=2, cls=FloatToStringEncoder)
print(“— Using Custom Encoder (Converting all floats to string) —“)
print(json_output_string_all)
except ValueError as e:
print(f”Serialization failed using float-to-string encoder: {e}”)
“`
输出示例:
json
--- Using Custom Encoder (Converting all floats to string) ---
{
"name": "test_data_string_all",
"value1": "1.23",
"value2": "NaN",
"value3": "Infinity",
"value4": "-Infinity",
"value5": "0.0",
"value6": 42, <-- 整数仍然是数字
"nested": {
"sub_value": "NaN"
}
}
优点:
- 生成的 JSON 字符串完全符合标准规范。
- 可以保留特殊值(通过特定的字符串,如
"NaN"
)的同时,也将常规浮点数的表示统一为字符串。 - 避免了浮点数在序列化/反序列化过程中潜在的精度表示问题(虽然接收端解析回浮点数时仍可能有精度问题)。
缺点:
- 接收端在反序列化后必须显式地检测并解析这些字符串,将其转换回数值类型。这增加了反序列化的复杂性,并且需要接收端知道哪些字符串应该被解释为浮点数。
- 数字类型被强制转换为字符串,这可能不符合数据原有的语义。
方法五:考虑使用其他序列化格式
如果你的应用场景对数据类型有严格要求,并且 JSON 的限制成为了瓶颈,那么可以考虑使用其他的序列化格式,它们可能原生支持更多的数据类型,包括 NaN
和 Infinity
。常见的替代方案包括:
- MessagePack: 一种高效的二进制序列化格式,支持多种数值类型。
- Protocol Buffers (Protobuf): Google 开发的一种语言中立、平台中立、可扩展的序列化结构数据的方法。
- Apache Avro: 另一个数据序列化系统,特别适用于大数据场景。
- BSON (Binary JSON): MongoDB 使用的一种二进制序列化格式,扩展了 JSON 的类型系统,支持更多的类型。
这些格式通常有更丰富的类型系统,能够更准确地表示原始数据类型,包括 NaN
和 Infinity
。然而,使用这些格式意味着放弃 JSON 的易读性、普适性和直接在浏览器中使用的便利性,并且需要在生产者和消费者双方引入额外的库。
第五部分:选择合适的处理方法
选择哪种方法取决于具体的应用场景和需求:
-
如果数据的接收方是不可控的(例如,公共 API),或者你需要最大的兼容性:
- 强烈推荐在序列化前将
NaN
和Infinity
替换为null
。这是最符合 JSON 规范、最不容易出错且最通用的方法。虽然丢失了特殊值的精确身份,但通常情况下null
是一种合理的“无效数据”表示。 - 使用自定义编码器实现这种替换是更优雅的方式。
- 强烈推荐在序列化前将
-
如果需要保留
NaN
和Infinity
的身份信息,并且你能控制数据的接收方:- 可以将
NaN
和Infinity
替换为特定的字符串(如"NaN"
或"Infinity"
)。 - 需要确保接收方知道这些字符串的含义,并在反序列化后进行相应的转换。
- 使用自定义编码器进行字符串替换是好的实践。
- 可以将
-
如果对浮点数的表示精度有严格要求,并且希望避免 JSON 数字表示带来的潜在精度问题(这与
NaN
/Infinity
问题不同,但有时会混淆):- 可以将所有浮点数转换为字符串。
- 同样需要接收端进行解析。注意选择合适的字符串格式以保留足够的精度。
-
如果 JSON 的类型系统限制严重影响了你的应用,并且易读性不是首要考虑因素:
- 考虑使用其他更丰富类型的序列化格式(如 MessagePack, Protobuf, BSON)。
-
绝对避免使用
allow_nan=True
(或类似选项)来生成包含NaN
、Infinity
字面量的 JSON,除非你完全控制了环境并接受其非标准性。
第六部分:在实践中应用 (以 Python 为例)
在 Python 中处理这个问题,最推荐和常用的方法是使用自定义编码器将 NaN
和 Infinity
替换为 null
。以下是一个更完整的示例,演示如何在实际代码中使用自定义编码器。
“`python
import json
import math
假设这是你需要序列化的数据,可能来自计算或外部输入
data_to_serialize = {
“id”: “sensor-reading-123”,
“timestamp”: “2023-10-27T10:00:00Z”,
“temperature”: 25.5,
“humidity”: 60.2,
“pressure”: float(‘nan’), # 压力传感器读数无效
“altitude”: 150.0,
“voltage”: float(‘inf’), # 电压超出测量范围
“status_code”: 200,
“readings”: [
{“time”: “T01”, “value”: 1.1},
{“time”: “T02”, “value”: float(‘nan’)},
{“time”: “T03”, “value”: 3.3}
]
}
定义一个自定义的 JSON Encoder
class CleanFloatEncoder(json.JSONEncoder):
“””
自定义 JSON 编码器,将 NaN 和 Infinity 浮点值转换为 null。
这确保生成的 JSON 符合标准规范,并易于解析。
“””
def default(self, obj):
# 检查对象是否是浮点数
if isinstance(obj, float):
# 检查是否是 NaN 或 Infinity
if math.isnan(obj) or math.isinf(obj):
# 将特殊浮点数转换为 None (JSON 中的 null)
return None
else:
# 普通浮点数按默认方式处理
return obj
# 对于其他类型,调用父类的 default 方法
# 如果 default 方法仍然无法处理该对象类型,会抛出 TypeError
return super(CleanFloatEncoder, self).default(obj)
使用自定义编码器进行序列化
try:
json_output = json.dumps(data_to_serialize, indent=4, cls=CleanFloatEncoder)
print(“Successfully serialized data with special floats replaced by null:”)
print(json_output)
except TypeError as e:
print(f”Serialization failed due to unsupported type: {e}”)
except ValueError as e:
# 这个错误在我们使用 CleanFloatEncoder 的情况下应该不会发生
print(f”Serialization failed due to invalid value (should not happen with this encoder): {e}”)
except Exception as e:
print(f”An unexpected error occurred during serialization: {e}”)
———————————————————-
演示直接序列化原始数据会失败
print(“\n” + “=”*50 + “\n”)
print(“Attempting to serialize original data without special handling:”)
try:
json_output_raw = json.dumps(data_to_serialize, indent=4) # 不指定 cls,使用默认编码器
print(“Raw serialization successful (this might be unexpected or rely on allow_nan=True which is risky):”)
print(json_output_raw)
except ValueError as e:
print(f”Raw serialization failed as expected: {e}”)
print(“Error message indicates non-compliant float values.”)
except TypeError as e:
print(f”Raw serialization failed due to type error: {e}”)
“`
运行上述 Python 代码,你会看到以下结果:
- 使用
CleanFloatEncoder
成功地将数据序列化,pressure
和voltage
的值变成了null
。 - 直接使用
json.dumps()
序列化原始数据会失败,并抛出一个ValueError
,提示 “Out of range float values are not JSON compliant” (或者类似信息,取决于 Python 版本和库实现)。这验证了问题的存在。
这个例子清晰地展示了问题以及使用自定义编码器解决问题的推荐方式。
结论
float32
(以及任何遵循 IEEE 754 标准的浮点数类型,如 float64
)之所以在某些情况下不可直接 JSON 序列化,根本原因在于它们能够表示 NaN
和 Infinity
等特殊数值,而 JSON 规范的数字类型定义中没有包含这些特殊值的标准文本表示形式。尝试直接序列化这些特殊值会违反 JSON 规范,导致序列化失败或生成非标准的、兼容性差的 JSON 字符串。
为了解决这个问题,我们需要在序列化过程中对这些特殊浮点数进行显式处理。最推荐和通用的方法是:
- 检测数据中的浮点数是否为
NaN
或Infinity
。 - 在序列化时,将这些特殊值替换为标准的 JSON 值,最常见且推荐的是替换为
null
。 - 通过实现自定义 JSON 编码器来封装和自动化这个处理逻辑,是提高代码质量和复用性的有效方式。
虽然也可以选择将特殊值转换为特定的字符串或探索其他非标准方法/序列化格式,但替换为 null
并使用自定义编码器通常能在遵守 JSON 规范、保证兼容性与处理特殊值之间取得最佳平衡。
理解 JSON 规范的局限性以及浮点数内部表示的特性,是正确处理数据序列化过程中这些看似小实则关键问题的基础。通过采取适当的处理策略,我们可以确保生成符合标准、可靠交换的 JSON 数据。