JSONはInfinityとNaNを省略しました。ECMAScriptのJSONステータス?


180

JSONがNaNおよび+/- Infinityを省略した理由は何ですか?NaNまたは+/-無限大の値が含まれている場合に、直列化可能であるオブジェクトがそうでないという奇妙な状況にJavascriptを配置します。

このように見えますが、石でキャストされています:参照RFC4627ECMA-262(セクション24.5.2、JSON.stringify、注4、最後の編集でECMA-262のPDFのページ683):

有限数は、を呼び出すかのように文字列化されToString(number)ます。符号に関係なくNaNとInfinityはStringとして表されnullます。


どちらのドキュメントにもその引用はありません。
wingedsubmariner 2015

1
それを修正しました、古い参照/古い編集がどういうわけかあったようです。
Jason S

回答:


90

InfinityそしてNaNキーワードや何か特別なものではなく、彼らはグローバルオブジェクトのプロパティだけです(あるとundefined)、そのように変更することができます。その理由は、JSONがそれらを仕様に含めないためです。eval(jsonString)つまり、またはを実行した場合、EcmaScriptで真のJSON文字列はすべて同じ結果になるはずですJSON.parse(jsonString)

それが許可された場合、誰かが同じようなコードを注入することができます

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

フォーラム(またはその他)にアクセスすると、そのサイトでのjsonの使用が危険にさらされる可能性があります。


29
1/0を評価するとInfinityが得られ、-1 / 0を評価すると-Infinityが得られ、0/0を評価するとNaNが得られます。
Jason S

9
しかし、用語NaNInfinityはプロパティ名であるため、String(1/0)"Infinity"は、値の無限大の文字列表現である文字列を生成します。NaNまたはInfinityリテラル値がESであるため、表現することはできません。式(たとえば、1 / 0、0 / 0など)またはプロパティルックアップ(Infinityまたはを参照NaN)を使用する必要があります。コードの実行が必要なため、JSONに含めることはできません。
olliej

16
安全性/セキュリティに関する要点として、NaNを変換しようとするとき、適切なJSONパーサーは、値0/0を生成する必要があります(シンボルNaNを評価するのではなく)。記号NaNは次のように再定義されます。
Jason S

33
@olliej:あなたはNaNがリテラルではないと主張します、私はJavaScriptのセマンティクスを判断するのに十分なJavascriptを知りません。ただし、倍精度浮動小数点数を格納するファイル形式の場合、IEEE浮動小数点数を定義する方法が必要です。つまり、リテラルNaN / Infinity / NegInfinityを使用する必要があります。これらは64ビットのdoubleの状態であり、表現可能でなければなりません。(理由のために)それらに依存する人々がいます。JSON / Javascriptが科学的コンピューティングではなくWeb開発から生まれたため、それらはおそらく忘れられました。
wirrbel 2013年

35
NaN、Infinity、および-Infinityの完全に有効な標準の浮動小数点数状態を任意に省略したのは、JSONにとって100%、絶対に間違っています。基本的に、JSONはIEEE float値の任意のサブセットをサポートすることを決定しました。3つの特定の値が難しいか何かであるため、それらを無意識のうちに省略しました。いいえ。そのような数値はリテラル1/0、-1 / 0、0 / 0としてエンコードされている可能性があるため、評価可能性は言い訳にもなりません。それらは、「/ 0」が付加された有効な数値になります。これは、検出が簡単であるだけでなく、ESとして同時に評価することもできます。言い訳しない。
Triynko

56

元の質問について:ユーザー「cbare」はJSONで残念ながら省略されていることに同意します。IEEE754では、これらを浮動小数点数の3つの特別な値として定義しています。したがって、JSONはIEEE754浮動小数点数を完全に表すことができません。ECMA262 5.1で定義されているJSONは、その番号がIEEE754に基づいているかどうかさえ定義していないため、実際にはさらに悪いことです。ECMA262のstringify()関数について説明されている設計フローは3つの特別なIEEE値について言及しているため、意図は実際にはIEEE754浮動小数点数をサポートすることであったと思われます。

XMLデータ型xs:floatおよびxs:doubleは、IEEE754浮動小数点数に基づいていると述べており、これらの3つの特別な値の表現をサポートしています(W3C XSD 1.0 Part 2を参照)。 、データ型)。


