Chrome sendrequest 오류 : TypeError : 순환 구조를 JSON으로 변환


383

나는 다음을 가지고있다 ...

chrome.extension.sendRequest({
  req: "getDocument",
  docu: pagedoc,
  name: 'name'
}, function(response){
  var efjs = response.reply;
});

다음을 호출합니다 ..

case "getBrowserForDocumentAttribute":
  alert("ZOMG HERE");
  sendResponse({
    reply: getBrowserForDocumentAttribute(request.docu,request.name)
  });
  break;

그러나 내 코드는 "ZOMG HERE"에 도달하지 않지만 실행 중에 다음 오류가 발생합니다. chrome.extension.sendRequest

 Uncaught TypeError: Converting circular structure to JSON
 chromeHidden.JSON.stringify
 chrome.Port.postMessage
 chrome.initExtension.chrome.extension.sendRequest
 suggestQuery

누구 든지이 원인을 알고 있습니까?


2
순환 참조가있는 객체를 보내려고합니다. 무엇입니까 pagedoc?
Felix Kling

9
무엇을 의미합니까? 1.의 가치는 pagedoc무엇입니까? 2. 순환 참조 :a = {}; a.b = a;
Felix Kling

1
아 .. 고쳤다! 당신이 대답에 그것을 넣고 싶다면 나는 그것에 대해 신용을 줄 것입니다!
Skizit

5
node.js를 사용해보십시오 : util.inspect
boldnik

답변:


489

그것은 당신이 요청에 전달하는 객체 ( pagedoc)가 순환 참조를 가지고 있음을 의미합니다 .

var a = {};
a.b = a;

JSON.stringify 이와 같은 구조는 변환 할 수 없습니다.

NB : DOM 트리에 연결되지 않은 경우에도 순환 참조가있는 DOM 노드의 경우입니다. 각 노드에는 대부분의 경우 ownerDocument참조 하는 노드가 document있습니다. document관통 적어도 DOM 트리에 대한 참조를 보유 document.body하고 document.body.ownerDocument위로 지칭 document만, 이는 다시 하나 DOM 트리의 다수의 순환 참조.


2
감사! 이것은 내가 가진 문제를 설명합니다. 그러나 DOM 객체에 존재하는 순환 참조는 어떻게 문제를 일으키지 않습니까? JSON이 document객체를 문자열 화 합니까?
ascs

3
@asgs : 적어도 Chrome에서는 문제 발생합니다. Firefox는 그것에 대해 조금 더 똑똑한 것 같지만, 그것이 무엇을하고 있는지 정확히 모르겠습니다.
Felix Kling

이 오류를 "잡아서"처리 할 수 ​​있습니까?
Doug Molineux

2
@DougMolineux : 물론, try...catch이 오류를 잡는 데 사용할 수 있습니다 .
Felix Kling

4
: 불행히도 나는이 사용하여 종료 (뭔가 잘못을하고 수도) 일에 그것을 얻을 수 없었다 @FelixKling github.com/isaacs/json-stringify-safe
더그 몰리뉴

128

당으로 모질라에서 JSON 워드 프로세서 , JSON.Stringify두 번째 매개 변수가 censor나무를 구문 분석하는 동안 아이들이 항목을 무시 / 필터로 사용할 수 있습니다. 그러나 아마도 순환 참조를 피할 수 있습니다.

Node.js에서는 할 수 없습니다. 따라서 다음과 같이 할 수 있습니다.

function censor(censor) {
  var i = 0;

  return function(key, value) {
    if(i !== 0 && typeof(censor) === 'object' && typeof(value) == 'object' && censor == value) 
      return '[Circular]'; 

    if(i >= 29) // seems to be a harded maximum of 30 serialized objects?
      return '[Unknown]';

    ++i; // so we know we aren't using the original object anymore

    return value;  
  }
}

var b = {foo: {bar: null}};

b.foo.bar = b;

console.log("Censoring: ", b);

console.log("Result: ", JSON.stringify(b, censor(b)));

결과:

Censoring:  { foo: { bar: [Circular] } }
Result: {"foo":{"bar":"[Circular]"}}

