Dairesel bir yapıyı JSON benzeri bir biçimde nasıl yazdırabilirim?


680

Ben JSON dönüştürmek ve göndermek istiyorum büyük bir nesne var. Ancak dairesel bir yapıya sahiptir. Dairesel referanslar varsa atmak ve stringize edilebilecekleri göndermek istiyorum. Bunu nasıl yaparım?

Teşekkürler.

var obj = {
  a: "foo",
  b: obj
}

Nesneleri dizgi haline getirmek istiyorum:

{"a":"foo"}

5
Ayrıştırmak istediğiniz döngüsel bir referansa sahip örnek bir nesne gönderebilir misiniz?
TWickz

3
gibi bir şey bu ?
Alvin Wong


2
Partiye geç ama bununla başa çıkmak için bir github projesi var.
Preston S

Yanıtlar:


606

JSON.stringifyÖzel bir değiştiriciyle kullanın . Örneğin:

// Demo: Circular reference
var circ = {};
circ.circ = circ;

// Note: cache should not be re-used by repeated calls to JSON.stringify.
var cache = [];
JSON.stringify(circ, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    // Duplicate reference found, discard key
    if (cache.includes(value)) return;

    // Store value in our collection
    cache.push(value);
  }
  return value;
});
cache = null; // Enable garbage collection

Bu örnekteki değiştirici% 100 doğru değil ("yinelenen" tanımınıza bağlı olarak). Aşağıdaki durumda, bir değer atılır:

var a = {b:1}
var o = {};
o.one = a;
o.two = a;
// one and two point to the same object, but two is discarded:
JSON.stringify(o, ...);

Ancak konsept şu anlama gelir: Özel bir değiştirici kullanın ve ayrıştırılan nesne değerlerini takip edin.

Es6 ile yazılmış bir yardımcı program işlevi olarak:

// safely handles circular references
JSON.safeStringify = (obj, indent = 2) => {
  let cache = [];
  const retVal = JSON.stringify(
    obj,
    (key, value) =>
      typeof value === "object" && value !== null
        ? cache.includes(value)
          ? undefined // Duplicate reference found, discard key
          : cache.push(value) && value // Store value in our collection
        : value,
    indent
  );
  cache = null;
  return retVal;
};

// Example:
console.log('options', JSON.safeStringify(options))

1
@Harry Hata nedir? Herhangi bir yanlışlık varsa cevabı memnuniyetle düzeltirim.
Rob W

1
@CruzDiablo DOM'un seri hale getirilmesi genellikle anlamsızdır. Bununla birlikte, amaçlarınız için anlamlı bir serileştirme yöntemi düşünebiliyorsanız, DOM nesnelerine serileştirilmiş bir özel eklemeyi deneyebilirsiniz: Node.prototype.toJSON = function() { return 'whatever you think that is right'; };(daha genel / spesifik bir şey istiyorsanız, prototip ağacında herhangi bir şey deneyin: HTMLDivElement HTMLElement uygular Öğe uygular Düğüm EventTarget uygular; Not: bu tarayıcıya bağlı olabilir, önceki ağaç Chrome için geçerlidir)
Rob W

7
bu yanlıştır, çünkü gerçekten döngüsel bir yapıda olmasa bile, iki kez bulunan nesnelerin ikinci görünümünü atlayacaktır. var a={id:1}; JSON.stringify([a,a]);
user2451227

3
@ user2451227 "Bu örnekteki değiştirici% 100 doğru değil (" yinelenen "tanımınıza bağlı olarak). Ancak kavram duruyor: Özel bir değiştirici kullanın ve ayrıştırılan nesne değerlerini takip edin."
Rob W

4
Buradaki GC endişesi tartışmalı bir şekilde gereksizdir. Bu tek bir komut dosyası olarak çalıştırılırsa, komut dosyası hemen sonlandırılır. Bu, uygulama için bir işlev içinde kapsüllenirse cache, ulaşılamaz geliştirici
tr

704

Node.js olarak, kullanabilirsiniz util.inspect (nesne) . Dairesel bağlantıları otomatik olarak "[Dairesel]" ile değiştirir.


Yerleşik olmanıza rağmen (kurulum gerekmez) , içe aktarmanız gerekir

import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or 
var util = require('util')
Kullanmak için,
console.log(util.inspect(myObject))

Ayrıca denetlemek için seçenekler nesnesini geçirebileceğinizi unutmayın (yukarıdaki bağlantıya bakın)

inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])



Lütfen aşağıdaki yorumculara okudum ve şudos verin ...


134
util yerleşik bir modüldür, onu kurmanıza gerek yoktur.
Mitar

10
console.log (util.inspect (obj))
starsinmypockets

19
@Mitar yerleşiktir, ancak yine de modülü yüklemeniz gerekirvar util = require('util');
bodecker

