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 и, наконец, восстановить его.