Chrome gönderme isteği hatası: TypeError: Dairesel yapıyı JSON'a dönüştürme


384

Aşağıdaki var ...

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

Aşağıdaki çağırır ..

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

Ancak, kodum asla "ZOMG HERE" ulaşmaz, ama yerine çalışırken aşağıdaki hatayı atar chrome.extension.sendRequest

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

Bunun neden olduğuna dair bir fikri olan var mı?


2
İçinde dairesel referansları olan bir nesne göndermeye çalışıyorsunuz. Nedir pagedoc?
Felix Kling

9
Ne ile ne demek istiyorum? 1. değeri nedir pagedoc? 2. Dairesel referans:a = {}; a.b = a;
Felix Kling

1
Ahh .. bunu düzelttim! Eğer cevabını vermek istersen sana kredi vereceğim!
Skizit

5
node.js kullanmayı deneyin: util.inspect
boldnik

Yanıtlar:


489

Bu, istekte ilettiğiniz nesnenin (sanırım öyle pagedoc) dairesel bir referansa sahip olduğu anlamına gelir :

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

JSON.stringify böyle yapıları dönüştüremez.

DİKKAT : DOM ağacına bağlı olmasalar bile, dairesel referansları olan DOM düğümlerinde bu durum söz konusudur. Her bir düğüm bir sahiptir ownerDocumentkarşılık gelmekte olup, documentçoğu durumda. documentkonusu en az DOM ağacına bir referans yer alır document.bodyve document.body.ownerDocumentarka değinmektedir documentsadece olan yine bir DOM ağacındaki katmerli dairesel referanslar.


2
Teşekkürler! Bu benim yaşadığım sorunu açıklıyor. Ancak DOM nesnelerinde bulunan döngüsel başvuru nasıl sorun yaratmaz? JSON bir documentnesneyi dizgi yapar mı?
asgs

3
@asgs: O does en azından Chrome'da, sorunlar neden olur. Firefox bu konuda biraz daha akıllı görünüyor ama tam olarak ne yaptığını bilmiyorum.
Felix Kling

Bu hatayı "yakalamak" ve ele almak mümkün müdür?
Doug Molineux

2
@DougMolineux: Elbette, try...catchbu hatayı yakalamak için kullanabilirsiniz .
Felix Kling

4
@FelixKling Ne yazık ki çalışmak için alamadım (yanlış bir şey yapıyor olabilir) Bunu kullanarak sona erdi: github.com/isaacs/json-stringify-safe
Doug Molineux

128

Gereğince Mozilla de JSON docs , JSON.Stringifyikinci parametresi vardır censorağaç ayrıştırılırken çocuk öğeleri görmezden / filtreye kullanılabilir. Bununla birlikte, belki de dairesel referanslardan kaçınabilirsiniz.

Node.js'de yapamayız. Böylece böyle bir şey yapabiliriz:

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

Sonuç:

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

Ne yazık ki, otomatik olarak dairesel olduğunu varsaymadan önce en fazla 30 yineleme var gibi görünüyor. Aksi takdirde, bu işe yaramalıdır. Hatta kullandımareEquivalent Buradan , ancak JSON.Stringify30 tekrardan sonra hala istisnayı atıyor. Yine de, gerçekten ihtiyacınız varsa, nesnenin en üst düzeyde iyi bir temsilini elde etmek için yeterince iyi. Belki birileri bu konuda gelişebilir? Bir HTTP istek nesnesi için Node.js'de şunu alıyorum:

{
"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]"
}

Bunu yapmak için küçük bir Node.js modülü oluşturdum: https://github.com/ericmuyser/stringy Geliştirmek / katkıda bulunmaktan çekinmeyin!


10
İlk defa, normal bir işlev döndüren, kendini yürüten bir işlev döndüren bir işlev görüldüğünü görüyorum. Bunun neden yapıldığını anladığımı düşünüyorum, ancak bu çözümü kendim bulacağımı sanmıyorum ve bu kurulumun gerekli olduğu diğer örnekleri görebilseydim bu tekniği daha iyi hatırlayabildiğimi hissediyorum . Bununla birlikte, bu kurulum / teknik (daha iyi bir kelime eksikliği için) veya benzerleriyle ilgili herhangi bir literatüre işaret edebilir misiniz ?
Shawn

1
Shawn'a +1. Lütfen IEFE'yi kaldırın, kesinlikle işe yaramaz ve okunaksız.
Bergi

1
sansür argünü işaret ettiğiniz için teşekkürler! döngüsel sorunların ayıklanmasına izin verir. benim durumumda nerede bir normal dizi var thougth bir jquery dizi vardı. her ikisi de hata ayıklama yazdırma modunda benzer görünür. IEFE hakkında, onlara kesinlikle ihtiyaç duyulmayan yerlerde sık sık kullanıldığını görüyorum ve Shawn ve Bergi ile bunun böyle bir durum olduğu konusunda hemfikirim.
citykid

1
Neden olduğundan emin değilim, ama bu çözüm benim için işe yaramıyor gibi görünüyor.
Nikola Schou

1
@ BrunoLM: 30 iterasyon sınırı için, geri dönerseniz, '[Unknown:' + typeof(value) + ']'fonksiyonları ve diğer bazı türleri düzgün bir şekilde tedavi etmek için sansürün nasıl düzeltileceğini göreceksiniz.
Alex Pakka

46

Bir yaklaşım, nesne ve fonksiyonları ana nesneden ayırmaktır. Ve daha basit bir formu dizgi haline getirin

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
Benim için mükemmel cevap. Belki 'işlev' anahtar kelimesi kaçırıldı?
Stepan Loginov

28

Bunu çözmek için normalde dairesel-json npm paketini kullanıyorum.

// 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":"~"}

Not: dairesel json kullanımdan kaldırıldı, şimdi flatted kullanıyorum (CircularJSON'un yaratıcısından):

// 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"}]

from: https://www.npmjs.com/package/flatted


8

Zainengineer'ın cevabına dayanarak ... Bir diğer yaklaşım, nesnenin derin bir kopyasını yapmak ve dairesel referansları şeritlemek ve sonucu dizgi yapmaktır.

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 bu sorunu bu şekilde gidermek:

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

Aşağıdaki mesajı jQuery ile oluşturmaya çalışırken aynı hatayla karşılaştım. Dairesel referans reviewerName, yanlışlıkla atanmışken gerçekleşir msg.detail.reviewerName. JQuery's .val () sorunu düzeltti, son satıra bakın.

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

Aynı hatayı jQuery formvaliadator ile alıyordum, ancak bir console.log'u başarı: işlev içinde kaldırdığımda işe yaradı.


0

Benim asyncdurumum için, sunucu tarafında mygoose kullanarak belgeleri almak için işlevi kullanırken bu hatayı alıyordum . Metodu awaitçağırmadan önce koymayı unuttum find({}). Bu kısmı eklemek sorunumu çözdü.


0

Bu, hangi özelliklerin dairesel olduğunu gösterir ve size söyler. Nesnenin referanslarla yeniden yapılandırılmasına da izin verir

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

Çok fazla gürültü çıkarılmış örnek:

{
    "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"
    }
}

Yeniden yapılandırmak için JSON.parse () öğesini çağırın, ardından [Circular Reference]etiketi arayan özellikler arasında döngü yapın . Sonra bunu kesin ve ... thiskök nesnesine ayarlayarak ... değerlendirin .

Saldırılabilecek hiçbir şeyi değerlendirmeyin. Daha iyi uygulama string.split('.'), referansı ayarlamak için özellikleri ada göre aramak olacaktır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.