불행히도 자동으로 원형이라고 가정하기 전에 최대 30 회의 반복이있는 것 같습니다. 그렇지 않으면 이것이 작동합니다. 나는 areEquivalent 여기 에서도 사용 했지만 JSON.Stringify30 회 반복 후에도 여전히 예외를 던졌습니다. 그래도 필요한 경우 최상위 수준에서 개체를 적절하게 표현할 수 있으면 충분합니다. 아마도 누군가 이것을 개선 할 수 있습니까? HTTP 요청 객체의 Node.js에서 다음을 얻습니다.

{
"limit": null,
"size": 0,
"chunks": [],
"writable": true,
"readable": false,
"_events": {
    "pipe": [null, null],
    "error": [null]
},
"before": [null],
"after": [],
"response": {
    "output": [],
    "outputEncodings": [],
    "writable": true,
    "_last": false,
    "chunkedEncoding": false,
    "shouldKeepAlive": true,
    "useChunkedEncodingByDefault": true,
    "_hasBody": true,
    "_trailer": "",
    "finished": false,
    "socket": {
        "_handle": {
            "writeQueueSize": 0,
            "socket": "[Unknown]",
            "onread": "[Unknown]"
        },
        "_pendingWriteReqs": "[Unknown]",
        "_flags": "[Unknown]",
        "_connectQueueSize": "[Unknown]",
        "destroyed": "[Unknown]",
        "bytesRead": "[Unknown]",
        "bytesWritten": "[Unknown]",
        "allowHalfOpen": "[Unknown]",
        "writable": "[Unknown]",
        "readable": "[Unknown]",
        "server": "[Unknown]",
        "ondrain": "[Unknown]",
        "_idleTimeout": "[Unknown]",
        "_idleNext": "[Unknown]",
        "_idlePrev": "[Unknown]",
        "_idleStart": "[Unknown]",
        "_events": "[Unknown]",
        "ondata": "[Unknown]",
        "onend": "[Unknown]",
        "_httpMessage": "[Unknown]"
    },
    "connection": "[Unknown]",
    "_events": "[Unknown]",
    "_headers": "[Unknown]",
    "_headerNames": "[Unknown]",
    "_pipeCount": "[Unknown]"
},
"headers": "[Unknown]",
"target": "[Unknown]",
"_pipeCount": "[Unknown]",
"method": "[Unknown]",
"url": "[Unknown]",
"query": "[Unknown]",
"ended": "[Unknown]"
}

나는 이것을하기 위해 작은 Node.js 모듈을 만들었습니다 : https://github.com/ericmuyser/stringy 자유롭게 개선 / 기여하십시오!


10
정규 함수를 반환하는 자체 실행 함수를 반환하는 함수가 처음 전달되는 것을 보았습니다. 나는 이것이 왜 일어 났는지 이해한다고 생각하지만, 나는 그 해결책을 스스로 찾았을 것이라고 생각하지 않으며 ,이 설정 이 필요한 다른 예제를 볼 수 있다면 이 기술을 더 잘 기억할 수 있다고 생각 합니다. 즉,이 설정 / 기술 (더 나은 단어가 없음) 또는 이와 유사한 기술 에 관한 문헌을 지적 할 수 있습니까?
Shawn

1
Shawn에게 +1 IEFE를 제거하십시오. 그것은 절대 쓸모없고 읽을 수 없습니다.
Bergi

1
검열 인수를 지적하기위한 thx! 순환 문제를 디버깅 할 수 있습니다. 내 경우에는 jquery 배열이 있었고 여기서는 정상적인 배열을 갖습니다. 그것들은 디버그 인쇄 모드에서 비슷하게 보입니다. IEFE에 대해, 나는 그것들이 절대적으로 필요하지 않은 곳에서 자주 사용되는 것을 보았고 Shawn과 Bergi와 동의합니다.
citykid

1
왜 그런지 잘 모르겠지만이 솔루션은 효과가없는 것 같습니다.
Nikola Schou 2016

1
@BrunoLM : 30 회 반복 제한으로 돌아 '[Unknown:' + typeof(value) + ']'가면 함수 및 기타 유형을 올바르게 처리하도록 검열 기를 수정하는 방법을 볼 수 있습니다.
Alex Pakka 2016 년

