JSON に変換して送信したい大きなオブジェクトがあります。しかし、それは円形の構造をしています。循環参照が存在するものは何でも投げて、文字列化できるものは何でも送信したいと思います。それ、どうやったら出来るの?
ありがとう。
var obj = {
a: "foo",
b: obj
}
obj を次のように文字列化したい:
{"a":"foo"}
JSON に変換して送信したい大きなオブジェクトがあります。しかし、それは円形の構造をしています。循環参照が存在するものは何でも投げて、文字列化できるものは何でも送信したいと思います。それ、どうやったら出来るの?
ありがとう。
var obj = {
a: "foo",
b: obj
}
obj を次のように文字列化したい:
{"a":"foo"}
Node.js では、util.inspect(object)を使用できます。循環リンクを自動的に「[Circular]」に置き換えます。
組み込み(インストールは不要)ですが、インポートする必要があります
import * as util from 'util' // has no default export
import { inspect } from 'util' // or directly
// or
var util = require('util')
それを使用するには、単に呼び出します
console.log(util.inspect(myObject))
また、検査するオプション オブジェクトを渡すことができることに注意してください(上記のリンクを参照) 。
inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])
以下のコメンターを読んで称賛してください...
JSON.stringify
カスタムリプレースと一緒に使用してください。例えば:
// 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
この例の置換は 100% 正しいわけではありません (「重複」の定義によって異なります)。次の場合、値は破棄されます。
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, ...);
しかし、コンセプトはそのままです。カスタムのリプレースを使用し、解析されたオブジェクトの値を追跡します。
es6 で書かれたユーティリティ関数として:
// 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))
JSON.decycle
Douglas Crockford によって実装されたメソッドもあることに注意してください。彼の
cycle.jsを参照してください。これにより、ほぼすべての標準構造を文字列化できます。
var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.
メソッドで元のオブジェクトを再作成することもできますretrocycle
。そのため、オブジェクトを文字列化するためにオブジェクトからサイクルを削除する必要はありません。
ただし、これはDOM ノードでは機能しません(これは、実際の使用例におけるサイクルの典型的な原因です)。たとえば、これはスローします:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));
その問題を解決するためにフォークを作成しました (私のcycle.js forkを参照してください)。これはうまくいくはずです:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));
私のフォークではオリジナルと同じように機能し、 DOM ノード/要素が含まれJSON.decycle(variable)
ていると例外がスローされることに注意してください。variable
使用するときはJSON.decycle(variable, true)
、結果が元に戻せないという事実を受け入れます (レトロサイクルは DOM ノードを再作成しません)。ただし、DOM 要素はある程度識別できるはずです。たとえば、div
要素に id がある場合、それは string に置き換えられます"div#id-of-the-element"
。
@isaacsの json-stringify-safeをチェックすることをお勧めします。これは NPM で使用されています。
ところで、Node.js を使用していない場合は、ソース コードの関連部分から 4 ~ 27 行目をコピーして貼り付けることができます。
インストールするには:
$ npm install json-stringify-safe --save
使用するには:
// 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));
これにより、次の結果が得られます。
{
a: 'foo',
b: '[Circular]'
}
@Rob W が述べたバニラの JSON.stringify 関数と同様に、「replacer」関数を の 2 番目の引数として渡すことで、サニタイズ動作をカスタマイズすることもできます
stringify()
。これを行う方法の簡単な例が必要な場合は、エラー、正規表現、関数を人間が読める文字列に変換するカスタム リプレースをここに書きました。
すべての循環参照のキーがわからないときにこの問題の解決策を探している将来のグーグル社員のために、JSON.stringify関数のラッパーを使用して循環参照を除外できます。https://gist.github.com/4653128でサンプル スクリプトを参照してください。
解決策は基本的に、以前に印刷されたオブジェクトへの参照を配列に保持し、値を返す前に置換関数でそれをチェックすることです。オブジェクトを 2 回印刷することも除外するため、循環参照のみを除外するよりも制限的です。その副作用の 1 つは、循環参照を回避することです。
ラッパーの例:
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);
}
代替でJSON.stringifyメソッドを使用します。詳細については、このドキュメントをお読みください。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));
置換配列に循環参照を設定する方法を見つけます。typeofメソッドを使用して、プロパティがタイプ'object'(reference)であるかどうかを確認し、正確な等価性チェック(===)を使用して循環参照を検証できます。
私はgithubでcircular-jsonライブラリを見つけましたが、それは私の問題に対してうまく機能しました。
私が便利だと思ったいくつかの優れた機能:
この問題を次のように解決します。
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));
他の回答に基づいて、次のコードになります。循環参照、カスタム コンストラクターを持つオブジェクトでうまく機能します。
シリアル化する特定のオブジェクトから、
Github リンク- 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;
使用例 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));
使用例 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));