将Map>转换为嵌套JSON。

huangapple go评论67阅读模式
英文:

Convert Map<String, ArrayList<String>> to Nested JSON

问题

这是代码的翻译部分:

public Set<Tree> transform(Map<String, ArrayList<String>> input) {
    Set<String> roots = new HashSet<>(input.keySet());

    Map<String, Tree> map = new HashMap<>();

    for (Map.Entry<String, ArrayList<String>> entry : input.entrySet()) {
        String key = entry.getKey();
        List<String> childKeys = entry.getValue();
        Tree tree = map.get(key);
        if (tree == null) {
            tree = new Tree(key);
            map.put(key, tree);
        }
        for (String childKey : childKeys) {
            roots.remove(childKey);
            Tree child = map.get(childKey);
            if (child == null) {
                child = new Tree(childKey);
                map.put(childKey, child);
            }
            tree.addChild(child);
        }
    }
    Set<Tree> res = new HashSet<>(roots.size());
    for (String key : roots) {
        res.add(map.get(key));
    }
    return res;
}

Tree 类的翻译:

public class Tree {
    private String key;
    private Tree child;

    public Tree(String key){
        this.key = key;
    }

    public void addChild(Tree child){
        this.child = child;
    }
}

你提到的问题是输出结果不符合预期,只有一层的嵌套。这是因为 Tree 类中的 child 字段只能存储一个子节点,所以它只能表示一层的关系。为了实现多层的嵌套,你需要修改 Tree 类以支持多个子节点。这可以通过将 child 字段改为存储子节点列表来实现。这里是修改后的 Tree 类:

public class Tree {
    private String key;
    private List<Tree> children = new ArrayList<>();

    public Tree(String key){
        this.key = key;
    }

    public void addChild(Tree child){
        children.add(child);
    }

    public List<Tree> getChildren() {
        return children;
    }
}

然后,在 transform 方法中,你需要递归地构建嵌套的树结构。这是修改后的代码:

public Set<Tree> transform(Map<String, ArrayList<String>> input) {
    Set<String> roots = new HashSet<>(input.keySet());

    Map<String, Tree> map = new HashMap<>();

    for (Map.Entry<String, ArrayList<String>> entry : input.entrySet()) {
        String key = entry.getKey();
        List<String> childKeys = entry.getValue();
        Tree tree = map.get(key);
        if (tree == null) {
            tree = new Tree(key);
            map.put(key, tree);
        }
        for (String childKey : childKeys) {
            roots.remove(childKey);
            Tree child = map.get(childKey);
            if (child == null) {
                child = new Tree(childKey);
                map.put(childKey, child);
            }
            tree.addChild(child);
        }
    }
    Set<Tree> res = new HashSet<>(roots.size());
    for (String key : roots) {
        res.add(map.get(key));
    }
    return res;
}

通过这些修改,你应该能够得到预期的多层嵌套的树结构。

英文:

So I have a Map<String, ArrayList<String>> parentToChild and want to create basically a "Family Tree" or nested hierarchy. Below is an example of the map but there could be more children at each level e.g. (Claire could have Matt and Bruce as children):

David -&gt; [Claire]
Claire -&gt; [Matt]
Matt -&gt; [Sean, Terry]

I know the root of the tree should be David for the above example and it will only have one root.

Example output

{
 &quot;David&quot;: {
   &quot;Claire&quot;: {
      &quot;Matt&quot;: {
        &quot;Sean&quot;: {},
        &quot;Terry&quot;: {}
      }
    }
  }
}

I've tried few things but genuinely stumped.

EDIT: Code tried so far

public Set&lt;Tree&gt; transform(Map&lt;String, ArrayList&lt;String&gt;&gt; input) {
        Set&lt;String&gt; roots = new HashSet&lt;String&gt;(input.keySet());

        
        Map&lt;String, Tree&gt; map = new HashMap&lt;String, Tree&gt;();

        for (Map.Entry&lt;String, ArrayList&lt;String&gt;&gt; entry : input.entrySet()) {
            String key = entry.getKey();
            List&lt;String&gt; childKeys = entry.getValue();
            Tree tree = map.get(key);
            if (tree == null) {
                tree = new Tree(key);
                map.put(key, tree);
            }
            for (String childKey : childKeys) {
                roots.remove(childKey);
                Tree child = map.get(childKey);
                if (child == null) {
                    child = new Tree(childKey);
                    map.put(childKey, child);
                }
                tree.addChild(child);
            }
        }
        Set&lt;Tree&gt; res = new HashSet&lt;Tree&gt;(roots.size());
        for (String key : roots) {
            res.add(map.get(key));
        }
        return res;
    }

Tree class:

public class Tree {
    private String key;
    private Tree child;

    public Tree(String key){
        this.key = key;
    }

    public void addChild(Tree child){
        this.child = child;
    }
}

The issue is when I use this code the output (What is in the set after debugging/printing) I get is

David:
  Claire:
    Matt:
     Terry:

答案1

得分: 2

以下是您要的代码的翻译部分:

你可以使用一个 `Map&lt;String,Object&gt;`:

private static final Gson GSON = new GsonBuilder()
        .setPrettyPrinting()
        .create();

public static void main(String[] args) {
    Map&lt;String, List&lt;String&gt;&gt; input = new HashMap&lt;&gt;();
    input.put(&quot;David&quot;, Arrays.asList(&quot;Claire&quot;));
    input.put(&quot;Claire&quot;, Arrays.asList(&quot;Matt&quot;));
    input.put(&quot;Matt&quot;, Arrays.asList(&quot;Sean&quot;, &quot;Terry&quot;));
    Map&lt;String,Object&gt; result = new HashMap&lt;&gt;();
    convert(input, &quot;David&quot;, result);
    GSON.toJson(result, System.out);
}

private static void convert(Map&lt;String, List&lt;String&gt;&gt; input, String root,
        Map&lt;String,Object&gt; result) {
    if (!result.containsKey(root)) {
        Map&lt;String,Object&gt; rootObj = new HashMap&lt;&gt;();
        result.put(root, rootObj);
        List&lt;String&gt; children = input.get(root);
        if (children != null) {
            for (String child: children) {
                convert(input, child, rootObj);
            }
        }
    }
}

输出:

{
  &quot;David&quot;: {
    &quot;Claire&quot;: {
      &quot;Matt&quot;: {
        &quot;Terry&quot;: {},
        &quot;Sean&quot;: {}
      }
    }
  }
}

如果您需要进一步的帮助,请告诉我。

英文:

You could use a Map&lt;String,Object&gt;:

private static final Gson GSON = new GsonBuilder()
.setPrettyPrinting()
.create();
public static void main(String[] args) {
Map&lt;String, List&lt;String&gt;&gt; input = new HashMap&lt;&gt;();
input.put(&quot;David&quot;, Arrays.asList(&quot;Claire&quot;));
input.put(&quot;Claire&quot;, Arrays.asList(&quot;Matt&quot;));
input.put(&quot;Matt&quot;, Arrays.asList(&quot;Sean&quot;, &quot;Terry&quot;));
Map&lt;String,Object&gt; result = new HashMap&lt;&gt;();
convert(input, &quot;David&quot;, result);
GSON.toJson(result, System.out);
}
private static void convert(Map&lt;String, List&lt;String&gt;&gt; input, String root,
Map&lt;String,Object&gt; result) {
if (!result.containsKey(root)) {
Map&lt;String,Object&gt; rootObj = new HashMap&lt;&gt;();
result.put(root, rootObj);
List&lt;String&gt; children = input.get(root);
if (children != null) {
for (String child: children) {
convert(input, child, rootObj);
}
}
}
}

Output:

{
&quot;David&quot;: {
&quot;Claire&quot;: {
&quot;Matt&quot;: {
&quot;Terry&quot;: {},
&quot;Sean&quot;: {}
}
}
}
}

答案2

得分: 0

在Java世界中,您可以访问Saxon 9.8或更高版本的HE,其中XPath 3.1、XQuery 3.1或XSLT 3.0都支持将您的初始映射表示为XdmMap并对其进行处理,例如使用XQuery:

declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method 'json';
declare option output:indent 'yes';
declare variable $map as map(xs:string, array(xs:string)) external := map {
'David' : [ 'Claire' ],
'Claire' : [ 'Matt' ],
'Matt' : [ 'Sean', 'Terry' ]
};
declare variable $root as xs:string external := 'David';
declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {
map:merge($children ! map { . : local:create-tree($map, $map(.)) })
};
local:create-tree($map, $root)