46

한 가지 접근 방식은 주 객체에서 객체와 기능을 제거하는 것입니다. 그리고 더 간단한 형태를

function simpleStringify (object){
    var simpleObject = {};
    for (var prop in object ){
        if (!object.hasOwnProperty(prop)){
            continue;
        }
        if (typeof(object[prop]) == 'object'){
            continue;
        }
        if (typeof(object[prop]) == 'function'){
            continue;
        }
        simpleObject[prop] = object[prop];
    }
    return JSON.stringify(simpleObject); // returns cleaned up JSON
};

2
나에게 완벽한 대답. 'function'키워드가 누락 되었습니까?
스테판 Loginov

28

나는 보통 이것을 해결하기 위해 순환 json npm 패키지를 사용합니다.

// Felix Kling's example
var a = {};
a.b = a;
// load circular-json module
var CircularJSON = require('circular-json');
console.log(CircularJSON.stringify(a));
//result
{"b":"~"}

참고 : Circular-json은 더 이상 사용되지 않으며 이제는 CircularJSON 작성자의 flatted를 사용합니다.

// ESM
import {parse, stringify} from 'flatted/esm';

// CJS
const {parse, stringify} = require('flatted/cjs');

const a = [{}];
a[0].a = a;
a.push(a);

stringify(a); // [["1","0"],{"a":"0"}]

에서 : https://www.npmjs.com/package/flatted


8

zainengineer의 답변을 바탕으로 ... 또 다른 접근법은 객체의 깊은 사본을 만들고 원형 참조를 제거하고 결과를 문자열로 만드는 것입니다.

function cleanStringify(object) {
    if (object && typeof object === 'object') {
        object = copyWithoutCircularReferences([object], object);
    }
    return JSON.stringify(object);

    function copyWithoutCircularReferences(references, object) {
        var cleanObject = {};
        Object.keys(object).forEach(function(key) {
            var value = object[key];
            if (value && typeof value === 'object') {
                if (references.indexOf(value) < 0) {
                    references.push(value);
                    cleanObject[key] = copyWithoutCircularReferences(references, value);
                    references.pop();
                } else {
                    cleanObject[key] = '###_Circular_###';
                }
            } else if (typeof value !== 'function') {
                cleanObject[key] = value;
            }
        });
        return cleanObject;
    }
}

// Example

var a = {
    name: "a"
};

var b = {
    name: "b"
};

b.a = a;
a.b = b;

console.log(cleanStringify(a));
console.log(cleanStringify(b));



4

NodeJS 에서이 문제를 다음과 같이 해결합니다.

var util = require('util');

// Our circular object
var obj = {foo: {bar: null}, a:{a:{a:{a:{a:{a:{a:{hi: 'Yo!'}}}}}}}};
obj.foo.bar = obj;

// Generate almost valid JS object definition code (typeof string)
var str = util.inspect(b, {depth: null});