14
Benim gibi bir sakın olma, sadece obj_str = util.inspect(thing) <s> garbage_str = JSON.stringify(util.inspect(thing))</s> DEĞİL
ThorSummoner

7
Bu, kontrol tipleri ile uğraşmaktan çok daha iyidir. Stringify neden böyle çalışamıyor? Dairesel bir referans olduğunu biliyorsa, neden bunu görmezden gelmesi söylenemez ???
Chris Peacock

141

Acaba neden henüz MDN sayfasından doğru çözümü yayınlamadı ...

const getCircularReplacer = () => {
  const seen = new WeakSet();
  return (key, value) => {
    if (typeof value === "object" && value !== null) {
      if (seen.has(value)) {
        return;
      }
      seen.add(value);
    }
    return value;
  };
};

JSON.stringify(circularReference, getCircularReplacer());

Görülen değerler dizide değil , bir kümede saklanmalıdır (replacer her öğede çağrılır ) ve zincirdeki JSON.stringify her öğeyi dairesel referansa götürmeye gerek yoktur .

Kabul edilen cevapta olduğu gibi, bu çözüm sadece dairesel değerleri değil, tekrar eden tüm değerleri kaldırır . Ama en azından üstel bir karmaşıklığa sahip değil.


Düzgün, ama bu sadece ES2015. IE desteği yok.
Martin Capodici

43
Yoda: "Eğer hala IE biri destekliyorsa, o zaman bir transpiler kullanın gerekir."
İspanya Treni

1
replacer = () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return; } seen.add(value); } return value; }; } () => { const seen = new WeakSet(); return (key, value) => { if (typeof value === "object" && value !== null) { if (seen.has(value)) { return; } seen.add(value); … JSON.stringify({a:1, b: '2'}, replacer)undefinedkrom döndürür
roberto tomás

1
React + Typescript'te çalışır. teşekkürler
user3417479

76

sadece yap

npm i --save circular-json

o zaman js dosyanızda

const CircularJSON = require('circular-json');
...
const json = CircularJSON.stringify(obj);

https://github.com/WebReflection/circular-json

NOT: Bu paketle ilgim yok. Ama bunu bunun için kullanıyorum.

2020 Güncellemesi

Not CircularJSON sadece ve bakım içindedir Lütfen flatted onun halefi olduğunu.


Çok teşekkürler! Büyük kütüphane, tonlarca zaman kazandırdı. Süper küçük (sadece 1.4KB küçültülmüş).
Brian Haak

16
Bence bir modül kullanmak için "sadece" yapmaktan daha fazla gerekçe gerekebilir. JSONPrensipte üzerine yazmak harika değil .
Edwin

Saplama testi için kullanılacak bir nesneyi kopyalamam gerekiyordu. Bu cevap mükemmeldi. Nesneyi kopyaladım ve ardından geçersiz kılmayı kaldırdım. Teşekkürler!!
Chris Sharp

1
Yazara göre, bu paket kullanımdan kaldırıldı. CircularJSON yalnızca bakımdadır, halefi düzdür. Link: github.com/WebReflection/flatted#flatted
Robert Molina

3
Dikkat edin, 'flatted' (ve dairesel-json?) Paketi JSON.stringify () işlevini çoğaltmaz. Kendi JSON olmayan biçimini oluşturur. (örneğin, Flatted.stringify({blah: 1})sonuçlarda [{"blah":1}]) Birisinin bu konuda bir sorun yaratmaya çalıştığını görüyorum ve yazar onları azarladı ve sorunu yorumlara kilitledi.
jameslol

48

Trindaz'ın çözümünü gerçekten sevdim - daha ayrıntılı, ancak bazı hatalar vardı. Onları da sevenler için düzelttim.

Ayrıca, önbellek nesnelere bir uzunluk sınırı ekledim.

Yazdırdığım nesne gerçekten büyükse - yani sonsuz büyüklükte - algoritmamı sınırlandırmak istiyorum.

JSON.stringifyOnce = function(obj, replacer, indent){
    var printedObjects = [];
    var printedObjectKeys = [];

    function printOnceReplacer(key, value){
        if ( printedObjects.length > 2000){ // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects
        return 'object too long';
        }
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if ( key == ''){ //root element
             printedObjects.push(obj);
            printedObjectKeys.push("root");
             return value;
        }

        else if(printedObjIndex+"" != "false" && typeof(value)=="object"){
            if ( printedObjectKeys[printedObjIndex] == "root"){
                return "(pointer to root)";
            }else{
                return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase()  : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")";
            }
        }else{

            var qualifiedKey = key || "(empty key)";
            printedObjects.push(value);
            printedObjectKeys.push(qualifiedKey);
            if(replacer){
                return replacer(key, value);
            }else{
                return value;
            }
        }
    }
    return JSON.stringify(obj, printOnceReplacer, indent);
};

Bu satırda boş bir denetim eksik: return "(bkz." + (!! value.constructor? Value.constructor.name.toLowerCase (): typeof (value)) + "anahtarlı" + basılıObjectKeys [basılıObjIndex] + ")";
Isak

Memnuniyetle ekleyeceğim. Şimdiye kadar herhangi bir sorun yaşadığım için null olabilecekleri bana bildirin.
guy mograbi

2
// tarayıcılar 20K'dan fazla yazdırmaz - Ama sınırı 2k olarak koyarsınız. Belki gelecek için değişebilir?
Pochen

38

@ RobW'un cevabı doğru, ama bu daha performanslı! Bir hashmap / set kullandığından:

const customStringify = function (v) {
  const cache = new Set();
  return JSON.stringify(v, function (key, value) {
    if (typeof value === 'object' && value !== null) {
      if (cache.has(value)) {
        // Circular reference found
        try {
          // If this value does not reference a parent it can be deduped
         return JSON.parse(JSON.stringify(value));
        }
        catch (err) {
          // discard key if value cannot be deduped
         return;
        }
      }
      // Store value in our set
      cache.add(value);
    }
    return value;
  });
};

Dairesel referansları olan derin yuvalanmış nesneler için stringifyDeep => github.com/ORESoftware/safe-stringify
Alexander Mills

Set uygulamasının sadece başlık altında bir dizi ve indexOf kullanması muhtemeldir, ancak bunu doğrulamamıştım.
Alexander Mills

Bu, farklı değerlere sahip olsa bile alt düğümleri olan üst düğümleri kaldırmaktır - örneğin - {"a":{"b":{"a":"d"}}}ve hatta boş nesnesi olan düğümleri kaldırmak {}
Sandip Pingle

Sandip'in bir örneğini gösterebilir misin? bir gist.github.com ya da değil oluşturmak
Alexander Mills

Mükemmel !!! İlk olarak (üstten, ancak kontrol edilen 2-3 işlev çözümü) burada node.js ve Fission ;-) - kütüphaneleri askıya alınmış çalışma çözümü.
Tom,

37

JSON.decycleDouglas Crockford tarafından uygulanan bir yöntem olduğunu da unutmayın . Onun cycle.js dosyasına bakın . Bu, hemen hemen tüm standart yapıları dizginlemenizi sağlar:

var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.

Orijinal nesneyi retrocycleyöntemle de yeniden oluşturabilirsiniz . Dolayısıyla, nesneleri dizgi haline getirmek için döngüleri kaldırmanız gerekmez.

Ancak bu olacak değil (gerçek hayattaki kullanım senaryoları içinde döngülerin tipik nedenidir) DOM Düğümler için çalışıyorum. Örneğin, bu:

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));

