有没有办法让serde_json正确/不正确地处理 NaN、Inf 和 -Inf(IEEE 754 特殊)?

Is there a way to make serde_json handle NaN, Inf and -Inf properly/improperly (IEEE 754 Specials)?

提问人:Corvus 提问时间:11/17/2023 最后编辑:Corvus 更新时间:11/18/2023 访问量:89

问:

官方的 JSON 规范不支持 IEEE 754,而是有自己的 null(不是“null”)或普通数字的约定。

在许多语言和用例中,人们会忽略这一点,并偏离规范以支持IEEE754浮点数。例如,在 python 中

>>> json.dumps(dict(a = np.inf, b = -np.inf, c = np.nan), allow_nan=True)
'{"a": Infinity, "b": -Infinity, "c": NaN}'

在本例中,allow_nan默认为 True。

同样,在 C# 中,我们可以将数字处理设置为 AllowNamedFloatingPointLiterals 以获得相同的行为

https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonnumberhandling?view=net-8.0

那么,我们如何让 rust/serde_json 做同样的事情 - serde_json某处是否有标志来执行此操作,如果没有,添加此功能的最简单方法是什么?(我的意思是作为用户实现此功能,而不是通过更新serde_json源或分叉它或其他任何东西)。

编辑:在一些评论之后,假设我们同意JSON有问题,是否有一种格式可以用来代替JSON,完全支持浮点数。

或者,如何实现 JSON 有效的替代方案,例如使用“Infinity”作为字符串。据我所知,这将影响所有其他序列化,因此,如果您将结构序列化为 BSON、CBOR、msgpack 等。

再次编辑

因此,我自己的研究提出了几种可能性:

  • JSON5 显然是在 JSON 的基础上扩展的,同时向后兼容。
  • 可能有一些神奇的诡计可以用 serde 未标记的枚举来完成,但我不知道这是否属实。似乎 serde 可以依次尝试一系列格式,直到一种格式成功 - 如果它尝试序列化一个特殊的浮点数(严格来说,这是 JSON 规范所说的它应该做的),有没有办法使serde_json失败)。如果是这样,我们可以将其用作后备方案?
json rust ieee-754 serde serde-json

评论

2赞 user2407038 11/17/2023
{"a": Infinity}不是 JSON,因此使用通用的 JSON 序列化库来解析此数据可能会导致您出现问题。拥有标准化的数据格式允许不同的应用程序相互通信,如果一个应用程序不遵循标准,尝试以相同的方式破坏其他应用程序是错误的方法 - 而是修复损坏的应用程序。另一方面,是完全有效的 Json,您可以轻松创建一个能够反序列化该 JSON 的结构类型。{"a": "Infinity"}
0赞 Corvus 11/18/2023
@user2407038“试图以同样的方式破坏其他应用程序是错误的方法”,我不同意 - 这里的突破是 JSON 不支持 IEEE754。因此,我们最终得到的是“官方 JSON”和“事实上的 JSON”。我想要的是一个用于 rust 的“事实上的 JSON”序列化库。大多数其他序列化都支持浮点数(msgpack、xml、BSON,所以我认为在这里调用 JSON 损坏是错误的。

答:

1赞 Masklinn 11/17/2023 #1

不受支持

维护者的答案并不完全清楚,因为我认为serde_json公开了可以添加回退的反序列化器钩子,我认为通常的解决方案()为时已晚。deserialize_with

因此,您可能想要求精度,但乍一看,您似乎必须维护自定义的 serde_json 供应商/分支才能支持非标准扩展。

评论

0赞 Corvus 11/17/2023
事实上,deserialize_with 的解决方案并不特定于 serde_json,因此当使用其他序列化格式时,它也适用,这些格式完全支持浮点数。不幸的是,serde_json宣布这一缺陷是一项特征
1赞 Masklinn 11/17/2023
serde_json实现了 JSON,这不是 JSON,它不再是不支持 undefined缺陷,或者注释是缺陷。
0赞 Corvus 11/17/2023
我的意思是该缺陷是仅实现 JSON,而 JSON 具有未实现 IEE754 的缺陷 - 大多数名义上的 JSON 包都识别 JSON 中的缺陷并将其扩展到涵盖浮点数,因为它非常有用。我所指的“缺陷”是坚持严格的 JSON,而不是野外使用的 JSON。
2赞 Masklinn 11/17/2023
是的,我明白你的意思,因此我举了例子。“实现你所说的格式”仍然不是一个缺陷。
3赞 Masklinn 11/18/2023
语言值到 JSON 值的映射与 JSON 标准没有任何关系。你的库可以丢弃每个值,并使它们符合所有JSON标准的要求,这是完全有效的JSON。null
3赞 BallpointBen 11/18/2023 #2

serde_json可以处理这些特殊的浮子吗?不可以,因为它严格遵守 JSON 规范。但是,JSON5 支持这些时髦的浮点数,并且serde_json5忠实地实现了 JSON5

use std::collections::HashMap;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let map = HashMap::<_, _>::from_iter([
        ("a", 1.0),
        ("b", f64::NAN),
        ("c", f64::INFINITY),
        ("d", f64::NEG_INFINITY),
    ]);

    let j = serde_json5::to_string(&map)?;
    let o = serde_json5::from_str::<HashMap<String, f64>>(&j)?;

    println!("JSON5: {}", j);
    println!("HashMap: {:?}", o);

    Ok(())
}

输出:

JSON5: {"a":1,"b":NaN,"d":-Infinity,"c":Infinity}
HashMap: {"a": 1.0, "d": -inf, "c": inf, "b": NaN}

评论

0赞 Corvus 11/18/2023
这听起来像是正确的方法。在从 JSON 切换到 JSON5 时,是否有任何明显的陷阱需要注意。YAML 也是 JSON 的超集,但有时髦的空格规则等。我知道这是一个非常主观的问题,但是 JSON5 几乎可以替代吗?
1赞 BallpointBen 11/18/2023
他们的网站说 JSON5 是 JSON 的超集和 ES5 的子集,这似乎是避免“陷阱”的理想场所。唯一可能不是替代品的方法是,在自己编辑 JSON5 时,编辑器支持可能比 JSON 本身更差。
0赞 Corvus 11/19/2023
你直接指向serde_json5,但它似乎有 <2k 的下载量,并且是 crates.io/crates/json5 的一个分支。是有特殊的原因还是只是你首先遇到的原因?
1赞 BallpointBen 11/19/2023
@Corvus谷歌做了一些修饰工作——看起来至少修复了一个小错误,还添加了 和 .AFAICT 原始的 JSON5 crate 无法从任意 (包括文件) 进行反序列化。from_readerfrom_sliceRead