Prototype.js의 JSON.stringify () 배열 기괴함


89

내 json 직렬화에 무엇이 잘못되었는지 알아 내고, 내 앱의 현재 버전과 이전 버전을 가지고 있으며, JSON.stringify () 작동 방식에서 놀라운 차이점을 찾고 있습니다 (json.org의 JSON 라이브러리 사용). ).

내 앱의 이전 버전에서 :

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

나에게 이것을 준다;

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

새 버전에서는

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

나에게 이것을 준다;

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

새 버전에서 동일한 라이브러리가 배열 괄호를 따옴표로 묶기 위해 변경 될 수 있었던 것이 무엇인지 아십니까?


4
최신 버전에서 소개 한 Prototype 라이브러리와 충돌하는 것 같습니다. Prototype에서 배열을 포함하는 json 객체를 문자열 화하는 방법에 대한 아이디어가 있습니까?
morgancodes 2009

26
사람들은 글로벌 내장 객체 (프로토 타입 프레임 워크와 마찬가지로)로 엉망으로 삼가해야하는 이유의 것을
제라르 리마

답변:


82

최근 JSON.stringify가 일부 브라우저와 함께 제공되었으므로 Prototype의 toJSON 대신 사용하는 것이 좋습니다. 그런 다음 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;
}

자신의 코드에서 window.JSON을 확인할 필요가 없습니다. json.org 스크립트가 자체적으로 수행합니다.
zcrar70

그럴 수도 있지만 필요하지 않더라도 전체 스크립트 파일을로드해야합니다.
Raphael Schweikert

11
삭제 Array.prototype.toJSON : 사실, 유일한 문은 문제를 처리하는 데 필요한
진 빈센트

1
정말 고맙습니다. 제가 지금 일하고있는 회사는 현재 우리 코드의 대부분에서 프로토 타입을 사용하고 있으며 이것은보다 현대적인 라이브러리를 사용하는 데있어 생명을 구했습니다. 그렇지 않으면 모든 것이 깨질 것입니다.
krob

1
나는 DAYS에 대한이 답변을 찾고 있었고 그것을 알아 내려고 두 개의 다른 SO 질문을 게시했습니다. 내가 세 번째를 입력하면서 이것을 관련 질문으로 보았습니다. 정말 고맙습니다!
Matthew Herbst 2014

78

ECMAScript 5 이상 에서 정의 된 JSON.stringify () 함수 ( 201 페이지-JSON 객체, 의사 코드 페이지 205) 는 객체에서 사용할 수있는 경우 toJSON () 함수를 사용합니다.

Prototype.js (또는 사용중인 다른 라이브러리)가 Array.prototype.toJSON () 함수를 정의하기 때문에 배열은 먼저 Array.prototype.toJSON ()을 사용하여 문자열로 변환 된 다음 JSON.stringify ()로 인용 된 문자열로 변환됩니다. 배열 주변에 잘못된 추가 따옴표가 있습니다.

따라서 솔루션은 간단하고 사소합니다 (이것은 Raphael Schweikert의 답변을 단순화 한 버전입니다).

delete Array.prototype.toJSON

이것은 물론 배열에 대한 toJSON () 함수 속성에 의존하는 라이브러리에 대한 부작용을 생성합니다. 하지만 ECMAScript 5와의 비 호환성을 고려하면 사소한 불편 함을 느낍니다.

ECMAScript 5에 정의 된 JSON 개체는 최신 브라우저에서 효율적으로 구현되므로 최상의 솔루션은 표준을 준수하고 기존 라이브러리를 수정하는 것입니다.


5
이것은 배열의 추가 인용에 대한 가장 간결한 대답입니다.
tmarthal

15

다른 프로토 타입 종속성에 영향을주지 않는 가능한 솔루션은 다음과 같습니다.

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

이는 JSON.stringify와의 Array toJSON 비 호환성을 처리하고 다른 Prototype 라이브러리가 의존 할 수 있으므로 toJSON 기능도 유지합니다.


웹 사이트에서이 스 니펫을 사용했습니다. 문제를 일으키고 있습니다. 결과적으로 배열의 toJSON 속성이 정의되지 않습니다. 그것에 대한 어떤 조언?
Sourabh

1
위의 스 니펫을 사용하여 JSON.stringify를 재정의하기 전에 Array.prototype.toJSON이 정의되어 있는지 확인하십시오. 내 테스트에서 잘 작동합니다.
akkishore