// Fix code to the valid state (in this example it is not required, but my object was huge and complex, and I needed this for my case)
str = str
    .replace(/<Buffer[ \w\.]+>/ig, '"buffer"')
    .replace(/\[Function]/ig, 'function(){}')
    .replace(/\[Circular]/ig, '"Circular"')
    .replace(/\{ \[Function: ([\w]+)]/ig, '{ $1: function $1 () {},')
    .replace(/\[Function: ([\w]+)]/ig, 'function $1(){}')
    .replace(/(\w+): ([\w :]+GMT\+[\w \(\)]+),/ig, '$1: new Date("$2"),')
    .replace(/(\S+): ,/ig, '$1: null,');

// Create function to eval stringifyed code
var foo = new Function('return ' + str + ';');

// And have fun
console.log(JSON.stringify(foo(), null, 4));

2

jQuery로 아래 메시지를 작성하려고 할 때 동일한 오류가 발생했습니다. 순환 참조 reviewerName는 실수로에 할당 되었을 때 발생 합니다 msg.detail.reviewerName. JQuery의 .val ()이 문제를 해결했습니다. 마지막 줄을 참조하십시오.

var reviewerName = $('reviewerName'); // <input type="text" id="taskName" />;
var msg = {"type":"A", "detail":{"managerReview":true} };
msg.detail.reviewerName = reviewerName; // Error
msg.detail.reviewerName = reviewerName.val(); // Fixed

1

jQuery formvaliadator와 동일한 오류가 발생했지만 success.function 내부에서 console.log를 제거하면 작동했습니다.


0

내 경우에는 async서버 측에서 mongoose를 사용하여 문서를 가져 오는 기능을 사용할 때 해당 오류가 발생했습니다 . 그 이유는 메소드 await를 호출하기 전에 넣지 않았기 때문 find({})입니다. 해당 부분을 추가하면 문제가 해결되었습니다.


0

이것은 작동하며 어떤 속성이 원형인지 알려줍니다. 참조를 사용하여 객체를 재구성 할 수도 있습니다.

  JSON.stringifyWithCircularRefs = (function() {
    const refs = new Map();
    const parents = [];
    const path = ["this"];

    function clear() {
      refs.clear();
      parents.length = 0;
      path.length = 1;
    }

    function updateParents(key, value) {
      var idx = parents.length - 1;
      var prev = parents[idx];
      if (prev[key] === value || idx === 0) {
        path.push(key);
        parents.push(value);
      } else {
        while (idx-- >= 0) {
          prev = parents[idx];
          if (prev[key] === value) {
            idx += 2;
            parents.length = idx;
            path.length = idx;
            --idx;
            parents[idx] = value;
            path[idx] = key;
            break;
          }
        }
      }
    }

    function checkCircular(key, value) {
      if (value != null) {
        if (typeof value === "object") {
          if (key) { updateParents(key, value); }

          let other = refs.get(value);
          if (other) {
            return '[Circular Reference]' + other;
          } else {
            refs.set(value, path.join('.'));
          }
        }
      }
      return value;
    }

    return function stringifyWithCircularRefs(obj, space) {
      try {
        parents.push(obj);
        return JSON.stringify(obj, checkCircular, space);
      } finally {
        clear();
      }
    }
  })();

많은 노이즈가 제거 된 예 :

{
    "requestStartTime": "2020-05-22...",
    "ws": {
        "_events": {},
        "readyState": 2,
        "_closeTimer": {
            "_idleTimeout": 30000,
            "_idlePrev": {
                "_idleNext": "[Circular Reference]this.ws._closeTimer",
                "_idlePrev": "[Circular Reference]this.ws._closeTimer",
                "expiry": 33764,
                "id": -9007199254740987,
                "msecs": 30000,
                "priorityQueuePosition": 2
            },
            "_idleNext": "[Circular Reference]this.ws._closeTimer._idlePrev",
            "_idleStart": 3764,
            "_destroyed": false
        },
        "_closeCode": 1006,
        "_extensions": {},
        "_receiver": {
            "_binaryType": "nodebuffer",
            "_extensions": "[Circular Reference]this.ws._extensions",
        },
        "_sender": {
            "_extensions": "[Circular Reference]this.ws._extensions",
            "_socket": {
                "_tlsOptions": {
                    "pipe": false,
                    "secureContext": {
                        "context": {},
                        "singleUse": true
                    },
                },
                "ssl": {
                    "_parent": {
                        "reading": true
                    },
                    "_secureContext": "[Circular Reference]this.ws._sender._socket._tlsOptions.secureContext",
                    "reading": true
                }
            },
            "_firstFragment": true,
            "_compress": false,
            "_bufferedBytes": 0,
            "_deflating": false,
            "_queue": []
        },
        "_socket": "[Circular Reference]this.ws._sender._socket"
    }
}

JSON.parse ()를 다시 생성하려면 [Circular Reference]태그를 찾는 속성을 반복하십시오 . 그런 다음 잘라낸 다음 this루트 개체 로 설정하여 평가하십시오 .

해킹 될 수있는 것을 평가하지 마십시오. 더 나은 방법은 string.split('.')속성을 이름으로 조회하여 참조를 설정하는 것입니다.

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