JSON Infinity ve NaN'yi geride bıraktı; ECMAScript'te JSON durumu?


180

JSON'un neden NaN ve +/- Infinity'yi bıraktığı hakkında bir fikrin var mı? Javascript'i, aksi halde serileştirilebilecek nesnelerin NaN veya +/- sonsuzluk değerleri içeriyorsa, tuhaf bir duruma sokar.

Bunun taştan yapılmış olduğu anlaşılıyor : RFC4627 ve ECMA- 262'ye bakınız (bölüm 24.5.2, JSON.stringify, NOT 4, ECMA-262 pdf'inin son düzenlemede sayfa 683'ü):

Sonlu sayılar, çağrılmış gibi dizilir ToString(number). İşaretten bağımsız olarak NaN ve Infinity, String olarak temsil edilir null.


Bu alıntıyı her iki belgede de bulamıyorum.
wingedsubmariner

1
düzeltildi, bir şekilde eski bir referans / eski düzenleme varmış gibi görünüyor.
Jason S

Yanıtlar:


90

Infinityve NaNanahtar kelimeler veya özel bir şey değildir, bunlar yalnızca global nesnenin (olduğu gibi undefined) özellikleridir ve bu nedenle değiştirilebilir. Bu nedenle JSON bunları spesifikasyona dahil etmez - aslında, gerçek JSON dizesi EcmaScript'te aynı sonuca sahip olmalıdır eval(jsonString)veyaJSON.parse(jsonString) .

İzin verildiyse, birisi benzer bir kod ekleyebilir

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

bir foruma (ya da her neyse) girdiğinizde, o sitedeki herhangi bir json kullanımı tehlikeye girebilir.


29
1/0 değerlendirirseniz Infinity, -1/0 değerlendirirseniz -Infinity, 0/0 değerlendirirseniz NaN alırsınız.
Jason S

9
Ancak, terimler NaNve Infinityözellik adlarıdır, bu nedenle String (1/0), "Infinity"sonsuzluğun yalnızca dize ile temsil edilen bir dizesini üretir . Değişken değerleri ES olarak ya NaNda temsil etmek mümkün değildir Infinity- ya bir ifade (örn. 1/0, 0/0 vb.) Ya da bir özellik araması ( Infinityya da atıfta bulunarak NaN) kullanmanız gerekir. Bunlar kod yürütmeyi gerektirdiğinden JSON'a dahil edilemezler.
olliej

16
Güvenlik / güvenlik konusundaki görüşünüze göre, NaN'yi dönüştürmeye gittiğinde tüm iyi bir JSON ayrıştırıcısı, ne olursa olsun "gerçek" NaN döndürecek olan 0/0 (NaN sembolünü değerlendirmek yerine) değerini vermektir. NaN sembolü olarak yeniden tanımlanır.
Jason S

33
@olliej: NaN'ın bir gerçek olmadığını iddia edersiniz, Javascript semantiklerini yargılayacak kadar Javascript bilmiyorum. Ancak, çift kesinlikli kayar nokta sayılarını depolayan bir dosya formatı için, IEEE kayan reklamlarını tanımlamanın bir yolu olmalıdır, örneğin NaN / Infinity / NegInfinity değişmezleriyle. Bunlar 64 bit iki katına çıkan durumlardır ve bu nedenle temsil edilebilir olmalıdır. Onlara güvenen insanlar var (nedenlerle). Muhtemelen unutulmuşlardı, çünkü JSON / Javascript bilimsel hesaplama yerine web geliştirmeden kaynaklanmıştır.
wirrbel

35
JSON için% 100, kesinlikle mükemmel geçerli ve standart kayan nokta sayısı durumlarını NaN, Infinity ve -Infinity'nin atlaması kesinlikle YANLIŞ. Esasen, JSON, IEEE kayan nokta değerlerinin rastgele bir alt kümesini desteklemeye karar verdi ve ciddiyetle üç belirli değeri ihmal etti, çünkü zor ya da bir şey. Hayır. Eval-yeteneği bir mazeret bile değildir, çünkü bu sayılar 1/0, -1/0 ve 0/0 değişmezleri olarak kodlanmış olabilir. "/ 0" ile eklenen geçerli sayılar olacaktır, bu yalnızca algılanması kolay değildir, aynı zamanda ES olarak değerlendirilebilir. Bahane yok.
Triynko

56

