203

知道为什么 JSON 忽略了 NaN 和 +/- Infinity 吗?它将 Javascript 置于一种奇怪的情况下,如果对象包含 NaN 或 +/- 无穷大值,则原本可以序列化的对象却不是。

看起来这是一成不变的:参见RFC4627ECMA-262(第 24.5.2 节,JSON.stringify,注 4,ECMA-262 pdf 的第 683 页最后编辑):

有限数被字符串化,就好像通过调用ToString(number). 无论符号如何, NaN和 Infinity 都表示为 String null

4

10 回答 10

101

Infinity and NaN aren't keywords or anything special, they are just properties on the global object (as is undefined) and as such can be changed. It's for that reason JSON doesn't include them in the spec -- in essence any true JSON string should have the same result in EcmaScript if you do eval(jsonString) or JSON.parse(jsonString).

If it were allowed then someone could inject code akin to

NaN={valueOf:function(){ do evil }};
Infinity={valueOf:function(){ do evil }};

into a forum (or whatever) and then any json usage on that site could be compromised.

于 2009-09-14T21:34:43.263 回答
61

关于最初的问题:我同意用户“cbare”的观点,因为这是 JSON 中的一个不幸的遗漏。IEEE754 将这些定义为浮点数的三个特殊值。所以 JSON 不能完全表示 IEEE754 浮点数。事实上更糟糕的是,因为 ECMA262 5.1 中定义的 JSON 甚至没有定义它的数字是否基于 IEEE754。由于 ECMA262 中为 stringify() 函数描述的设计流程确实提到了三个特殊的 IEEE 值,因此可以怀疑其意图实际上是支持 IEEE754 浮点数。

作为另一个与问题无关的数据点:XML 数据类型 xs:float 和 xs:double 确实声明它们基于 IEEE754 浮点数,并且它们确实支持这三个特殊值的表示(参见 W3C XSD 1.0 第 2 部分, 数据类型)。

于 2012-02-09T21:19:54.437 回答
20

您能否调整空对象模式,并在您的 JSON 中表示如下值

"myNum" : {
   "isNaN" :false,
   "isInfinity" :true
}

然后在检查的时候可以检查类型

if (typeof(myObj.myNum) == 'number') {/* do this */}
else if (myObj.myNum.isNaN) {/* do that*/}
else if (myObj.myNum.isInfinity) {/* Do another thing */}

我知道在 Java 中你可以重写序列化方法来实现这样的事情。不确定您的序列化从哪里开始,因此我无法详细说明如何在序列化方法中实现它。

于 2009-09-14T18:18:27.697 回答
15

字符串“Infinity”、“-Infinity”和“NaN”都强制转换为 JS 中的预期值。所以我认为在 JSON 中表示这些值的正确方法是字符串。

> +"Infinity"
Infinity

> +"-Infinity"
-Infinity

> +"NaN"
NaN

可惜 JSON.stringify 默认不这样做。但是有一个办法:

> JSON.stringify({ x: Infinity }, function (k,v) { return v === Infinity ? "Infinity" : v; })
"{"x":"Infinity"}"
于 2015-02-27T10:57:32.007 回答
7

如果您有权访问序列化代码,您可能会将 Infinity 表示为 1.0e+1024。指数太大而无法以双精度表示,反序列化时表示为无穷大。适用于 webkit,不确定其他 json 解析器!

于 2012-02-20T05:21:57.860 回答
6

原因在Standard ECMA-404 The JSON Data Interchange Syntax, 1st Edition 的第 ii 页中说明

JSON 与数字无关。在任何编程语言中,都可以有各种容量和补码、固定或浮点、二进制或十进制的各种数字类型。这会使不同编程语言之间的交换变得困难。JSON 相反只提供人类使用的数字表示:数字序列。所有编程语言都知道如何理解数字序列,即使它们在内部表示上存在分歧。这足以允许交换。

NaN原因并不像许多人声称的那样,是由于InfinityECMA 脚本的表示。简单是 JSON 的核心设计原则。

因为它是如此简单,所以预计 JSON 语法永远不会改变。这为 JSON 作为一种基础符号提供了极大的稳定性

于 2019-10-10T12:29:07.377 回答
4

JSON5 允许正负无穷大、NaN 和许多其他有效的 ECMAScript 内容被排除在 JSON 之外(尾随逗号等)的标准 Javascript 表示法。

https://json5.org/

这使得 JSON 成为一种更有用的格式。

但是,无论是使用 JSON 还是 JSON5:出于安全原因,始终解析——不要评估!

于 2021-07-01T11:32:11.860 回答
2

{"key":Infinity} 等情况的潜在解决方法:

JSON.parse(theString.replace(/":(Infinity|-IsNaN)/g, '":"{{$1}}"'), function(k, v) {
   if (v === '{{Infinity}}') return Infinity;
   else if (v === '{{-Infinity}}') return -Infinity;
   else if (v === '{{NaN}}') return NaN;
   return v;
   });

一般的想法是用我们在解析时会识别的字符串替换出现的无效值,然后用适当的 JavaScript 表示替换它。

于 2018-08-03T18:42:55.960 回答
1

当前的 IEEE Std 754-2008 包括对两种不同的 64 位浮点表示的定义:十进制 64 位浮点类型和二进制 64 位浮点类型。

舍入后的字符串.99999990000000006.9999999IEEE 二进制 64 位表示中的相同,但与IEEE.9999999十进制 64 位表示中的不同。在 64 位 IEEE 十进制浮点中.99999990000000006舍入到.9999999000000001与十进制值不同的.9999999值。

由于 JSON 仅将数值视为十进制数字的数字字符串,因此支持 IEEE 二进制和十进制浮点表示的系统(例如 IBM Power)无法确定两个可能的 IEEE 数字浮点值中的哪一个是故意的。

于 2015-10-27T21:30:37.240 回答
-3

如果像我一样,您无法控制序列化代码,则可以通过将 NaN 值替换为 null 或任何其他值来处理 NaN 值,如下所示:

$.get("file.json", theCallback)
.fail(function(data) {
  theCallback(JSON.parse(data.responseText.replace(/NaN/g,'null'))); 
} );

本质上,当原始 json 解析器检测到无效令牌时,将调用 .fail。然后使用字符串替换来替换无效标记。在我的情况下,序列化程序返回 NaN 值是一个例外,所以这种方法是最好的方法。如果结果通常包含无效标记,则最好不要使用 $.get 而是手动检索 JSON 结果并始终运行字符串替换。

于 2013-04-04T04:24:34.420 回答