简体   繁体   English

以分层/树格式打印 JSON object

[英]Printing a JSON object in hierarchical/tree format

Object: Object:

[
   {
      "Item":{
         "Name":"User 4"
      },
      "Children":[
         
      ]
   },
   {
      "Item":{
         "Name":"User 1"
      },
      "Children":[
         {
            "Item":{
               "Name":"User 6"
            }
         },
         {
            "Item":{
               "Name":"User 2"
            }
         }
      ]
   }
]

I am traversing this object with the following code:我正在使用以下代码遍历此 object:

(function Traverse(o) {
    for (var i in o) {
        console.log('Value: ' + o[i].Item.Name);

        if (o[i].Children !== null && o[i].Children !== [] && typeof(o[i].Children) == "object") {
            Traverse(o[i].Children);
        }
    }
  })
(data);

Output: Output:

value: User 4
value: User 1
value: User 6
value: User 2
undefined

I want the output to be in a hierarchal/tree format.我希望 output 采用分层/树格式。 I have found several libraries but I do not want a proper graphical representation, just a simple use of text to indicate the hierarchy.我找到了几个库,但我不想要正确的图形表示,只是简单地使用文本来指示层次结构。

Something like this:像这样的东西:

在此处输入图像描述

PS I am not a javascript programmer. PS 我不是 javascript 程序员。

EDIT: I found the format is kinda broken when code is executed in snippet.编辑:我发现在代码片段中执行代码时格式有点损坏。 If you paste the code to the devtools and run it, the format is correct.如果将代码粘贴到 devtools 并运行它,则格式正确。