Orijinal soru üzerine: Ben bu JSON talihsiz bir ihmal olduğunu "cbare" kullanıcı ile katılıyorum. IEEE754 bunları kayan nokta sayısının üç özel değeri olarak tanımlar. Dolayısıyla JSON, IEEE754 kayan nokta sayılarını tam olarak temsil edemez. Aslında daha da kötüsü, ECMA262 5.1'de tanımlanan JSON, sayılarının IEEE754'e dayanıp dayanmadığını bile tanımlamıyor. ECMA262'de stringify () işlevi için açıklanan tasarım akışı üç özel IEEE değerinden bahsettiğinden, niyetin aslında IEEE754 kayan nokta sayılarını desteklediğinden şüphelenilebilir.

Diğer bir veri noktası olarak, şu soru ile ilgisi yoktur: XML veri tipleri xs: float ve xs: double, IEEE754 kayan nokta numaralarına dayalı olduklarını ve bu üç özel değerin temsilini desteklediklerini belirtir (Bkz. W3C XSD 1.0 Bölüm 2 , Veri tipleri).


5
Bunun talihsiz olduğuna katılıyorum. Ancak, JSON numaralarının tam olarak kayan nokta biçimini belirtmemesi iyi bir şeydir. IEEE754 bile birçok formatı belirtir - farklı boyutlar ve ondalık ve ikili üsler arasında bir ayrım. JSON özellikle ondalık sayılara çok uygundur, bu nedenle bazı standartların ikiliye sabitlemesi üzücü olurdu.
Adrian Ratnapala

5
@AdrianRatnapala +1 Gerçekten: JSON numaraları potansiyel olarak sonsuz hassasiyete sahiptir, bu nedenle boyut sınırı, hassas sınırı ve yuvarlama etkisi olmadığı için (serileştirici işleyebilirse) IEEE özelliklerinden çok daha iyidir.
Arnaud Bouchez

2
@ArnaudBouchez. Bununla birlikte, JSON hala NaN ve + -Infinity'i temsil eden dizeleri desteklemelidir. JSON herhangi bir IEEE formatına sabitlenmese bile, sayı formatını tanımlayan insanlar en azından IEEE754 wikipedia sayfasına bakmalı ve düşünmek için bir süre durmalıdır.
Adrian Ratnapala


Bu talihsiz değil. @CervEd tarafından verilen cevaba bakınız. İyi bir şey olan IEE754'e bağlı değildir (çoğu programlama dili IEEE754 kullanıyor olsa ve bu nedenle NaN vb. Durumunda ek işleme gerektirir).
Ludovic Kuty

16

Boş nesne desenini uyarlayabilir misiniz ve JSON'unuzda şu değerleri temsil eder:

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

Sonra kontrol ederken, türü kontrol edebilirsiniz

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

Java'da böyle bir şeyi uygulamak için serileştirme yöntemlerini geçersiz kılabileceğinizi biliyorum. Serileştirmenin nereden geldiğinden emin değilim, bu yüzden serileştirme yöntemlerinde nasıl uygulanacağı hakkında ayrıntılı bilgi veremem.


1
hmmm ... bu geçici bir çözümün cevabı; Gerçekten bir geçici çözüm istemiyordum, bunun yerine neden bu değerlerin hariç tutulduğunu. Ama yine de +1.
Jason S

2
@Zoidberg: undefinedbir anahtar kelime değil, küresel nesne üzerinde bir özelliktir
olliej

2
@Zoidberg: undefined, global nesne üzerindeki bir özelliktir - bir anahtar kelime değildir, bu nedenle "undefined" in thisglobal kapsamda true değerini döndürür. Ayrıca yapabileceğiniz undefined = 42ve if (myVar == undefined)(esasen) olabileceğiniz anlamına gelir myVar == 42. Bu undefined, varsayılan olarak mevcut olmayan ecmascript nee javascriptinin ilk günlerine geri dönüyor , bu yüzden insanlar sadece var undefinedküresel kapsamda yaptılar. Sonuç olarak undefined, mevcut siteleri bozmadan bir anahtar kelime yapılamadı ve bu yüzden tanımsız bir normal mülk olmak için her zaman mahkum olduk.
olliej