5
私はこれがすべて残念なことに同意します。しかし、おそらくJSONの数値が正確な浮動小数点形式を指定していないのは良いことです。IEEE754でも多くのフォーマットが指定されています-異なるサイズ、および10進数と2進数の指数の違いです。JSONは10進数に特に適しています。そのため、JSONを2進数に固定すると、残念です。
エイドリアン・ラトナパラ2014年

5
@AdrianRatnapala +1確かに:JSON数値は潜在的に無限の精度を持っているため、サイズ制限、精度制限、丸め効果がないため(シリアライザが処理できる場合)、IEEE仕様よりもはるかに優れています。
Arnaud Bouchez 2015年

2
@ArnaudBouchez。そうは言っても、JSONは引き続きNaNおよび+ -Infinityを表す文字列をサポートするはずです。JSONをIEEE形式に固定しないでください。数値形式を定義する人は、少なくともWikipediaのページIEEE754を見て、考えるのをやめるべきです。
エイドリアン・ラトナパラ2015年


これは不幸なことではありません。@CervEdによる回答を参照してください。これは、良いことであるIEE754に関連付けられていません(ほとんどのプログラミング言語がIEEE754を使用しているため、NaNなどの場合に追加の処理が必要な場合でも)。
Ludovic Kuty

16

nullオブジェクトパターンを適合させ、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
うーん...それは回避策への答えです。私は実際に回避策を求めていたのではなく、なぜこれらの値が除外されているのかを尋ねました。とにかく+1。
Jason S

2
@Zoidberg:undefinedキーワードではなく、グローバルオブジェクトのプロパティです
olliej

2
@Zoidberg:undefinedはグローバルオブジェクトのプロパティ"undefined" in thisです。これはキーワードではないため、グローバルスコープでtrueを返します。それはまた、あなたができることundefined = 42を意味し、if (myVar == undefined)(本質的に)なるmyVar == 42。これundefinedは、デフォルトでは存在しなかったecmascript nee javascriptの初期の時代を思い起こさせるので、人々は単にvar undefinedグローバルスコープで実行しました。その結果undefined、既存のサイトを壊さずにキーワードにすることはできなかったので、undefinedを通常のプロパティにすることはいつまでも運命にありました。
olliej

2
@olliej:undefinedがグローバルオブジェクトのプロパティであると考える理由がわかりません。デフォルトでは、undefinedのルックアップはundefinedの組み込み値です。「undefined = 42」でオーバーライドすると、undefinedに変数ルックアップとしてアクセスすると、オーバーライドされた値が取得されます。しかし、「zz = undefined; undefined = 42; x = {}; 'undefined old =' +(xa === zz)+ '、undefined new =' +(xa === undefined)」を実行してみてください。シンボル検索をオーバーライドできても、null、undefined、NaN、またはInfinityの内部値を再定義することはできません。
ジェイソンS

2
@Jason undefinedは、そのように指定されているため、グローバルプロパティです。ECMAScript-262 3rd edの15.1.1.3を参照してください。
kangax 2009

11

文字列「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"}"

1
0/0などは有効なJSONではありません。標準の範囲内で作業する必要があり、文字列がうまく機能します。
teh_senaus

逆に、これが唯一の実用的な解決策だと思いますが、入力値が「NaN」などの場合にNaNを返す関数を実行します。変換を行う方法は、コードインジェクションが発生しやすくなります。
マルコ・スラ

3
JSON値を算術式にすることはできません...標準を言語リテラル構文から分離する目的は、コードとして実行せずにJSONを非直列化可能にすることです。わからないなぜ我々は持つことができませんでしたNaNし、Infinityなどのキーワードの値として追加trueしてfalseいるが、。
マークリード

それがより明確にし、私たちが使用できるためにNumber("Infinity")Number("-Infinity")そしてNumber("NaN")
HKTonyLee

これは魔法のような仕事です。JavaScriptでJSON.parse("{ \"value\" : -1e99999 }")簡単に戻り{ value:-Infinity }ます。それよりも大きい可能性のあるカスタム数値タイプと互換性がないだけです
Thaina

7

