Döngüsel nesne değeri içeren nesneyi seri hale getirme


151

Diğer düğümlere başvurular olan alt düğümleri içeren bir nesne (ayrıştırma ağacı) var.

Kullanarak bu nesneyi serileştirmek istiyorum JSON.stringify(), ancak

TypeError: döngüsel nesne değeri

bahsettiğim yapılar yüzünden.

Bu sorunu nasıl çözebilirim? Diğer düğümlere yapılan bu referansların serileştirilmiş nesnede temsil edilip edilmemesi benim için önemli değil.

Öte yandan, bu özellikleri oluşturulduklarında nesneden kaldırmak sıkıcı görünmektedir ve ayrıştırıcıda (nergis) değişiklik yapmak istemem.


1
Kod olmadan size yardımcı olamayız. Lütfen nesnenin ve / veya JSON çıktısının ilgili bitlerini, serileştirmek için kullandığınız JS ile birlikte gönderin.
Bojangles

1
dahili referans olan bu özelliklere önek ekleyebiliyor musunuz?
wheresrhys

@Loic Burada cycle.jsbir cevap olarak Douglas Crockford'un olması değerli olacaktır , çünkü birçok vaka için en uygun çözümdür. Bu cevabı göndermeniz sizin için uygun görünüyor çünkü ona ilk referansta sizsiniz (aşağıdaki yorumunuzda). Kendinize bir cevap olarak göndermek istemiyorsanız, sonunda yapacağım.
Jeremy Banks


1
Keşke JSON daha akıllı veya bunu çözmenin daha kolay bir yolu olsaydı. Çözümler basit (!) Hata ayıklama amacıyla imo için çok zahmetlidir.
BluE

Yanıtlar:


220

Zaten serileştirilmiş nesneleri hariç tutmak stringifyiçin replacer işlevinin ikinci parametresini kullanın :

var seen = [];

JSON.stringify(obj, function(key, val) {
   if (val != null && typeof val == "object") {
        if (seen.indexOf(val) >= 0) {
            return;
        }
        seen.push(val);
    }
    return val;
});

http://jsfiddle.net/mH6cJ/38/

Diğer yorumlarda doğru bir şekilde işaret edildiği gibi, bu kod sadece "özyinelemeli" olanları değil, tüm "görülen" nesneleri kaldırır.

Örneğin:

a = {x:1};
obj = [a, a];

sonuç yanlış olur. Yapınız böyle ise, tekrarlayan referansları null'larla değiştiren Crockford'un döngüsünü veya bu (daha basit) işlevini kullanmak isteyebilirsiniz :

function decycle(obj, stack = []) {
    if (!obj || typeof obj !== 'object')
        return obj;
    
    if (stack.includes(obj))
        return null;

    let s = stack.concat([obj]);

    return Array.isArray(obj)
        ? obj.map(x => decycle(x, s))
        : Object.fromEntries(
            Object.entries(obj)
                .map(([k, v]) => [k, decycle(v, s)]));
}

//

let a = {b: [1, 2, 3]}
a.b.push(a);

console.log(JSON.stringify(decycle(a)))


3
aaah güzel! Teşekkürler, bunu deneyeceğim. Douglas Crockford ( github.com/douglascrockford/JSON-js/blob/master/cycle.js ) tarafından oluşturulan bir çözüm buldum , ancak onunla birlikte gelen lisanstan emin olmadığım için tarif ettiğiniz kolay çözüm mükemmel olurdu!
Loic Duros

3
@LoicDuros Lisans "kamu malı" dır. Yani, onunla istediğin her şeyi yapabilirsin.
Ateş Goral

1
Bu kod, bisiklet döngüleri üretir, kullanmaya dikkat edin, uygulamanızın çok potansiyel çökmesine neden olur. doğru noktalı virgüllere ihtiyaç duyar ve etkinlik nesnelerinde kullanılamaz!
Ol Sen

3
Bu, sadece döngüsel referanslardan daha fazlasını kaldırır - sadece bir kereden fazla görünen her şeyi kaldırır. Halihazırda serileştirilmiş nesne yeni nesnenin "üst öğesi" değilse, onu
Gio

1
İyi cevap! Bunu biraz değiştirdim, işlevi özyinelemeli bir işleve dönüştürdüm, böylece alt nesneler üst nesnelerin klonlandığı şekilde klonlanır.
HoldOffHunger

2

Döngüsel yapıları algılayabilen ve bunları çözüp kodlayan bir GitHub Gist oluşturdum: https://gist.github.com/Hoff97/9842228

Dönüştürmek için JSONE.stringify / JSONE.parse komutunu kullanın. Ayrıca işlevleri de- ve kodlar. Bunu devre dışı bırakmak istiyorsanız, 32-48 ve 61-85 satırlarını kaldırın.

var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);

Burada bir keman örneği bulabilirsiniz:

http://jsfiddle.net/hoff97/7UYd4/


2

Bu bir tür alternatif cevaptır, ancak buraya birçok insanın geleceği şey dairesel nesnelerinin hatalarını ayıklamaktır ve bunu bir grup kod çekmeden yapmanın gerçekten harika bir yolu yoktur, işte gidiyor.

Bilindiği kadar iyi bilinmeyen bir JSON.stringify()özellik console.table(). Basitçe arayın console.table(whatever);ve değişkeni konsolda tablo biçiminde kaydederek değişkenin içeriğini incelemek oldukça kolay ve kullanışlı hale gelir.