2
@olliej: undefined'in neden global nesnede bir özellik olduğunu düşündüğün hakkında hiçbir fikrim yok. Varsayılan olarak undefined araması, undefined öğesinin yerleşik değeridir. "Undefined = 42" ile geçersiz kılarsanız, tanımsız olarak değişken arama olarak eriştiğinizde, geçersiz kılınan değeri alırsınız. Ancak "zz = undefined; undefined = 42; x = {}; 'undefined old =' + (xa === zz) + ', undefined new =' + (xa === undefined)". Sembol aramalarını geçersiz kılabilseniz bile, null, undefined, NaN veya Infinity dahili değerlerini asla yeniden tanımlayamazsınız.
Jason S

2
@Jason undefinedküresel bir mülktür çünkü bu şekilde belirtilmiştir. ECMAScript-262 3. Baskı'nın 15.1.1.3'üne başvurun.
kanguru

11

"Infinity", "-Infinity" ve "NaN" dizelerinin tümü JS'de beklenen değerlere zorlanır. Bu yüzden JSON bu değerleri temsil etmek için doğru yolu dizeleri olduğunu iddia ediyorum.

> +"Infinity"
Infinity

> +"-Infinity"
-Infinity

> +"NaN"
NaN

JSON.stringify bunu varsayılan olarak yapmaz. Ama, bir yolu var:

> JSON.stringify({ x: Infinity }, function (k,v) { return v === Infinity ? "Infinity" : v; })
"{"x":"Infinity"}"

1
0/0, vb. Geçerli JSON değildir. Standardın sınırları içinde çalışmak zorundasınız ve dizeler işi güzel yapıyor.
teh_senaus

Aksine, bu tek pratik çözüm olduğunu düşünüyorum, ama giriş değeri "NaN", vb ise NaN döndüren bir işlev yapacağım. Dönüşüm yapma yolu kod enjeksiyon eğilimli.
Marco Sulla

3
JSON değerleri aritmetik ifadeler olamaz ... Standardı dil değişmez sözdiziminden ayrı hale getirmenin amacı, JSON'u kod olarak yürütmeden deeserializable yapmaktır. Emin değilim neden olamazdı NaNve Infinitybenzeri anahtar kelime değerler olarak eklenir trueve falseolsa.
Mark Reed

Daha açık, kullanabileceğimiz hale getirmek için Number("Infinity"), Number("-Infinity")veNumber("NaN")
HKTonyLee

Bu sihir gibi bir iş. JSON.parse("{ \"value\" : -1e99999 }")kolayca { value:-Infinity }javascript dönün . Sadece bundan daha büyük olabilecek özel sayı türüyle uyumlu değil
Thaina

7

Serileştirme koduna erişiminiz varsa Infinity'yi 1.0e + 1024 olarak temsil edebilirsiniz. Üs, bir çiftte temsil edilemeyecek kadar büyüktür ve serileştirildiğinde bu Sonsuzluk olarak temsil edilir. Webkit üzerinde çalışır, diğer json ayrıştırıcılarından emin değilsiniz!


4
IEEE754 128 bit kayan nokta sayısını destekler, böylece 1.0e5000 daha iyidir
Ton Plomp

2
Ton: Daha sonra 128 bit eklendi. 256 bit eklemeye karar verirlerse ne olur? Ardından daha fazla sıfır eklemeniz gerekir ve mevcut kod farklı şekilde davranır. Infinityher zaman olacak Infinity, neden bunu desteklemiyorsun?
uçan koyun

1
Zekice fikir! Farklı bir biçime geçmek ya da ayrıştırıcıya hantal geçici çözüm kodu eklemek üzereydim. Her durum için ideal değil, ama sonsuzluğun yakınsak bir diziye sadece optimize edilmiş bir kenar durumu olarak hizmet ettiği benim durumumda, sadece mükemmel ve daha büyük bir hassasiyet getirilse bile çoğunlukla doğru olurdu. Teşekkürler!
Veya Sharir