2
나는 if(typeof Prototype !== 'undefined' && parseFloat(Prototype.Version.substr(0,3)) < 1.7 && typeof Array.prototype.toJSON !== 'undefined'). 효과가있었습니다.
Sourabh

1
큰. Prototype 1.7이 문제가 될 때까지만 가능합니다. :)
akkishore

1
이 문제는 버전 <1.7
Sourabh

9

좀 더 정확하게 수정하세요.

코드의 문제 핵심 부분은 JSON.org의 JSON 라이브러리 (및 ECMAScript 5의 JSON 객체의 다른 구현)에 있습니다.

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

문제는 Prototype 라이브러리가 위의 코드에서 JSON 개체가 호출하는 toJSON 메서드를 포함하도록 Array를 확장한다는 것입니다. JSON 객체가 배열 값에 도달하면 Prototype에 정의 된 배열에서 toJSON을 호출하고 해당 메서드는 배열의 문자열 버전을 반환합니다. 따라서 배열 대괄호를 따옴표로 묶습니다.

Array 객체에서 toJSON을 삭제하면 JSON 라이브러리가 제대로 작동합니다. 또는 JSON 라이브러리를 사용하십시오.


2
이것은 ECMAScript 5에서 JSON.stringify ()가 정의 된 정확한 방식이기 때문에 라이브러리의 버그가 아닙니다. 문제는 prototype.js에 있으며 해결책은 다음과 같습니다. delete Array.prototype.toJSON 이것은 약간의 측면이 있습니다 프로토 타입 toJSON 직렬화에 대한 효과,하지만 난 프로토 타입 인 ECMAScript 5와 함께 가지고있는 호환성의 점에서 이러한 사소한 발견
진 빈센트

프로토 타입 라이브러리는 Object.prototype을 확장하지 않고 Array.prototype을 확장합니다. JavaScript의 typeof 배열도 "object"를 반환하지만 동일한 "생성자"와 프로토 타입이 없습니다. 문제를 해결하려면 다음을 수행해야합니다. "delete Array.prototype.toJSON;"
Jean Vincent

@Jean 공정하게 말하면 Prototype은 Object를 포함한 모든 기본 네이티브 개체를 확장합니다. 그러나 좋아, 내가 다시 점을 볼 :) 내 대답은 더 나은 도움을 주셔서 감사합니다

Prototype은 for .. 문제를 피하기 위해 오랫동안 "Object.prototype"확장을 중단했습니다 (하지만 어떤 버전인지는 기억이 나지 않습니다). 그것은 이제 네임 스페이스와 같은 객체의 정적 속성 (이 훨씬 안전합니다) 확장 : api.prototypejs.org/language/Object을
진 빈센트에게

Jean, 실제로 그것은 정확히 도서관의 버그입니다. 객체에 toJSON이있는 경우이를 호출하고 그 결과를 사용해야하지만 따옴표로 묶어서는 안됩니다.
grr

4

더 나은 해결책은 프로토 타입이로드 된 직후에 이것을 포함하는 것입니다.

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 ()를 유지하므로 이전 브라우저와 더 잘 호환됩니다.


전달 된 'value'가 Object 인 경우 JSON.stringify 버전이 작동하지 않습니다. 대신 이렇게해야합니다. JSON.stringify = function (value) {return Object.toJSON (value); };
akkishore

2

나는 Prototype에 능숙하지 않지만 문서 에서 이것을 보았다 .

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

그래도 현재 인코딩과 동일한 문제가 있는지 확실하지 않습니다.

Prototype과 함께 JSON을 사용 하는 방법에 대한 더 긴 자습서 도 있습니다 .


2

이것은 동일한 문제에 사용한 코드입니다.

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 ()로 대체됩니다.


1

여기 내가 그것을 다루는 방법이 있습니다.

var methodCallString =  Object.toJSON? Object.toJSON(options.jsonMethodCall) :  JSON.stringify(options.jsonMethodCall);

1

내 관용 솔루션은 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

사람들이 지적했듯이 이것은 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
    }
}

누군가를 도울 것입니다.


0

모든 것을 죽이고 싶지 않고 대부분의 브라우저에서 괜찮은 코드가 있다면 다음과 같이 할 수 있습니다.

(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/questions/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하고 old를 호출 JSON.stringify하고 마지막으로 복원하는 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.