1

ve bir döngü nesnesinin nerede olduğunu gösterir .

<script>
var jsonify=function(o){
    var seen=[];
    var jso=JSON.stringify(o, function(k,v){
        if (typeof v =='object') {
            if ( !seen.indexOf(v) ) { return '__cycle__'; }
            seen.push(v);
        } return v;
    });
    return jso;
};
var obj={
    g:{
        d:[2,5],
        j:2
    },
    e:10
};
obj.someloopshere = [
    obj.g,
    obj,
    { a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>

üretir

jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

ancak obj.b=this'birisi yanlış bir kapsamla yapılmış çok uzun kireçlerin nasıl önleneceğini bilirse, biriyle nesne oluşturacaksa bu kodla ilgili bir sorun thisvar
Ol Sen

2
Bu olmalıseen.indexOf(v) != -1

1

Ben bir String gibi serializename özniteliğinde kaydederseniz döngüsel nesneyi serileştirmek ve sınıf geri yükleyebilirsiniz çok github proje oluşturmak

var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal(  b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal(  retCaseDep.b, 25 );
assert.equal(  retCaseDep.enfant.papa, retCaseDep );

https://github.com/bormat/serializeStringifyParseCyclicObject

Düzenleme: Komutumu NPM https://github.com/bormat/borto_circular_serialize için dönüştürdüm ve Fransızcadan İngilizceye işlev adları değiştirme var.


Bu örnek Gist'e uymuyor. Gist'in hataları var.
Ernst Ernst

Güzel bir fikir - ama bir kez hazır olun :-) Eğer npm'de dağıtırsanız, belki bunun için bile yazımlar geliştirirsiniz, muhtemelen oldukça popüler hale geldi.
peterh - Monica Şubesi

1

Döngüsel referansları olan bir veri yapısı örneği: toolshedCY

function makeToolshed(){
    var nut = {name: 'nut'}, bolt = {name: 'bolt'};
    nut.needs = bolt; bolt.needs = nut;
    return { nut: nut, bolt: bolt };
}

Döngüsel referansları TUTMAK istediğinizde ("nuking" yerine serileştirdiğinizde geri yükleyin), burada karşılaşacağım 2 seçeneğiniz var. Birincisi Douglas Crockford'un cycle.js , ikincisi sibirya paketim. Her ikisi de önce nesneyi "çözerek", yani aynı bilgiyi içeren başka bir nesne (döngüsel referanslar olmadan) oluşturarak çalışır.

Bay Crockford önce gidiyor:

JSON.decycle(makeToolshed())

JSON_decycleMakeToolshed

Gördüğünüz gibi, JSON'un iç içe yapısı korunur, ancak özel bir $refözelliğe sahip nesneler olan yeni bir şey vardır . Bunun nasıl çalıştığını görelim.

root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]

Dolar işareti kök anlamına gelir. .boltsahip $refolduğunu söyler .boltbir "zaten görmüş" nesnedir ve ilk gördüğünüz yerde bu özel mülkiyet değeri (burada, dize $ [ "somun"] [ "ihtiyaçlar"]) söyler ===yukarıda. Aynı şekilde ikinci $refve ikinci için de ===.

Klonlamanın işe yarayıp yaramadığını görmek için uygun bir derin eşitlik testi (yani Anders Kaseorg'un bu soruyadeepGraphEqual kabul edilen cevaptan işlevi) kullanalım .

root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true

Şimdi, sibirya:

JSON.Siberia.forestify(makeToolshed())

JSON_Siberia_forestify_makeToolshed

Sibirya "klasik" JSON, iç içe bir yapı taklit etmeye çalışmaz. Nesne grafiği "düz" bir şekilde tarif edilir. Nesne grafiğinin her düğümü düz bir ağaca dönüştürülür (yalnızca tamsayı değerlere sahip düz anahtar değer çifti listesi). .forest.Bu, dizin sıfırında kök nesneyi buluruz, daha yüksek endekslerde, nesne grafiği ve negatif değerler (ormanın bir ağacının bazı anahtarlarının) atomsdiziyi işaret eder (türler dizisi aracılığıyla yazılır, ancak buraya yazma ayrıntılarını atlayacağız). Tüm terminal düğümleri atom tablosunda, terminal olmayan tüm düğümler orman tablosundadır ve nesne grafiğinin kaç düğümü olduğunu hemen görebilirsiniz forest.length. Çalışıp çalışmadığını test edelim:

root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true

karşılaştırma

bölümü daha sonra ekleyecektir.


0
function stringifyObject ( obj ) {
  if ( _.isArray( obj ) || !_.isObject( obj ) ) {
    return obj.toString()
  }
  var seen = [];
  return JSON.stringify(
    obj,
    function( key, val ) {
      if (val != null && typeof val == "object") {
        if ( seen.indexOf( val ) >= 0 )
          return
          seen.push( val )
          }
      return val
    }
  );
}

Bir önkoşul eksikti, aksi takdirde dizi nesnelerindeki tamsayı değerleri kesilir, yani [[08.11.2014 12:30:13, 1095]] 1095, 095'e düşürülür.


RefrenceError: Değişken bulunamıyor: _
amit pandya

Lütfen kodunuzu düzeltin.
Anastasios Moraitis
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.