Bu sorunu çözmek için bir çatal yaptım (bkz . Cycle.js çatalım ). Bu iyi çalışmalıdır:

var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));

Çatalımda JSON.decycle(variable)orijinalde olduğu gibi çalıştığını ve variableDOM düğümlerini / öğelerini içerdiğinde bir istisna atacağını unutmayın .

Kullandığınızda JSON.decycle(variable, true), sonucun geri alınamayacağı gerçeğini kabul edersiniz (retrocycle DOM düğümlerini yeniden oluşturmaz). DOM öğeleri bir dereceye kadar tanımlanabilir olmalıdır. Örneğin, bir divöğenin kimliği varsa, o zaman bir dize ile değiştirilir "div#id-of-the-element".


2
Onları kullandığımda hem kodu hem de seninki bana "RangeError: Maksimum çağrı yığını boyutu aşıldı" veriyor.
jcollum


Aradığım şey buydu. JSON.decycle(a, true)fonksiyonu devir için bir parametre olarak true ilettiğinizde ne olur.
Rudra

@Rudra true, çataldaki stringifyNodesseçeneği doğru yapar . Bu, örneğin: divid = "some-id" ile string: 'i döker div#some-id. Bazı problemlerden kaçınacaksınız, ancak tamamen geri dönüş yapamayacaksınız.
Nux

Npmjs.com/package/json-js npm paketi var , ancak bir süre güncellenmedi
Michael Freidgeim

23

@ İsaacs json-stringify-safe kontrol etmenizi tavsiye ederim - NPM'de kullanılır.

BTW- Node.js kullanmıyorsanız , kaynak kodunun ilgili kısmından 4-27 satırlarını kopyalayıp yapıştırabilirsiniz .

Yüklemek:

$ npm install json-stringify-safe --save

Kullanmak:

// Require the thing
var stringify = require('json-stringify-safe');

// Take some nasty circular object
var theBigNasty = {
  a: "foo",
  b: theBigNasty
};

// Then clean it up a little bit
var sanitized = JSON.parse(stringify(theBigNasty));

Bu şu sonuçları verir:

{
  a: 'foo',
  b: '[Circular]'
}