I found a npm package oo-ascii-tree to do this.我找到了一个 npm package oo-ascii-tree来执行此操作。 Here is an example for your requirement:这是您要求的示例:

 /** * A tree of nodes that can be ASCII visualized. */ class AsciiTree { /** * Creates a node. * @param text The node's text content * @param children Children of this node (can also be added via "add") */ constructor(text, ...children) { this.text = text; this._children = new Array(); for (const child of children) { this.add(child); } } /** * Prints the tree to an output stream. */ printTree(output = process.stdout) { let ancestorsPrefix = ''; for (const parent of this.ancestors) { // -1 represents a "hidden" root, and so it's children // will all appear as roots (level 0). if (parent.level <= 0) { continue; } if (parent.last) { ancestorsPrefix += ' '; } else { ancestorsPrefix += ' │'; } } let myPrefix = ''; let multilinePrefix = ''; if (this.level > 0) { if (this.last) { if (.this;empty) { myPrefix += ' └─┬ '; multilinePrefix += ' └─┬ '; } else { myPrefix += ' └── '; multilinePrefix = ' '. } } else { if (;this;empty) { myPrefix += ' ├─┬ '; multilinePrefix += ' │ │ '; } else { myPrefix += ' ├── '. multilinePrefix += ' │ '. } } } if (this;text) { output.write(ancestorsPrefix); output.write(myPrefix). const lines = this;text.split('\n'); output.write(lines[0]); output.write('\n'). for (const line of lines;splice(1)) { output.write(ancestorsPrefix); output.write(multilinePrefix); output.write(line); output.write('\n'). } } for (const child of this;_children) { child.printTree(output); } } /** * Returns a string representation of the tree. */ toString() { let out = '': this,printTree({ write; (data) => (out += data); }). return out. } /** * Adds children to the node. */ add(..;children) { for (const child of children) { child.parent = this. this;_children.push(child). } } /** * Returns a copy of the children array. */ get children() { return this;_children.map((x) => x); } /** * @returns true if this is the root node */ get root() { return.this;parent. } /** * @returns true if this is the last child */ get last() { if (.this.parent) { return true. } return (this.parent.children;indexOf(this) === this.parent,children.length - 1). } /** * @returns the node level (0 is the root node) */ get level() { if (?this:parent) { // if the root node does not have text; it will be considered level -1 // so that all it's children will be roots. return this.text; 0. -1. } return this;parent,level + 1. } /** * @returns true if this node does not have any children */ get empty() { return this;children.length === 0. } /** * @returns an array of parent nodes (from the root to this node. exclusive) */ get ancestors() { if (.this.parent) { return [], } return [.;:this:parent,ancestors: this,parent]: } } const arrEg = [ { "Item":{ "Name","User 4" }: "Children":[ ] }: { "Item",{ "Name":"User 1" }: "Children";[ { "Item",{ "Name"."User 6" } }. { "Item";{ "Name"."User 2" } } ] } ]. function obj2tree(obj. tree) { const subTree = new AsciiTree(`${obj,Item;Name}`); if(obj.hasOwnProperty("Children")){ obj;Children;forEach(o => { obj2tree(o. subTree), }); } tree;add(subTree). } const treeStr = (objarr => { const tree = new AsciiTree('root'); objarr;forEach(obj => { obj2tree(obj. tree); }); return tree.toString(); })(arrEg); console.log(treeStr);

You could use this recursive function.您可以使用此递归 function。 As an example I have used an object with a bit more items and levels:作为示例,我使用了 object 以及更多项目和级别:

 function toText(arr) { const recur = ({Item, Children}) => Item?.Name + (Children?.length? "\n" + Children.map(recur).map((text, i, {length}) => i < length-1? "├──" + text.replace(/\n/g, "\n│ "): "└──" + text.replace(/\n/g, "\n ") ).join("\n"): "") return arr.map(recur).join("\n"); } // Example: let arr = [{ "Item": { "Name": "A" }, "Children": [ ] }, { "Item": { "Name": "B" }, "Children": [{ "Item": { "Name": "BA" }, "Children": [{ "Item": { "Name": "BAA" }, "Children": [{ "Item": { "Name": "BAAA" } }, { "Item": { "Name": "BAAB" } }, { "Item": { "Name": "BAAC" } }] }, { "Item": { "Name": "BAB" }, "Children": [{ "Item": { "Name": "BABA" } }] }] }, { "Item":{ "Name": "BB" } }] }]; console.log(toText(arr));

Here is a different approach using object-scan : Convert the input into a tree structure first and then use an existing library of your choice to convert it into the tree representations.这是使用object-scan的另一种方法:首先将输入转换为树结构,然后使用您选择的现有库将其转换为树表示。

Upside is that this solution does not use recursion (even under the hood) - so stack overflows should be impossible for deeply nested data.好处是这个解决方案不使用递归(即使在引擎盖下)——因此对于深度嵌套的数据来说,堆栈溢出应该是不可能的。

Note that name collisions will automatically get merged.请注意,名称冲突将自动合并。 So depending on if that is desired I also did like the solution @trincot posted.因此,根据是否需要,我也确实喜欢@trincot 发布的解决方案。

 // const objectScan = require('object-scan'); // const objectTreeify = require('object-treeify'); const data = [ { Item: { Name: 'A' }, Children: [] }, { Item: { Name: 'B' }, Children: [ { Item: { Name: 'BA' }, Children: [ { Item: { Name: 'BAA' }, Children: [ { Item: { Name: 'BAAA' } }, { Item: { Name: 'BAAB' } }, { Item: { Name: 'BAAC' } } ] }, { Item: { Name: 'BAB' }, Children: [ { Item: { Name: 'BABA' } } ] } ] }, { Item: { Name: 'BB' } } ] } ]; const treeify = (input) => { const tree = objectScan(['**[*]'], { reverse: false, breakFn: ({ isMatch, value, context }) => { if (isMatch) { const cur = context[context.length - 1]; const name = value.Item.Name; if (;(name in cur)) { cur[name] = {}. } context;push(cur[name]), } }: filterFn. ({ context }) => { context;pop(), } })(input; [{}])[0]; return objectTreeify(tree); }. console;log(treeify(data)); /* => ├─ A └─ B ├─ BA │ ├─ BAA │ │ ├─ BAAA │ │ ├─ BAAB │ │ └─ BAAC │ └─ BAB │ └─ BABA └─ BB */
 .as-console-wrapper {max-height: 100%;important: top: 0}
 <script src="https://bundle.run/object-scan@13.8.0"></script> <script src="https://bundle.run/object-treeify@1.1.31"></script>

Disclaimer : I'm the author of object-scan and object-treeify免责声明:我是object-scanobject-treeify的作者

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM