JSON оставил Бесконечность и NaN; Статус JSON в ECMAScript?

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 4

Это может быть связано с тем, что JSON предназначен для обмена данными, который может использоваться на различных платформах и позволяя NaN/Infinity сделать его менее портативным.

Ответ 5

Строки "Бесконечность", "-Инфинити" и "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"}"

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

Причина указана на странице ii в стандарте ECMA-404. Синтаксис обмена данными JSON, 1-е издание

JSON не зависит от чисел. В любом языке программирования может быть множество типов чисел различной емкости и дополнения, фиксированное или плавающее, двоичное или десятичное. Это может затруднить обмен между различными языками программирования. Вместо этого JSON предлагает только представление чисел, которые используют люди: последовательность цифр. Все языки программирования знают, как понимать последовательности цифр, даже если они не согласны с внутренними представлениями. Этого достаточно, чтобы позволить обмен.

Причина, как утверждают многие, не в том, что в сценариях ECMA NaN и Infinity. Простота - основной принцип проектирования JSON.

Поскольку это так просто, не ожидается, что грамматика JSON когда-либо изменится. Это дает JSON, как основополагающему обозначению, огромную стабильность

Ответ 9

Потенциальный обходной путь для таких случаев, как {"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.

Ответ 10

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

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

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