@Rob W'un belirttiği gibi vanilya JSON.stringify işlevinde olduğu gibi, ikinci bağımsız değişken olarak bir "replacer" işlevini ileterek dezenfeksiyon davranışını da özelleştirebileceğinizi unutmayın stringify(). Kendinizi Bunun nasıl basit bir örnek gerek buluyor, sadece insan tarafından okunabilir dizeleri içine hataları regexp'lere ve işlevleri coerces özel replacer yazdı buraya .


13

Bu soruna bir çözüm ararken gelecek Google'cı ne zaman için yok tüm dairesel referansların anahtarlarını biliyorum Döngüsel başvurular ekarte etmek JSON.stringify işlevi etrafında sarıcı kullanabilirsiniz. Https://gist.github.com/4653128 adresinde bir örnek komut dosyasına bakın .

Çözüm temel olarak bir dizideki önceden yazdırılmış nesnelere bir referans tutmak ve bir değer döndürmeden önce bir replacer işlevinde kontrol etmek için kaynar. Sadece dairesel referansları dışlamaktan daha daraltıcıdır, çünkü bir nesnenin iki kez yazdırılmasını da ekarte eder, yan etkilerden biri dairesel referanslardan kaçınmaktır.

Örnek paketleyici:

function stringifyOnce(obj, replacer, indent){
    var printedObjects = [];
    var printedObjectKeys = [];

    function printOnceReplacer(key, value){
        var printedObjIndex = false;
        printedObjects.forEach(function(obj, index){
            if(obj===value){
                printedObjIndex = index;
            }
        });

        if(printedObjIndex && typeof(value)=="object"){
            return "(see " + value.constructor.name.toLowerCase() + " with key " + printedObjectKeys[printedObjIndex] + ")";
        }else{
            var qualifiedKey = key || "(empty key)";
            printedObjects.push(value);
            printedObjectKeys.push(qualifiedKey);
            if(replacer){
                return replacer(key, value);
            }else{
                return value;
            }
        }
    }
    return JSON.stringify(obj, printOnceReplacer, indent);
}

3
Güzel kod. , Yazdığınız olsa bir aptal hata var if(printedObjIndex)yazman gereken süre if(printedObjIndex==false)nedeniyle indexde olabilir 0çevrilir hangi falseaçıkça aksini belirtmedikçe.
guy mograbi

1
Bunu mu demek istediniz ===? 0 == falseolduğu true, 0 === falseolduğunu false. ; ^) Ama printedObjIndexyanlış undefinedyapmaya başlamamayı tercih ederim , çünkü o zaman kontrol edebilirsiniz, böylece (iyi, Trindaz'ın) metaforları garip bir şekilde karıştırmıyorsunuz.
33'te ruffin

@ruffin güzel yakala. evet açıkçası, böyle aptalca hataları yakalamak için her zaman sert eşitlik ve jshint kullanın.
guy mograbi

4

Bir yenisiyle JSON.stringify yöntemini kullanın. Daha fazla bilgi için bu belgeleri okuyun. http://msdn.microsoft.com/en-us/library/cc836459%28v=vs.94%29.aspx

var obj = {
  a: "foo",
  b: obj
}

var replacement = {"b":undefined};

alert(JSON.stringify(obj,replacement));

Değiştirme dizisini döngüsel başvurularla doldurmanın bir yolunu bulun. Bir özelliğin 'nesne' (başvuru) türünde olup olmadığını ve döngüsel başvuruyu doğrulamak için tam eşitlik denetiminde (===) bulmak için typeof yöntemini kullanabilirsiniz.


4
Bu yalnızca IE'de çalışabilir (MSDN'in Microsoft'un belgeleri olduğu ve Microsoft'un IE oluşturduğu düşünüldüğünde). Firefox / Chrome'da jsfiddle.net/ppmaW dairesel referans hatası oluşturur. Bilginize: var obj = {foo:obj}yok değil döngüsel bir başvuru oluşturun. Bunun yerine, fooözniteliği önceki değerine obj( undefineddaha önce tanımlanmamışsa, nedeniyle bildirilmiştir var obj) karşılık gelen bir nesne oluşturur .
Rob W

4

Eğer

console.log(JSON.stringify(object));

ile sonuçlanır

TypeError: döngüsel nesne değeri

O zaman şöyle yazdırmak isteyebilirsiniz:

var output = '';
for (property in object) {
  output += property + ': ' + object[property]+'; ';
}
console.log(output);

21
Belki sadece bir seviye yazdırdığı için?
Alex Turpin

ÇOK BASİT bunu benim için krom kutunun dışında benim için çalıştı çünkü iptal etti. MÜKEMMEL
Sevgi ve barış - Joe Codeswell

4
var a={b:"b"};
a.a=a;
JSON.stringify(preventCircularJson(a));

aşağıdakileri değerlendirir:

"{"b":"b","a":"CIRCULAR_REFERENCE_REMOVED"}"