简单的Java示例,使用Saxon 10 HE运行此示例(其API文档位于http://saxonica.com/html/documentation/using-xquery/api-query/s9api-query.html),将Java的Map传递给XQuery(内联插入为字符串,但当然也可以从文件中加载):

import java.util.HashMap;
import java.util.Map;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmMap;

public class SaxonJavaMapToNestedJSONObject {
    
    public static void main(String[] args) throws SaxonApiException {
        
        Map<String, String[]> map = new HashMap<>();
        map.put("David", new String[] { "Claire" });
        map.put("Claire", new String[] { "Matt" });
        map.put("Matt", new String[] { "Sean", "Terry" });
        
        Processor processor = new Processor(true);
        
        XQueryCompiler compiler = processor.newXQueryCompiler();
        
        XQueryExecutable executable = compiler.compile("declare namespace map = \"http://www.w3.org/2005/xpath-functions/map\";\n" +
        "\n" +
        "declare namespace output = \"http://www.w3.org/2010/xslt-xquery-serialization\";\n" +
        "\n" +
        "declare option output:method 'json';\n" +
        "declare option output:indent 'yes';\n" +
        "\n" +
        "declare variable $map as map(xs:string, array(xs:string)) external;\n" +
        "\n" +
        "declare variable $root as xs:string external := 'David';\n" +
        "\n" +
        "declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {\n" +
        "    map:merge($children ! map { . : local:create-tree($map, $map(.)) })\n" +
        "};\n" +
        "\n" +
        "local:create-tree($map, $root)");
        
         XQueryEvaluator evaluator = executable.load();
         
         evaluator.setExternalVariable(new QName("map"), XdmMap.makeMap(map));
         
         evaluator.run(processor.newSerializer(System.out));
         
    }
    
}

当然,您也可以从Java设置root变量:evaluator.setExternalVariable(new QName("root"), new XdmAtomicValue("David"));

英文:

In the Java world you have access to Saxon 9.8 or later HE where XPath 3.1 or XQuery 3.1 or XSLT 3.0 all have support for representing your initial map as an XdmMap and processing them, for instance with XQuery:

declare namespace map = &quot;http://www.w3.org/2005/xpath-functions/map&quot;;
declare namespace output = &quot;http://www.w3.org/2010/xslt-xquery-serialization&quot;;
declare option output:method &#39;json&#39;;
declare option output:indent &#39;yes&#39;;
declare variable $map as map(xs:string, array(xs:string)) external := map {
&#39;David&#39; : [ &#39;Claire&#39; ],
&#39;Claire&#39; : [ &#39;Matt&#39; ],
&#39;Matt&#39; : [ &#39;Sean&#39;, &#39;Terry&#39; ]
};
declare variable $root as xs:string external := &#39;David&#39;;
declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {
map:merge($children ! map { . : local:create-tree($map, $map(.)) })
};
local:create-tree($map, $root)

https://xqueryfiddle.liberty-development.net/3Nzd8bV

A simple Java example to run this with Saxon 10 HE (its API documentation is at http://saxonica.com/html/documentation/using-xquery/api-query/s9api-query.html), passing a Java Map to the XQuery (inserted inline as a string but could of course be loaded from a file instead) is:

import java.util.HashMap;
import java.util.Map;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmMap;
public class SaxonJavaMapToNestedJSONObject {
public static void main(String[] args) throws SaxonApiException {
Map&lt;String, String[]&gt; map = new HashMap&lt;&gt;();
map.put(&quot;David&quot;, new String[] { &quot;Claire&quot; });
map.put(&quot;Claire&quot;, new String[] { &quot;Matt&quot; });
map.put(&quot;Matt&quot;, new String[] { &quot;Sean&quot;, &quot;Terry&quot; });
Processor processor = new Processor(true);
XQueryCompiler compiler = processor.newXQueryCompiler();
XQueryExecutable executable = compiler.compile(&quot;declare namespace map = \&quot;http://www.w3.org/2005/xpath-functions/map\&quot;;\n&quot; +
&quot;\n&quot; +
&quot;declare namespace output = \&quot;http://www.w3.org/2010/xslt-xquery-serialization\&quot;;\n&quot; +
&quot;\n&quot; +
&quot;declare option output:method &#39;json&#39;;\n&quot; +
&quot;declare option output:indent &#39;yes&#39;;\n&quot; +
&quot;\n&quot; +
&quot;declare variable $map as map(xs:string, array(xs:string)) external;\n&quot; +
&quot;\n&quot; +
&quot;declare variable $root as xs:string external := &#39;David&#39;;\n&quot; +
&quot;\n&quot; +
&quot;declare function local:create-tree($map as map(xs:string, array(xs:string)), $children as xs:string*) as map(*) {\n&quot; +
&quot;    map:merge($children ! map { . : local:create-tree($map, $map(.)) })\n&quot; +
&quot;};\n&quot; +
&quot;\n&quot; +
&quot;local:create-tree($map, $root)&quot;);
XQueryEvaluator evaluator = executable.load();
evaluator.setExternalVariable(new QName(&quot;map&quot;), XdmMap.makeMap(map));
evaluator.run(processor.newSerializer(System.out));
}
}

Of course you could set the root variable as well from Java: evaluator.setExternalVariable(new QName(&quot;root&quot;), new XdmAtomicValue(&quot;David&quot;));

huangapple
  • 本文由 发表于 2020年7月28日 17:30:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63131081.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定