【问题标题】:JSON left out Infinity and NaN; JSON status in ECMAScript?JSON 省略了 Infinity 和 NaN; ECMAScript 中的 JSON 状态?
【发布时间】: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。

【问题讨论】:

标签: javascript json ecma262


【解决方案1】:

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

https://json5.org/

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

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

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

    {"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 吗?对于许多用例来说,假设这不是问题可能是安全的,但解决方案并不完全可靠。
    【解决方案4】:

    您能否调整空对象模式,并在您的 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 = 42 和if (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。
    【解决方案5】:

    当前的 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)
    【解决方案6】:

    字符串“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 可反序列化,而无需将其任何作为代码执行。不知道为什么我们不能将 NaN 和 Infinity 添加为关键字值,例如 true 和 false。
    • 为了更明确,我们可以使用Number("Infinity")、Number("-Infinity")和Number("NaN")
    • 这就像魔术一样工作。JSON.parse("{ \"value\" : -1e99999 }") 在javascript中轻松返回{ value:-Infinity }。只是它与可能大于此的自定义数字类型不兼容
    【解决方案7】:

    如果您像我一样无法控制序列化代码,则可以通过将 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()。
    【解决方案8】:

    如果您有权访问序列化代码,您可能会将 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'。没有借口。
    【解决方案9】:

    关于最初的问题:我同意用户“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 并停下来思考一下。
    • 这并不不幸。请参阅@CervEd 的答案。它与 IEE754 无关,这是一件好事(即使大多数编程语言使用 IEEE754,因此需要在 NaN 等情况下进行额外处理)。
    【解决方案10】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-14
      • 1970-01-01
      • 2016-12-15
      • 2011-09-08
      • 2014-06-29
      相关资源
      最近更新 更多