fonksiyonu ile:

/**
 * Traverses a javascript object, and deletes all circular values
 * @param source object to remove circular references from
 * @param censoredMessage optional: what to put instead of censored values
 * @param censorTheseItems should be kept null, used in recursion
 * @returns {undefined}
 */
function preventCircularJson(source, censoredMessage, censorTheseItems) {
    //init recursive value if this is the first call
    censorTheseItems = censorTheseItems || [source];
    //default if none is specified
    censoredMessage = censoredMessage || "CIRCULAR_REFERENCE_REMOVED";
    //values that have allready apeared will be placed here:
    var recursiveItems = {};
    //initaite a censored clone to return back
    var ret = {};
    //traverse the object:
    for (var key in source) {
        var value = source[key]
        if (typeof value == "object") {
            //re-examine all complex children again later:
            recursiveItems[key] = value;
        } else {
            //simple values copied as is
            ret[key] = value;
        }
    }
    //create list of values to censor:
    var censorChildItems = [];
    for (var key in recursiveItems) {
        var value = source[key];
        //all complex child objects should not apear again in children:
        censorChildItems.push(value);
    }
    //censor all circular values
    for (var key in recursiveItems) {
        var value = source[key];
        var censored = false;
        censorTheseItems.forEach(function (item) {
            if (item === value) {
                censored = true;
            }
        });
        if (censored) {
            //change circular values to this
            value = censoredMessage;
        } else {
            //recursion:
            value = preventCircularJson(value, censoredMessage, censorChildItems.concat(censorTheseItems));
        }
        ret[key] = value

    }

    return ret;
}

3

Bunun eski bir soru olduğunu biliyorum, ancak akıllı döngü adı verilen ve önerilen diğer yollardan farklı çalışan bir NPM paketi önermek istiyorum . Büyük ve derin nesneler kullanıyorsanız özellikle kullanışlıdır .

Bazı özellikler:

  • Nesnenin içindeki dairesel referansların veya basitçe tekrarlanan yapıların, ilk oluşumuna giden yolla değiştirilmesi (sadece [dairesel] değil );

  • Genişlik ilk aramada döngüleri arayarak, paket bu yolun mümkün olduğunca küçük olmasını sağlar, bu da yolların can sıkıcı bir şekilde uzun ve takip edilmesinin zor olabileceği çok büyük ve derin nesnelerle uğraşırken önemlidir. JSON.stringify bir DFS yapar);

  • Nesnenin daha az önemli kısımlarını basitleştirmek veya yoksaymak için kullanışlı olan kişiselleştirilmiş değiştirmelere izin verir;

  • Son olarak, yollar tam olarak başvurulan alana erişmek için gerekli şekilde yazılır ve hata ayıklamanıza yardımcı olabilir.


3

JSON.stringify () işlevinin ikinci bağımsız değişkeni , verilerinizde karşılaştığı her nesneden korunması gereken bir dizi anahtar ad belirtmenize de olanak tanır. Bu, tüm kullanım durumlarında işe yaramayabilir, ancak çok daha basit bir çözümdür.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

var obj = {
    a: "foo",
    b: this
}

var json = JSON.stringify(obj, ['a']);
console.log(json);
// {"a":"foo"}

Not: OP'nin nesne tanımı garip bir şekilde en son Chrome veya Firefox'ta dairesel bir referans hatası atmıyor. O yüzden bu cevap tanım değiştirildi yaptığı bir hata atmak.



Bu cevap kabul edilmelidir
Manic Depresyon

2

JSON'un çalışma şeklini geçersiz kılma yanıtını güncellemek için (muhtemelen önerilmez, ancak çok basit), kullanmayın circular-json(kullanımdan kaldırılmıştır). Bunun yerine halefi kullanın, düzleştirin:

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

@ User1541685 adresinden yukarıdaki eski yanıttan ödünç alındı, ancak yenisiyle değiştirildi:

npm i --save flatted

o zaman js dosyanızda

const CircularJSON = require('flatted');
const json = CircularJSON.stringify(obj);

1

Github'da dairesel-json kütüphanesi buldum ve sorunum için iyi çalıştı.

Yararlı bulduğum bazı iyi özellikler:

  • Çoklu platform kullanımını destekler ancak şimdiye kadar sadece node.js ile test ettim.
  • API aynıdır, bu yüzden yapmanız gereken tek şey bir JSON değiştirme olarak kullanmak ve kullanmaktır.
  • 'Dairesel' serileştirilmiş verileri nesneye geri dönüştürebilmeniz için kendi ayrıştırma yöntemi vardır.

2
Bu kütüphane benim için bir hata attı, bu yüzden başka bir tane aramam gerekiyor. HATA TypeError: toISOString Nesne at String.toJSON (<anonim>) bir işlev değil <anonim>. ( Localhost: 8100 / build / polyfills.js: 1: 3458 ) JSON.stringify (<anonim>) de Nesne at. stringifyRecursion [ stringize olarak] ( localhost: 8100 / build / main.js: 258450: 15 )
Mark Ellul

