Objet de sérialisation qui contient une valeur d'objet cyclique
-
28-10-2019 - |
Question
J'ai un objet (arbre d'analyse) qui contient des nœuds enfants qui sont des références à d'autres nœuds.
Je voudrais sérialiser cet objet, en utilisant JSON.stringify()
, mais je comprends
TypeError: valeur d'objet cyclique
À cause des constructions que j'ai mentionnées.
Comment pourrais-je contourner cela? Peu importe pour moi si ces références à d'autres nœuds sont représentées ou non dans l'objet sérialisé.
D'un autre côté, retirer ces propriétés de l'objet lorsqu'ils sont créés semble fastidieux et je ne voudrais pas apporter des modifications à l'analyseur (narcisse).
La solution
Utiliser le deuxième paramètre de stringify
, la fonction de remplacement, pour exclure les objets déjà sérialisés:
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;
});
Comme le souligne correctement dans d'autres commentaires, ce code supprime tous les objets "vus", pas seulement ceux "récursifs".
Par exemple, pour:
a = {x:1};
obj = [a, a];
Le résultat sera incorrect. Si votre structure est comme ça, vous voudrez peut-être utiliser Crockford décycler ou cette fonction (plus simple) qui remplace simplement les références récursives par Nulls:
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)))
Autres conseils
J'ai créé un gist github qui est capable de détecter les structures cycliques et de les décoder: https://gist.github.com/hoff97/9842228
Pour transformer simplement, utilisez JSONE.Stringify / jsone.parse. Il dé- et code également les fonctions. Si vous souhaitez désactiver cela, supprimez simplement les lignes 32-48 et 61-85.
var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);
Vous pouvez trouver un exemple de violon ici:
beaucoup d'économie et cela montre où un cycle L'objet était.
<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>
produit
jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}
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
}
);
}
Une condition préalable était manquante, sinon les valeurs entières dans les objets du tableau sont tronquées, c'est-à-dire [[08.11.2014 12:30:13, 1095]] 1095 est réduit à 095.
Je crée aussi un projet GitHub qui peut sérialiser l'objet cyclique et restaurer la classe si vous l'enregistrez dans l'attribut Serializename comme une chaîne
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/serializestrifyparsecyclicobject
Edit: j'ai transformé mon script pour NPM https://github.com/bormat/borto_circular_serialize Et j'ai des noms de fonction de changement du français à l'anglais.