Javascript 如何以类似 JSON 的格式打印圆形结构?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11616630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I print a circular structure in a JSON-like format?
提问by Harry
I have a big object I want to convert to JSON and send. However it has circular structure. I want to toss whatever circular references exist and send whatever can be stringified. How do I do that?
我有一个大对象要转换为 JSON 并发送。但是它具有圆形结构。我想抛弃任何存在的循环引用并发送任何可以字符串化的内容。我怎么做?
Thanks.
谢谢。
var obj = {
a: "foo",
b: obj
}
I want to stringify obj into:
我想将 obj 字符串化为:
{"a":"foo"}
采纳答案by Rob W
Use JSON.stringify
with a custom replacer. For example:
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
The replacer in this example is not 100% correct (depending on your definition of "duplicate"). In the following case, a value is discarded:
此示例中的替换器并非 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, ...);
But the concept stands: Use a custom replacer, and keep track of the parsed object values.
但概念仍然存在:使用自定义替换器,并跟踪解析的对象值。
As a utility function written in es6:
作为用 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))
回答by Erel Segal-Halevi
In Node.js, you can use util.inspect(object). It automatically replaces circular links with "[Circular]".
在 Node.js 中,您可以使用util.inspect(object)。它会自动用“[Circular]”替换圆形链接。
Albeit being built-in (no installation is required), you must import it
尽管是内置的(无需安装),但您必须导入它
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))
Also be aware that you can pass options object to inspect (see link above)
另请注意,您可以传递选项对象进行检查(请参阅上面的链接)
inspect(myObject[, options: {showHidden, depth, colors, showProxy, ...moreOptions}])
Please, read and give kudos to commenters below...
请阅读下面的评论者并给他们点赞...
回答by Klesun
I wonder why nobody posted the proper solution from MDN pageyet...
我想知道为什么没有人从 MDN 页面发布正确的解决方案......
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());
Seen values should be stored in a set, not in array (replacer gets called on every element) and there is no need to try JSON.stringify
each elementin the chain leading to a circular reference.
看到的值应该存储在集合中,而不是数组中(替换器在每个元素上被调用),并且不需要尝试导致循环引用的链中的JSON.stringify
每个元素。
Like in the accepted answer, this solution removes all repeating values, not just the circular ones. But at least it does not have exponential complexity.
就像在接受的答案中一样,此解决方案删除了所有重复值,而不仅仅是循环值。但至少它没有指数级的复杂性。
回答by user1541685
just do
做就是了
npm i --save circular-json
then in your js file
然后在你的 js 文件中
const CircularJSON = require('circular-json');
...
const json = CircularJSON.stringify(obj);
https://github.com/WebReflection/circular-json
https://github.com/WebReflection/circular-json
NOTE: I have nothing to do with this package. But I do use it for this.
注意:我与这个包无关。但我确实为此使用它。
Update 2020
2020 年更新
Please note CircularJSON is in maintenance only and flattedis its successor.
请注意 CircularJSON 仅处于维护状态,flatted是它的后继者。
回答by guy mograbi
I really liked Trindaz's solution - more verbose, however it had some bugs. I fixed them for whoever likes it too.
我真的很喜欢 Trindaz 的解决方案——更冗长,但它有一些错误。我也为喜欢它的人修复了它们。
Plus, I added a length limit on my cache objects.
另外,我对缓存对象添加了长度限制。
If the object I am printing is really big - I mean infinitely big - I want to limit my algorithm.
如果我打印的对象真的很大——我的意思是无限大——我想限制我的算法。
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);
};
回答by Alexander Mills
@RobW's answer is correct, but this is more performant ! Because it uses a hashmap/set:
@RobW 的答案是正确的,但这更高效!因为它使用了一个 hashmap/set:
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;
});
};
回答by Nux
Note that there is also a JSON.decycle
method implemented by Douglas Crockford. See his
cycle.js. This allows you to stringify almost any standard structure:
请注意,还有一个JSON.decycle
由 Douglas Crockford 实现的方法。参见他的
cycle.js。这允许您对几乎任何标准结构进行字符串化:
var a = [];
a[0] = a;
a[1] = 123;
console.log(JSON.stringify(JSON.decycle(a)));
// result: '[{"$ref":"$"},123]'.
You can also recreate original object with retrocycle
method. So you don't have to remove cycles from objects to stringify them.
您还可以使用retrocycle
方法重新创建原始对象。因此,您不必从对象中删除循环来对它们进行字符串化。
However this will notwork for DOM Nodes (which are typical cause of cycles in real life use-cases). For example this will throw:
然而,这不适用于 DOM 节点(这是现实生活用例中循环的典型原因)。例如这将抛出:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a)));
I've made a fork to solve that problem (see my cycle.js fork). This should work fine:
我已经制作了一个 fork 来解决这个问题(请参阅我的cycle.js fork)。这应该可以正常工作:
var a = [document.body];
console.log(JSON.stringify(JSON.decycle(a, true)));
Note that in my fork JSON.decycle(variable)
works as in the original and will throw an exception when the variable
contain DOM nodes/elements.
请注意,在我的 fork 中的JSON.decycle(variable)
工作方式与原始版本相同,并且在variable
包含 DOM 节点/元素时会引发异常。
When you use JSON.decycle(variable, true)
you accept the fact that the result will not be reversible (retrocycle will not re-create DOM nodes). DOM elements should be identifiable to some extent though. For example if a div
element has an id then it will be replaced with a string "div#id-of-the-element"
.
当您使用时,JSON.decycle(variable, true)
您接受结果不可逆的事实(retrocycle 不会重新创建 DOM 节点)。DOM 元素在某种程度上应该是可识别的。例如,如果一个div
元素有一个 id ,那么它将被一个 string 替换"div#id-of-the-element"
。
回答by mikermcneil
I'd recommend checking out json-stringify-safefrom @isaacs-- it's used in NPM.
我建议从@isaacs 中查看json-stringify-safe——它在 NPM 中使用。
BTW- if you're not using Node.js, you can just copy and paste lines 4-27 from the relevant part of the source code.
To install:
安装:
$ npm install json-stringify-safe --save
To use:
使用:
// 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));
This yields:
这产生:
{
a: 'foo',
b: '[Circular]'
}
Note that, just like with the vanilla JSON.stringify function as @Rob W mentioned, you can also customize the sanitization behavior by passing in a "replacer" function as the second argument to
stringify()
. If you find yourself needing a simple example of how to do this, I just wrote a custom replacer which coerces errors, regexps, and functions into human-readable strings here.
请注意,就像@Rob W 提到的香草 JSON.stringify 函数一样,您还可以通过将“替换器”函数作为第二个参数传递给
stringify()
. 如果你发现自己需要如何做一个简单的例子,我只是写了要挟的错误,正则表达式和功能为人类可读的字符串自定义替代品在这里。
回答by Trindaz
For future googlers searching for a solution to this problem when you don'tknow the keys of all circular references, you could use a wrapper around the JSON.stringify function to rule out circular references. See an example script at https://gist.github.com/4653128.
对于未来在不知道所有循环引用的键时搜索此问题的解决方案的 googlers ,您可以使用 JSON.stringify 函数的包装器来排除循环引用。请参阅https://gist.github.com/4653128 上的示例脚本。
The solution essentially boils down to keeping a reference to previously printed objects in an array, and checking that in a replacer function before returning a value. It's more constrictive than only ruling out circular references, because it also rules out ever printing an object twice, one of the side affects of which is to avoid circular references.
该解决方案基本上归结为在数组中保留对先前打印对象的引用,并在返回值之前在替换器函数中检查该引用。它比仅排除循环引用更严格,因为它还排除了将对象打印两次的可能性,其副作用之一是避免循环引用。
Example wrapper:
示例包装器:
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);
}
回答by TWickz
Use the JSON.stringify method with a replacer. Read this documentation for more information. http://msdn.microsoft.com/en-us/library/cc836459%28v=vs.94%29.aspx
使用带有替换器的 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));
Figure out a way to populate the replacement array with cyclic references. You can use the typeof method to find if an the property is of type 'object' ( reference ) and an exact equality check ( === ) to verify circular reference.
找出一种用循环引用填充替换数组的方法。您可以使用 typeof 方法来查找属性是否属于“对象”(引用)类型,并使用精确相等性检查 ( === ) 来验证循环引用。