1
@MarkEllul 2015 yılında yorumu yazdım ve daha iyi bir alternatif görürsem, burada bir düzenleme ile gönderirim. Aynı sorunu günlük işlerde hala bitiriyorum ve genellikle kendi manuel işlevlerimi düzgün / güvenli bir inceleme ile tekrarlı bir şekilde tercih ediyorum. Eğer bilmiyorsanız, fonksiyonel programlama uygulamalarını kontrol etmenizi öneririm, genellikle, bu tür yinelemeli işlemleri daha az zor ve daha güvenilir olarak kolaylaştırır.
JacopKane

Ayrıca bir olayı dizginlemeye ve bir selvi testinde tekrar göndermeye çalışan "TOISOString bir işlev değildir"
Devin G Rhode

1

Bu sorunu şu şekilde çözdüm:

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

Bu benim için neredeyse işe yaradı ama sınıflar gibi temsil ediliyor gibi görünüyor _class: ClassName { data: "here" }, bu yüzden aşağıdaki kuralı ekledim .replace(/(\w+) {/g, '{ __ClassName__: "$1", '). Benim durumumda bir http istek nesnesinin neye benzediğini görmeye çalışıyordum.
redbmk

1

Bu sorunun eski olduğunu ve çok sayıda harika cevabı olduğunu biliyorum, ancak yeni bir lezzet nedeniyle bu cevabı gönderiyorum (es5 +)


1

Bu yeterince yanıtlanmış olmasına rağmen, deleteişleç kullanarak dizilemeden önce söz konusu özelliği de silebilirsiniz .

delete obj.b; 
const jsonObject = JSON.stringify(obj);

operatörü sil

bu dairesel referansları kaldırmak için karmaşık mantık oluşturma veya sürdürme ihtiyacını ortadan kaldıracaktır.


1
function myStringify(obj, maxDeepLevel = 2) {
  if (obj === null) {
    return 'null';
  }
  if (obj === undefined) {
    return 'undefined';
  }
  if (maxDeepLevel < 0 || typeof obj !== 'object') {
    return obj.toString();
  }
  return Object
    .entries(obj)
    .map(x => x[0] + ': ' + myStringify(x[1], maxDeepLevel - 1))
    .join('\r\n');
}


0

Diğer cevaplara dayanarak aşağıdaki kodla karşılaşıyorum. Dairesel referanslarla, özel yapıcılara sahip nesnelerle oldukça iyi çalışır.

Serileştirilecek belirli nesneden,

  • Nesneyi gezerken karşılaştığınız tüm nesneyi önbelleğe alın ve her birine benzersiz bir hashID atayın (otomatik artan bir sayı da çalışır)
  • Dairesel bir başvuru bulunduğunda, yeni nesne içindeki alanı dairesel olarak işaretleyin ve orijinal nesnenin hashID değerini bir nitelik olarak saklayın.

Github Bağlantısı - DecycledJSON

DJSHelper = {};
DJSHelper.Cache = [];
DJSHelper.currentHashID = 0;
DJSHelper.ReviveCache = [];

// DOES NOT SERIALIZE FUNCTION
function DJSNode(name, object, isRoot){
    this.name = name;
    // [ATTRIBUTES] contains the primitive fields of the Node
    this.attributes = {};

    // [CHILDREN] contains the Object/Typed fields of the Node
    // All [CHILDREN] must be of type [DJSNode]
    this.children = []; //Array of DJSNodes only

    // If [IS-ROOT] is true reset the Cache and currentHashId
    // before encoding
    isRoot = typeof isRoot === 'undefined'? true:isRoot;
    this.isRoot = isRoot;
    if(isRoot){
        DJSHelper.Cache = [];
        DJSHelper.currentHashID = 0;

        // CACHE THE ROOT
        object.hashID = DJSHelper.currentHashID++;
        DJSHelper.Cache.push(object);
    }

    for(var a in object){
        if(object.hasOwnProperty(a)){
            var val = object[a];

            if (typeof val === 'object') {
                // IF OBJECT OR NULL REF.

                /***************************************************************************/
                // DO NOT REMOVE THE [FALSE] AS THAT WOULD RESET THE [DJSHELPER.CACHE]
                // AND THE RESULT WOULD BE STACK OVERFLOW
                /***************************************************************************/
                if(val !== null) {
                    if (DJSHelper.Cache.indexOf(val) === -1) {
                        // VAL NOT IN CACHE
                        // ADD THE VAL TO CACHE FIRST -> BEFORE DOING RECURSION
                        val.hashID = DJSHelper.currentHashID++;
                        //console.log("Assigned", val.hashID, "to", a);
                        DJSHelper.Cache.push(val);

                        if (!(val instanceof Array)) {
                            // VAL NOT AN [ARRAY]
                            try {
                                this.children.push(new DJSNode(a, val, false));
                            } catch (err) {
                                console.log(err.message, a);
                                throw err;
                            }
                        } else {
                            // VAL IS AN [ARRAY]
                            var node = new DJSNode(a, {
                                array: true,
                                hashID: val.hashID // HashID of array
                            }, false);
                            val.forEach(function (elem, index) {
                                node.children.push(new DJSNode("elem", {val: elem}, false));
                            });
                            this.children.push(node);
                        }
                    } else {
                        // VAL IN CACHE
                        // ADD A CYCLIC NODE WITH HASH-ID
                        this.children.push(new DJSNode(a, {
                            cyclic: true,
                            hashID: val.hashID
                        }, false));
                    }
                }else{
                    // PUT NULL AS AN ATTRIBUTE
                    this.attributes[a] = 'null';
                }
            } else if (typeof val !== 'function') {
                // MUST BE A PRIMITIVE
                // ADD IT AS AN ATTRIBUTE
                this.attributes[a] = val;
            }
        }
    }

    if(isRoot){
        DJSHelper.Cache = null;
    }
    this.constructorName = object.constructor.name;
}
DJSNode.Revive = function (xmlNode, isRoot) {
    // Default value of [isRoot] is True
    isRoot = typeof isRoot === 'undefined'?true: isRoot;
    var root;
    if(isRoot){
        DJSHelper.ReviveCache = []; //Garbage Collect
    }
    if(window[xmlNode.constructorName].toString().indexOf('[native code]') > -1 ) {
        // yep, native in the browser
        if(xmlNode.constructorName == 'Object'){
            root = {};
        }else{
            return null;
        }
    }else {
        eval('root = new ' + xmlNode.constructorName + "()");
    }

    //CACHE ROOT INTO REVIVE-CACHE
    DJSHelper.ReviveCache[xmlNode.attributes.hashID] = root;

    for(var k in xmlNode.attributes){
        // PRIMITIVE OR NULL REF FIELDS
        if(xmlNode.attributes.hasOwnProperty(k)) {
            var a = xmlNode.attributes[k];
            if(a == 'null'){
                root[k] = null;
            }else {
                root[k] = a;
            }
        }
    }

    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.array){
            // ITS AN [ARRAY]
            root[value.name] = [];
            value.children.forEach(function (elem) {
                root[value.name].push(elem.attributes.val);
            });
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }else if(!value.attributes.cyclic){
            // ITS AN [OBJECT]
            root[value.name] = DJSNode.Revive(value, false);
            //console.log("Caching", value.attributes.hashID);
            DJSHelper.ReviveCache[value.attributes.hashID] = root[value.name];
        }
    });

    // [SEPARATE ITERATION] TO MAKE SURE ALL POSSIBLE
    // [CYCLIC] REFERENCES ARE CACHED PROPERLY
    xmlNode.children.forEach(function (value) {
        // Each children is an [DJSNode]
        // [Array]s are stored as [DJSNode] with an positive Array attribute
        // So is value

        if(value.attributes.cyclic){
            // ITS AND [CYCLIC] REFERENCE
            root[value.name] = DJSHelper.ReviveCache[value.attributes.hashID];
        }
    });

    if(isRoot){
        DJSHelper.ReviveCache = null; //Garbage Collect
    }
    return root;
};

