Причудливость массива JSON.stringify () с помощью Prototype.js - PullRequest
81 голосов
/ 02 апреля 2009

Я пытаюсь выяснить, что пошло не так с моей сериализацией JSON, у меня есть текущая версия моего приложения и старая, и я нахожу некоторые удивительные различия в том, как работает JSON.stringify () (Использование библиотеки JSON из json.org).

В старой версии моего приложения:

 JSON.stringify({"a":[1,2]})

дает мне это;

"{\"a\":[1,2]}"

в новой версии,

 JSON.stringify({"a":[1,2]})

дает мне это;

"{\"a\":\"[1, 2]\"}"

есть идеи, что могло бы измениться, чтобы одна и та же библиотека помещала кавычки в скобки массива в новой версии?

Ответы [ 11 ]

76 голосов
/ 30 июня 2010

Поскольку JSON.stringify поставляется с некоторыми браузерами в последнее время, я бы предложил использовать его вместо toJSON Prototype. Затем вы проверите для window.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;
}
75 голосов
/ 07 июля 2011

Функция JSON.stringify (), определенная в ECMAScript 5 и выше (стр. 201 - объект JSON, псевдокод стр. 205) , использует функцию toJSON (), когда она доступна для объектов.

Поскольку Prototype.js (или другая используемая вами библиотека) определяет функцию Array.prototype.toJSON (), массивы сначала преобразуются в строки с использованием Array.prototype.toJSON (), а затем строка, заключенная в кавычки JSON.stringify () отсюда и неверные дополнительные кавычки вокруг массивов.

Таким образом, решение является простым и тривиальным (это упрощенная версия ответа Рафаэля Швейкера):

delete Array.prototype.toJSON

Это, конечно, создает побочные эффекты для библиотек, которые полагаются на свойство функции toJSON () для массивов. Но я считаю это незначительным неудобством, учитывая несовместимость с ECMAScript 5.

Следует отметить, что объект JSON, определенный в ECMAScript 5, эффективно реализован в современных браузерах, и поэтому наилучшим решением является соответствие стандарту и изменение существующих библиотек.

14 голосов
/ 13 декабря 2012

Возможное решение, которое не повлияет на другие зависимости 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 toJSON с JSON.stringify, а также сохраняет функциональность toJSON, так как другие библиотеки Prototype могут зависеть от этого.

7 голосов
/ 10 февраля 2010

Изменить, чтобы сделать немного более точным:

Код проблемного ключа кода находится в библиотеке JSON от JSON.org (и других реализаций объекта JSON ECMAScript 5):

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

Проблема в том, что библиотека Prototype расширяет массив Array, чтобы включить метод toJSON, который объект JSON будет вызывать в приведенном выше коде. Когда объект JSON достигает значения массива, он вызывает toJSON для массива, определенного в Prototype, и этот метод возвращает строковую версию массива. Следовательно, кавычки в скобках массива.

Если вы удалите toJSON из объекта Array, библиотека JSON должна работать правильно. Или просто используйте библиотеку JSON.

4 голосов
/ 25 августа 2009

Я думаю, что лучшим решением было бы включить это сразу после загрузки прототипа

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 (), если он доступен, что делает вещи более совместимыми со старыми браузерами.

2 голосов
/ 15 октября 2015

Это код, который я использовал для той же проблемы:

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

Вы проверяете, существует ли Prototype, затем вы проверяете версию. Если старая версия использует Object.toJSON (если он определен), во всех остальных случаях используется JSON.stringify ()

2 голосов
/ 02 апреля 2009

Я не очень хорошо разбираюсь в Prototype, но я видел это в документах :

Object.toJSON({"a":[1,2]})

Я не уверен, что это вызовет ту же проблему, что и текущая кодировка.

Существует также более длинное руководство по использованию JSON с Prototype.

1 голос
/ 08 ноября 2012

Как уже указывалось, это связано с 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
    }
}

Надеюсь, это кому-нибудь поможет.

1 голос
/ 02 февраля 2012

Мое толерантное решение проверяет, является ли 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
    }
}
1 голос
/ 02 апреля 2009

Вот как я с этим справляюсь.

var methodCallString =  Object.toJSON? Object.toJSON(options.jsonMethodCall) :  JSON.stringify(options.jsonMethodCall);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...