JSON исключил бесконечность и NaN; Статус JSON в ECMAScript? - PullRequest
153 голосов
/ 14 сентября 2009

Есть идеи, почему JSON исключил NaN и +/- Infinity? Это ставит Javascript в странную ситуацию, в которой объекты, которые в противном случае были бы сериализуемыми, отсутствуют, если они содержат значения NaN или +/- бесконечность.

Похоже, это было отлито из камня: см. RFC4627 и ECMA-262 (раздел 24.3.2, JSON.stringify, ПРИМЕЧАНИЕ 4, стр. 507 при последнем редактировании):

Конечные числа как бы выстраиваются в очередь, вызывая ToString(number). NaN и бесконечность независимо от знака представлены в виде строки null.

Ответы [ 8 ]

82 голосов
/ 15 сентября 2009

Infinity и NaN не являются ключевыми словами или чем-то особенным, они являются просто свойствами глобального объекта (как и undefined) и поэтому могут быть изменены. По этой причине JSON не включает их в спецификацию - по сути, любая истинная строка JSON должна иметь такой же результат в EcmaScript, если вы выполните eval(jsonString) или JSON.parse(jsonString).

Если бы это было разрешено, то кто-то мог бы ввести код, похожий на

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

в форум (или что-то еще), и тогда любое использование json на этом сайте может быть скомпрометировано.

52 голосов
/ 10 февраля 2012

По первоначальному вопросу: я согласен с пользователем "cbare" в том, что это неудачное упущение в JSON. IEEE754 определяет их как три специальных значения числа с плавающей запятой. Поэтому JSON не может полностью представлять числа с плавающей запятой IEEE754. На самом деле это даже хуже, поскольку JSON, как определено в ECMA262 5.1, даже не определяет, основаны ли его числа на IEEE754. Поскольку в процессе проектирования, описанном для функции stringify () в ECMA262, упоминаются три специальных значения IEEE, можно предположить, что на самом деле предполагалось поддерживать числа с плавающей запятой IEEE754.

Как еще одна точка данных, не связанная с вопросом: типы данных XML xs: float и xs: double do утверждают, что они основаны на числах с плавающей запятой IEEE754, и они поддерживают представление этих трех специальных значений (см. W3C XSD 1.0 Часть 2, Типы данных).

14 голосов
/ 14 сентября 2009

Не могли бы вы адаптировать шаблон нулевого объекта и представить в вашем 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 вы можете переопределить методы сериализации, чтобы реализовать такую ​​вещь. Не знаю, откуда твоя сериализация, поэтому я не могу подробно рассказать о том, как реализовать ее в методах сериализации.

9 голосов
/ 27 февраля 2015

Строки "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"}"
5 голосов
/ 20 февраля 2012

Если у вас есть доступ к коду сериализации, вы можете представить Infinity как 1.0e + 1024. Показатель степени слишком велик для двойного представления, и при десериализации это представляется как бесконечность. Работает над webkit, не уверен насчет других парсеров json!

1 голос
/ 28 октября 2015

Текущий стандарт IEEE 754-2008 включает определения для двух разных 64-битных представлений с плавающей точкой: десятичный 64-битный тип с плавающей точкой и двоичный 64-битный тип с плавающей точкой.

После округления строка .99999990000000006 совпадает с .9999999 в двоичном 64-битном представлении IEEE, но NOT совпадает с .9999999 в десятичном 64-битном представлении IEEE. В 64-битном IEEE десятичное число с плавающей точкой .99999990000000006 округляется до значения .9999999000000001, которое не совпадает с десятичным значением .9999999.

Поскольку JSON просто обрабатывает числовые значения как числовые строки десятичных цифр, система, которая поддерживает двоичные и десятичные представления с плавающей точкой IEEE (например, IBM Power), не может определить, какой из двух возможных числовых с плавающей запятой IEEE Точка значения предназначена.

0 голосов
/ 03 августа 2018

Потенциальный обходной путь для таких случаев, как {"key": Infinity}:

JSON.parse(theString.replace(/":(Infinity|-InNaN)/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.

0 голосов
/ 04 апреля 2013

Если вы, как и я, не можете контролировать код сериализации, вы можете иметь дело со значениями NaN, заменив их нулевым или любым другим значением в качестве хака, как показано ниже:

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

По сути, .fail будет вызываться, когда оригинальный анализатор json обнаружит недопустимый токен. Затем замена строки используется для замены недействительных токенов. В моем случае это исключение для сериализатора, возвращающего значения NaN, поэтому этот метод является лучшим подходом. Если результаты обычно содержат недопустимый токен, лучше не использовать $ .get, а вместо этого вручную извлекать результат JSON и всегда выполнять замену строки.

...