3
1, -1 ve 0 ..... mükemmel geçerli / ayrıştırılabilir sayılar, sadece /0sonuna eklediğinizde bu üç özel değer haline gelir . Kolayca ayrıştırılabilir, hemen görünür ve hatta değerlendirilebilir. Henüz standarda eklememiş olmaları affedilemez: {"Not A Number":0/0,"Infinity":1/0,"Negative Infinity":-1/0} << Neden olmasın? alert(eval("\"Not A Number\"") //works alert(eval("1/0")) //also works, prints 'Infinity'. Bahane yok.
Triynko


1

Geçerli IEEE Std 754-2008, iki farklı 64 bit kayan nokta gösterimi için tanımları içerir: ondalık 64 bit kayan nokta türü ve ikili 64 bit kayan nokta türü.

Dize yuvarlama sonra .99999990000000006aynıdır .9999999, IEEE ikili, 64-bit gösteriminde ama öyle değil aynı .9999999IEEE ondalık 64 bit temsilinde. 64-bit IEEE ondalık kayan nokta ondalık değerle aynı olmayan .99999990000000006değere yuvarlar ..9999999000000001.9999999

JSON yalnızca sayısal değerleri ondalık basamakların sayısal dizeleri olarak ele aldığından, olası iki IEEE sayısal kayan nokta değerinden hangisinin mümkün olduğunu belirlemek için hem IEEE ikili hem de ondalık kayan nokta gösterimlerini (IBM Power gibi) destekleyen bir sistem yoktur. niyetindeydi.


Bunun soru ile ne ilgisi var? (Sonsuzluk ve NaN hakkında)
Bryan

1

{"Anahtar": Infinity} gibi durumlar için potansiyel çözüm:

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;
   });

Genel fikir, geçersiz değerlerin oluşumlarını ayrıştırırken tanıyacağımız bir dize ile değiştirmek ve uygun JavaScript temsiliyle değiştirmek.


Açıkçası, JSON dizeniz Infinity veya IsNaN değerleri içeren bir durumla karşılaşırsanız, ayrıştırmaya çalıştığınızda başarısız olacağından, neden bu çözümün bir alt oy aldığını bilmiyorum. Bu tekniği kullanarak, önce IsNaN veya Infinity oluşumlarını başka bir şeyle değiştirirsiniz (bunları bu terimleri içerebilecek herhangi bir geçerli dizeden ayırmak için) ve uygun, geçerli JavaScript değerlerini döndürmek için JSON.parse (string, callback) kullanın. Bunu üretim kodunda kullanıyorum ve hiçbir sorun yaşamadım.
Şamel

Bu sonsuzluk dizeleri içinde berbat olmaz mı? Birçok kullanım için, bunun bir sorun olmadığını varsaymak muhtemelen güvenlidir, ancak çözüm tamamen sağlam değildir.
olejorgenb

1

Nedeni Standart ECMA-404'te sayfa ii'de belirtilmiştir JSON Veri Değişimi Sözdizimi, 1. Baskı

JSON sayılar konusunda agnostiktir. Herhangi bir programlama dilinde, sabit veya hareketli, ikili veya ondalık sayılarda çeşitli kapasitelerde ve tamamlayıcılarda çeşitli tipler olabilir. Bu, farklı programlama dilleri arasındaki değişimi zorlaştırabilir. Bunun yerine JSON yalnızca insanların kullandığı sayıların temsilini sunar: bir basamak dizisi. Tüm programlama dilleri, dahili gösterimlere katılmıyor olsalar bile, basamak dizilerinin nasıl anlamlandırılacağını bilir. Bu değiş tokuşa izin vermek için yeterlidir.

Sebep, birçoklarının iddia ettiği gibi, ECMA betiğinin temsili NaNve nedeni değildir Infinity. Sadelik, JSON'un temel tasarım ilkesidir.

Çok basit olduğu için JSON dilbilgisinin değişmesi beklenmiyor. Bu JSON'a temel bir gösterim olarak muazzam bir istikrar kazandırır


-3

Benim gibi serileştirme kodu üzerinde herhangi bir kontrole sahip değilseniz, NaN değerlerini null veya başka bir değerle değiştirerek aşağıdaki gibi bir kesmek gibi değiştirebilirsiniz:

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

Özünde, orijinal json ayrıştırıcısı geçersiz bir belirteç algıladığında .fail çağrılır. Daha sonra geçersiz belirteçlerin yerine bir dize değiştirme kullanılır. Benim durumumda serileştiricinin NaN değerlerini döndürmesi bir istisnadır, bu nedenle bu yöntem en iyi yaklaşımdır. Sonuçlar normalde geçersiz jeton içeriyorsa, $ .get kullanmamanız, bunun yerine JSON sonucunu manuel olarak almanız ve her zaman dize değişimini çalıştırmanız daha iyi olur.


21
Zeki, ama tamamen kusursuz değil. Deneyin{ "tune": "NaNaNaNaNaNaNaNa BATMAN", "score": NaN }
JJJ

1
ve jQuery kullanıyor olmalısınız. $ .Get () yok.
Jason S
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.