JSON 省略了 Infinity 和 NaN; ECMAScript 中的 JSON 状态?

Posted

技术标签:

【中文标题】JSON 省略了 Infinity 和 NaN; ECMAScript 中的 JSON 状态?【英文标题】:JSON left out Infinity and NaN; JSON status in ECMAScript? 【发布时间】:2010-11-28 05:28:41 【问题描述】:

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

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

通过调用ToString(number) 对有限数字进行字符串化。 NaN 和 Infinity 无论符号表示为字符串null

【问题讨论】:

我在任一文档中都找不到该引用。 修复了它,看起来好像有一个过时的参考/过时的编辑。 dbj.org/json-how-to-create-an-undefined-value-or-an-nan-value 【参考方案1】:

InfinityNaN 不是关键字或任何特殊的东西,它们只是全局对象上的属性(undefined 也是如此),因此可以更改。正是因为这个原因,JSON 没有将它们包含在规范中——本质上,如果你使用 eval(jsonString)JSON.parse(jsonString),任何真正的 JSON 字符串在 EcmaScript 中都应该有相同的结果。

如果允许,那么有人可以注入类似于

的代码
NaN=valueOf:function() do evil ;
Infinity=valueOf:function() do evil ;

进入论坛(或其他),然后该网站上的任何 json 使用都可能受到损害。

【讨论】:

【参考方案2】:

关于最初的问题:我同意用户“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 部分,数据类型)。

【讨论】:

我同意这一切都是不幸的。但也许 JSON 数字没有指定确切的浮点格式是件好事。甚至 IEEE754 也指定了许多格式——不同的大小,以及十进制和二进制指数之间的区别。 JSON 特别适合十进制,因此如果某些标准将其固定为二进制将是很遗憾的。 @AdrianRatnapala +1 确实:JSON 数字可能具有无限精度,因此比 IEEE 规范要好得多,因为它们没有大小限制、精度限制和舍入效果(如果序列化程序可以处理它)。 @ArnaudBouchez。也就是说,JSON 仍应支持表示 NaN 和 +-Infinity 的字符串。即使 JSON 不应该被固定为任何 IEEE 格式,定义数字格式的人至少应该查看***页面 IEEE754 并停下来思考一下。 dbj.org/json-how-to-create-an-undefined-value-or-an-nan-value 这并不不幸。请参阅@CervEd 的答案。它与 IEE754 无关,这是一件好事(即使大多数编程语言使用 IEEE754,因此需要在 NaN 等情况下进行额外处理)。【参考方案3】:

您能否调整空对象模式,并在您的 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 中你可以重写序列化方法来实现这样的事情。不知道你从哪里序列化,所以我不能详细说明如何在序列化方法中实现它。

【讨论】:

嗯...这是解决方法的答案;我并不是真的要求解决方法,而是要求排除这些值的原因。但无论如何 +1。 @Zoidberg: undefined 不是关键字,它是全局对象的属性 @Zoidberg: undefined 是全局对象的属性——它不是关键字,所以"undefined" in this 在全局范围内返回 true。这也意味着你可以做到undefined = 42if (myVar == undefined) 变成(基本上)myVar == 42。这可以追溯到早期的 ecmascript nee javascript,其中 undefined 默认不存在,所以人们只是在全局范围内使用 var undefined。因此,undefined 无法在不破坏现有网站的情况下成为关键字,因此我们注定永远将 undefined 设为正常属性。 @olliej:我不知道你为什么认为 undefined 是全局对象的一个​​属性。默认情况下,未定义的查找是未定义的内置值。如果你用“undefined=42”覆盖它,那么当你访问 undefined 作为变量查找时,你会得到覆盖的值。但是尝试执行“zz=undefined; undefined=42; x=;'undefined old='+(x.a === zz)+', undefined new='+(x.a === undefined)”。您永远不能重新定义 null、undefined、NaN 或 Infinity 的内部值,即使您可以覆盖它们的符号查找。 @Jason undefined 是一个全局属性,因为它是这样指定的。请参阅 ECMAScript-262 第 3 版的 15.1.1.3。【参考方案4】:

字符串“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""

【讨论】:

0/0 等不是有效的 JSON。您必须在标准的范围内工作,而字符串可以很好地完成这项工作。 相反,我认为这是唯一可行的解​​决方案,但是如果输入值为“NaN”等,我会做一个返回NaN的函数。你做转换的方式很容易代码注入。 JSON 值不能是算术表达式...使标准与语言文字语法分离的目标是使 JSON 可反序列化,而无需将其任何作为代码执行。不知道为什么我们不能将 NaNInfinity 添加为关键字值,例如 truefalse 为了更明确,我们可以使用Number("Infinity")Number("-Infinity")Number("NaN") 这就像魔术一样工作。JSON.parse(" \"value\" : -1e99999 ") 在javascript中轻松返回 value:-Infinity 。只是它与可能大于此的自定义数字类型不兼容【参考方案5】:

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

【讨论】:

IEEE754 支持 128 位浮点数,所以 1.0e5000 更好 吨:128 位是后来添加的。如果他们决定添加 256 位怎么办?然后你必须添加更多的零,现有代码的行为会有所不同。 Infinity 将永远是 Infinity,那为什么不支持呢? 聪明的主意!我正要切换到不同的格式或向我的解析器添加繁琐的解决方法代码。并非每种情况都理想,但在我的情况下,无穷大只是收敛序列的优化边缘情况,它是完美的,即使引入更大的精度,它仍然大部分是正确的。谢谢! 1、-1 和 0 ..... 完全有效/可解析的数字,只需将 /0 添加到它们的末尾即可成为这三个特殊值。它易于解析、立即可见,甚至可评估。他们还没有将其添加到标准中是不可原谅的:"Not A Number":0/0,"Infinity":1/0,"Negative Infinity":-1/0 alert(eval("\"Not A Number\"") //worksalert(eval("1/0")) //also works, prints 'Infinity'。没有借口。 dbj.org/json-how-to-create-an-undefined-value-or-an-nan-value【参考方案6】:

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

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

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

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

【讨论】:

...只是人类使用的数字表示... 那么,使用无穷大符号的数学家、科学家、工程师和程序员不是人类吗? (Snark 的目标是标准,而不是 CervEd。)【参考方案7】:

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

https://json5.org/

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

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

【讨论】:

【参考方案8】:

"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 表示替换它。

【讨论】:

我不知道为什么这个解决方案会被否决,因为坦率地说,如果您遇到 JSON 字符串包含 Infinity 或 IsNaN 值的情况,当您尝试解析它时它会失败。使用此技术,您首先将出现的 IsNaN 或 Infinity 替换为其他内容(以将它们与可能包含这些术语的任何有效字符串隔离开来),然后使用 JSON.parse(string,callback) 返回正确、有效的 JavaScript 值。我在生产代码中使用它,从来没有任何问题。 这不会弄乱字符串中的 Infinity 吗?对于许多用例来说,假设这不是问题可能是安全的,但解决方案并不完全可靠。【参考方案9】:

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

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

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

【讨论】:

这与问题有什么关系? (关于 Infinity 和 NaN)【参考方案10】:

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

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

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

【讨论】:

聪明,但并非万无一失。试试 "tune": "NaNaNaNaNaNaNaNa BATMAN", "score": NaN 你必须使用 jQuery。我没有 $.get()。

以上是关于JSON 省略了 Infinity 和 NaN; ECMAScript 中的 JSON 状态?的主要内容,如果未能解决你的问题,请参考以下文章

强制 JSON.stringify() 发出这样做的 NaN / Infinity 或 JS JSON 库

json模块使用

如何在 ANSI C 中生成 NaN、-Infinity 和 +Infinity?

Infinity 和 NaN 的类型都有哪些?

如何在 NaN 和 Infinity 上使用条件类型

NaN 和 Infinity