DecycledJSON = {};
DecycledJSON.stringify = function (obj) {
    return JSON.stringify(new DJSNode("root", obj));
};
DecycledJSON.parse = function (json, replacerObject) {
    // use the replacerObject to get the null values
    return DJSNode.Revive(JSON.parse(json));
};
DJS = DecycledJSON;

Örnek Kullanım 1:

var obj = {
    id:201,
    box: {
        owner: null,
        key: 'storm'
    },
    lines:[
        'item1',
        23
    ]
};

console.log(obj); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonObj = DJS.stringify(obj);
console.log(DJS.parse(jsonObj));

Örnek Kullanım 2:

// PERSON OBJECT

function Person() {
    this.name = null;
    this.child = null;
    this.dad = null;
    this.mom = null;
}
var Dad = new Person();
Dad.name = 'John';
var Mom = new Person();
Mom.name = 'Sarah';
var Child = new Person();
Child.name = 'Kiddo';

Dad.child = Mom.child = Child;
Child.dad = Dad;
Child.mom = Mom;

console.log(Child); // ORIGINAL

// SERIALIZE AND THEN PARSE
var jsonChild = DJS.stringify(Child);
console.log(DJS.parse(jsonChild));

0

Bunu dene:

var obj = {
    a: "foo",
    b: obj
};

var circular_replacer = (value) => {
    var seen = [];
    if (value != null && typeof value == "object") {
        if (seen.indexOf(value) >= 0) return;
        seen.push(value);
    }
    return value;
};