シリアライゼーションコードにアクセスできる場合、Infinityを1.0e + 1024として表すことができます。指数が大きすぎてdoubleで表すことができません。逆シリアル化すると、これはInfinityとして表されます。Webkitで動作しますが、他のjsonパーサーについては不明です!


4
IEEE754は128ビットの浮動小数点数をサポートするため、1.0e5000の方が優れています
Ton Plomp

2
Ton:128ビットが後で追加されました。256ビットの追加を決定した場合はどうなりますか?次に、ゼロをさらに追加する必要があります。既存のコードは異なる動作をします。Infinityは常になのでInfinity、それをサポートしないのはなぜですか?
空飛ぶ羊

1
賢いアイデア!別のフォーマットに切り替えるか、面倒な回避策のコードをパーサーに追加しようとしていました。すべての場合に理想的ではありませんが、私の場合、無限大は収束シーケンスに対する最適化されたエッジケースとして機能しますが、それは完璧です。ありがとう!
または、Sharir、

3
1、-1、0 .....完全に有効/解析可能な数値は、単純/0にそれらの末尾に追加すると、これら3つの特別な値になります。簡単に解析でき、すぐに表示でき、評価も可能です。彼らがまだそれを標準に追加していないことは言い訳がありません:{"Not A Number":0/0,"Infinity":1/0,"Negative Infinity":-1/0} <<なぜですか? alert(eval("\"Not A Number\"") //works alert(eval("1/0")) //also works, prints 'Infinity'。言い訳しない。
Triynko


1

現在のIEEE Std 754-2008には、2つの異なる64ビット浮動小数点表現の定義が含まれています。10進64ビット浮動小数点型と2進64ビット浮動小数点型です。

文字列を丸めた後に.99999990000000006同じである.9999999IEEEバイナリ64ビット表現におけるそれであるNOT同じ.9999999IEEE進64ビット表現です。64ビットIEEEの10進浮動小数点では、10 進値とは異なる.99999990000000006値に丸められます。.9999999000000001.9999999

JSONは数値を10進数の数値文字列として扱うだけなので、IEEEの2進数と10進数の浮動小数点表現(IBM Powerなど)の両方をサポートするシステムが、2つの可能なIEEE数値浮動小数点値のどちらであるかを判別する方法はありません。意図されました。


これは質問とどのような関係がありますか?(これはInfinityとNaNについてです)
ブライアン

1

{"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を台無しにしないのですか?多くのユースケースでは、それが問題ではないと想定してもおそらく安全ですが、ソリューションは完全に堅牢ではありません。
olejorgenb

1

理由は、Standard ECMA-404 The JSON Data Interchange Syntax、1st Editionの iiページに記載されています。

JSONは数値にとらわれません。どのプログラミング言語でも、固定または浮動、2進数または10進数など、さまざまな容量および補数のさまざまな数値タイプが存在する可能性があります。これにより、異なるプログラミング言語間の交換が困難になる可能性があります。JSONは代わりに、人間が使用する数値の表現、つまり一連の数字のみを提供します。すべてのプログラミング言語は、内部表現に同意しない場合でも、数字列を理解する方法を知っています。それは交換を可能にするのに十分です。

その理由は、多くの人が主張しているようにNaNInfinityECMAスクリプトの表現によるものではありません。シンプルさは、JSONの中心的な設計原則です。

とてもシンプルなので、JSONの文法が変わることは期待されていません。これにより、JSONが基本的な表記として、非常に安定します。


-3

私のように、シリアライゼーションコードを制御できない場合は、次のように、ハックの一部としてnullまたはその他の値で置き換えることにより、NaN値を処理できます。

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

基本的に、元のjsonパーサーが無効なトークンを検出すると、.failが呼び出されます。次に、文字列置換を使用して無効なトークンを置換します。私の場合、シリアライザがNaN値を返すのは例外なので、この方法が最善の方法です。結果に通常無効なトークンが含まれている場合は、$。getを使用せずに、JSON結果を手動で取得し、常に文字列置換を実行することをお勧めします。


21
賢いですが、完全に万能というわけではありません。試してみる{ "tune": "NaNaNaNaNaNaNaNa BATMAN", "score": NaN }
JJJ 2013

1
また、jQueryを使用している必要があります。$ .get()を持っていません。
Jason S
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.