JSON.stringify() массив bizarreness с Prototype.js
Я пытаюсь выяснить, что пошло не так с моей сериализацией json, иметь текущую версию моего приложения и старый, и я нахожу некоторые удивительные различия в том, как работает JSON.stringify() (используя библиотеку JSON из json.org).
В старой версии моего приложения:
JSON.stringify({"a":[1,2]})
дает мне это;
"{\"a\":[1,2]}"
в новой версии,
JSON.stringify({"a":[1,2]})
дает мне это;
"{\"a\":\"[1, 2]\"}"
любая идея, что могло измениться, чтобы одна и та же библиотека помещала кавычки вокруг скобок массива в новую версию?
Ответы
Ответ 1
Так как JSON.stringify отправляется с некоторыми браузерами в последнее время, я бы предложил использовать его вместо Prototypes toJSON. Затем вы должны проверить окно. JSON && window.JSON.stringify и только включить библиотеку json.org в противном случае (через document.createElement('script')
...). Чтобы устранить несовместимость, используйте:
if(window.Prototype) {
delete Object.prototype.toJSON;
delete Array.prototype.toJSON;
delete Hash.prototype.toJSON;
delete String.prototype.toJSON;
}
Ответ 2
Функция JSON.stringify(), определенная в ECMAScript 5 и выше (Страница 21 - Объект JSON, псевдокод Страница 205), использует функцию toJSON(), когда она доступна на объектах.
Поскольку Prototype.js(или другая используемая вами библиотека) определяет функцию Array.prototype.toJSON(), массивы сначала преобразуются в строки с использованием Array.prototype.toJSON(), а затем строки, цитируемые JSON.stringify(), следовательно, неправильные дополнительные кавычки вокруг массивов.
Таким образом, решение является прямым и тривиальным (это упрощенная версия ответа Рафаэля Швейкера):
delete Array.prototype.toJSON
Это создает, конечно, побочные эффекты для библиотек, которые полагаются на свойство функции toJSON() для массивов. Но я нахожу это незначительным неудобством, учитывая несовместимость с ECMAScript 5.
Следует отметить, что объект JSON, определенный в ECMAScript 5, эффективно реализуется в современных браузерах, поэтому лучшим решением является соответствие стандарту и изменение существующих библиотек.
Ответ 3
Возможное решение, которое не повлияет на другие зависимости Prototype:
var _json_stringify = JSON.stringify;
JSON.stringify = function(value) {
var _array_tojson = Array.prototype.toJSON;
delete Array.prototype.toJSON;
var r=_json_stringify(value);
Array.prototype.toJSON = _array_tojson;
return r;
};
Это касается несовместимости Array с JSON с JSON.stringify, а также сохраняет функциональность JSON, поскольку другие библиотеки Prototype могут зависеть от нее.
Ответ 4
Изменить, чтобы сделать более точным:
Ключ командной строки проблемы находится в библиотеке JSON от JSON.org(и других реализаций объекта JSON ECMAScript 5):
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
Проблема заключается в том, что библиотека Prototype расширяет Array для включения метода toJSON, который объект JSON будет вызывать в коде выше. Когда объект JSON попадает в значение массива, он вызывает JSON в массиве, который определен в Prototype, и этот метод возвращает строковую версию массива. Следовательно, кавычки вокруг скобок массива.
Если вы удалите JSON из объекта Array, библиотека JSON должна работать правильно. Или просто используйте библиотеку JSON.
Ответ 5
Я думаю, что лучшим решением было бы включить это сразу после загрузки прототипа
JSON = JSON || {};
JSON.stringify = function(value) { return value.toJSON(); };
JSON.parse = JSON.parse || function(jsonsring) { return jsonsring.evalJSON(true); };
Это делает функцию прототипа доступной как стандартную JSON.stringify() и JSON.parse(), но сохраняет родной JSON.parse(), если она доступна, поэтому это делает вещи более совместимыми со старыми браузерами.
Ответ 6
Я не так свободно владею Prototype, но я видел это в docs:
Object.toJSON({"a":[1,2]})
Я не уверен, что это будет иметь ту же проблему, что и текущая кодировка.
Также существует более длинный учебник об использовании JSON с Prototype.
Ответ 7
Это код, который я использовал для одной и той же проблемы:
function stringify(object){
var Prototype = window.Prototype
if (Prototype && Prototype.Version < '1.7' &&
Array.prototype.toJSON && Object.toJSON){
return Object.toJSON(object)
}
return JSON.stringify(object)
}
Вы проверяете, существует ли прототип, затем вы проверяете версию. Если в старой версии используется Object.toJSON(если определено) во всех остальных случаях, резервное копирование на JSON.stringify()
Ответ 8
Вот как я с этим справляюсь.
var methodCallString = Object.toJSON? Object.toJSON(options.jsonMethodCall) : JSON.stringify(options.jsonMethodCall);
Ответ 9
Мое толерантное решение проверяет, является ли Array.prototype.toJSON вредным для JSON stringify и сохраняет его, когда это возможно, чтобы окружающий код работал должным образом:
var dummy = { data: [{hello: 'world'}] }, test = {};
if(Array.prototype.toJSON) {
try {
test = JSON.parse(JSON.stringify(dummy));
if(!test || dummy.data !== test.data) {
delete Array.prototype.toJSON;
}
} catch(e) {
// there only hope
}
}
Ответ 10
Как отмечали люди, это связано с Prototype.js - в частности версиями до 1.7. У меня была аналогичная ситуация, но мне приходилось иметь код, который работал, был ли Prototype.js или нет; это означает, что я не могу просто удалить Array.prototype.toJSON, поскольку я не уверен, что на него опирается. Для этой ситуации это лучшее решение, с которым я столкнулся:
function safeToJSON(item){
if ([1,2,3] === JSON.parse(JSON.stringify([1,2,3]))){
return JSON.stringify(item); //sane behavior
} else {
return item.toJSON(); // Prototype.js nonsense
}
}
Надеюсь, это поможет кому-то.
Ответ 11
Если вы не хотите убивать все и иметь код, который будет хорошо в большинстве браузеров, вы можете сделать это следующим образом:
(function (undefined) { // This is just to limit _json_stringify to this scope and to redefine undefined in case it was
if (true ||typeof (Prototype) !== 'undefined') {
// First, ensure we can access the prototype of an object.
// See http://stackoverflow.com/info/7662147/how-to-access-object-prototype-in-javascript
if(typeof (Object.getPrototypeOf) === 'undefined') {
if(({}).__proto__ === Object.prototype && ([]).__proto__ === Array.prototype) {
Object.getPrototypeOf = function getPrototypeOf (object) {
return object.__proto__;
};
} else {
Object.getPrototypeOf = function getPrototypeOf (object) {
// May break if the constructor has been changed or removed
return object.constructor ? object.constructor.prototype : undefined;
}
}
}
var _json_stringify = JSON.stringify; // We save the actual JSON.stringify
JSON.stringify = function stringify (obj) {
var obj_prototype = Object.getPrototypeOf(obj),
old_json = obj_prototype.toJSON, // We save the toJSON of the object
res = null;
if (old_json) { // If toJSON exists on the object
obj_prototype.toJSON = undefined;
}
res = _json_stringify.apply(this, arguments);
if (old_json)
obj_prototype.toJSON = old_json;
return res;
};
}
}.call(this));
Это кажется сложным, но это сложно только для большинства случаев использования.
Основная идея - переопределить JSON.stringify
, чтобы удалить toJSON
из объекта, переданного в качестве аргумента, затем вызвать старый JSON.stringify
и, наконец, восстановить его.