obj = circular_replacer(obj);

seen.push(value)= -D'den sonra birkaç satır daha kod olmamalı mı ? Beğenfor (var key in value) {value[key] = circular_replacer(value[key]);}
Klesun

Yalnızca kod yanıtları önerilmez. Lütfen düzenle'yi tıklayın ve kodunuzun soruyu nasıl ele aldığını özetleyen bazı kelimeler ekleyin veya belki de cevabınızın önceki cevap / cevaplardan nasıl farklı olduğunu açıklayın. Şu kaynaktan
Nick

0

Benim çözümümde, bir döngüye girerseniz, sadece "döngü" (ya da hiçbir şey) demez, foo gibi bir şey söyler: yukarıdaki # 42 nesnesine bakın ve hangi foo noktalarının nereye gidip arama yapabileceğini görmek için nesne # 42 için (her nesne başladığında, bazı tamsayı xxx ile # xxx nesnesi der)

Pasaj:

(function(){
	"use strict";
	var ignore = [Boolean, Date, Number, RegExp, String];
	function primitive(item){
		if (typeof item === 'object'){
			if (item === null) { return true; }
			for (var i=0; i<ignore.length; i++){
				if (item instanceof ignore[i]) { return true; }
			}
			return false;
		} else {
			return true;
		}
	}
	function infant(value){
		return Array.isArray(value) ? [] : {};
	}
	JSON.decycleIntoForest = function decycleIntoForest(object, replacer) {
		if (typeof replacer !== 'function'){
			replacer = function(x){ return x; }
		}
		object = replacer(object);
		if (primitive(object)) return object;
		var objects = [object];
		var forest  = [infant(object)];
		var bucket  = new WeakMap(); // bucket = inverse of objects 
		bucket.set(object, 0);       // i.e., map object to index in array
		function addToBucket(obj){
			var result = objects.length;
			objects.push(obj);
			bucket.set(obj, result);
			return result;
		}
		function isInBucket(obj){
			return bucket.has(obj);
			// objects[bucket.get(obj)] === obj, iff true is returned
		}
		function processNode(source, target){
			Object.keys(source).forEach(function(key){
				var value = replacer(source[key]);
				if (primitive(value)){
					target[key] = {value: value};
				} else {
					var ptr;
					if (isInBucket(value)){
						ptr = bucket.get(value);
					} else {
						ptr = addToBucket(value);
						var newTree = infant(value);
						forest.push(newTree);
						processNode(value, newTree);
					}
					target[key] = {pointer: ptr};
				}
			});
		}
		processNode(object, forest[0]);
		return forest;
	};
})();
the = document.getElementById('the');
function consoleLog(toBeLogged){
  the.textContent = the.textContent + '\n' + toBeLogged;
}
function show(root){
	var cycleFree = JSON.decycleIntoForest(root);
	var shown = cycleFree.map(function(tree, idx){ return false; });
	var indentIncrement = 4;
	function showItem(nodeSlot, indent, label){
	  leadingSpaces = ' '.repeat(indent);
      leadingSpacesPlus = ' '.repeat(indent + indentIncrement);
	  if (shown[nodeSlot]){
	  consoleLog(leadingSpaces + label + ' ... see above (object #' + nodeSlot + ')');
        } else {
		  consoleLog(leadingSpaces + label + ' object#' + nodeSlot);
		  var tree = cycleFree[nodeSlot];
		  shown[nodeSlot] = true;
		  Object.keys(tree).forEach(function(key){
			var entry = tree[key];
			if ('value' in entry){
			  consoleLog(leadingSpacesPlus + key + ": " + entry.value);
                } else {
					if ('pointer' in entry){
						showItem(entry.pointer, indent+indentIncrement, key);
                    }
                }
			});
        }
    }
	showItem(0, 0, 'root');
}
cities4d = {
	Europe:{
		north:[
			{name:"Stockholm", population:1000000, temp:6},
			{name:"Helsinki", population:650000, temp:7.6}
		],
		south:[
			{name:"Madrid", population:3200000, temp:15},
			{name:"Rome", population:4300000, temp:15}
		]
	},
	America:{
		north:[
			{name:"San Francisco", population:900000, temp:14},
			{name:"Quebec", population:530000, temp:4}
		],
		south:[
			{name:"Rio de Janeiro", population:7500000, temp:24},
			{name:"Santiago", population:6300000, temp:14}
		]
	},
	Asia:{
		north:[
			{name:"Moscow", population:13200000, temp:6}
		]
	}
};
cities4d.Europe.north[0].alsoStartsWithS = cities4d.America.north[0];
cities4d.Europe.north[0].smaller = cities4d.Europe.north[1];
cities4d.Europe.south[1].sameLanguage = cities4d.America.south[1];
cities4d.Asia.ptrToRoot = cities4d;
show(cities4d)
<pre id="the"></pre>

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.