将嵌套的分隔字符串转换为对象

时间:2020-05-09 11:05:57

标签: java json tree delimiter-separated-values

我正在尝试像这样转换嵌套的点分隔文件:

AAA, value1, value11
AAA.BBB, value3, value22
AAA.BBB.CCC, value3, value33
AAA.DDD, values44, value44

到Object,它将能够描述为JSON:

{
    "name": "AAA",
    "type": "value1",
    "property": "value11",
    "children": [
        {
            "name": "BBB",
            "type": "value2",
            "property": "value22",
            "children": [
                {
                    "name": "CCC",
                    "type": "value3",
                    "property": "value33",
                    "children": []
                }
            ]
        },
        {
            "name": "DDD",
            "type": "value4",
            "property": "value44",
            "children": []
        }
    ]
}

请告诉我,如何在Java中实现这种情况?

我已经尝试过像这样手工制作JSON,但是对我来说不起作用。我无法正确实现父子结构。

static abstract class Node {
}

static class IntermediateNode extends Node {
    public Map<String, Node> keyValueMap = new LinkedHashMap<>();

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[{");
        sb.append(keyValueMap.entrySet().stream().map(entry -> "\"name\":\"" + entry.getKey() + "\", \"children\"" + ":" + entry.getValue())
                .collect(Collectors.joining(", ")));
        sb.append("}]");
        return sb.toString();
    }
}

public void test(String source) {
    IntermediateNode root = new IntermediateNode();

    String[] lines = source.split("\\n");
    for (String line : lines) {
        List<String> values = new LinkedList<>(Arrays.asList(line.split(",")));
        String[] paths = values.get(0).split("\\.");

        IntermediateNode currentNode = root;
        for (int i = 0; i < paths.length - 1; i++) {
            Node node = currentNode.keyValueMap.get(paths[i]);
            if (node == null) {
                IntermediateNode child = new IntermediateNode();
                currentNode.keyValueMap.put(paths[i].trim(), child);
                currentNode = child;
            } else {
                currentNode = (IntermediateNode) node;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

执行此操作的方法可能更简洁明了,但这对于错误,丢失节点和未排序的数据应该非常健壮(请参见下面的示例输入;我假设您可以读取文件,因此我将开始使用字符串列表)。

对于初学者而言,最好使用org.json或其他一些库来避免重新发明轮子并在序列化过程中处理各种边缘情况。如果您似乎正在处理简单的字符串,那么它也许是可行的,但是自己滚动它应该是最后的选择。

关于算法,我的方法是标记每行并向后浏览路径,为所有丢失的项目创建Node对象,并将其保留在以路径名作为关键字的哈希中。每行路径中的最后一项是叶子,将被分配该行的属性,而内部节点将查找并使用哈希从先前解析的节点添加子级。这样会建立一个链接的n元树结构。

下一步是找到根(我们断言只有一个根)并运行JSON序列化器,从叶子开始,然后将序列化的对象递归传递回父级。

请注意,org.JSON将对键进行排序,但结果符合JSON规范,这与预期的输出一样多,从而保证了对象属性没有顺序。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;

class Node {
    String name;
    String type;
    String property;
    ArrayList<Node> children;

    public Node(String name, String type, String property, ArrayList<Node> children) {
        this.name = name;
        this.type = type;
        this.property = property;
        this.children = children;
    }

    public JSONObject toJSON() {
        JSONArray serializedChildren = new JSONArray();

        for (Node child : children) {
            serializedChildren.put(child.toJSON());
        }

        return new JSONObject()
            .put("name", name)
            .put("type", type)
            .put("property", property)
            .put("children", serializedChildren);
    }
}

public class Main {
    private static void validate(boolean cond, String msg) {
        if (!cond) throw new IllegalArgumentException(msg);
    }

    private static void parseLine(HashMap<String, Node> nodes, String line) {
        String[] tokens = line.split(", ");
        validate(tokens.length == 3, "There must be 3 tokens per line");
        String[] names = tokens[0].split("\\.");
        validate(names.length != 0, "There must be at least one name in the path");

        for (int i = names.length - 1; i >= 0; i--) {
            String name = String.join(".", Arrays.copyOfRange(names, 0, i + 1));
            Node node = nodes.get(name);

            if (node == null) {
                nodes.put(name, node = new Node(names[i], null, null, new ArrayList<>()));
            }

            if (i < names.length - 1) {
                Node child = nodes.get(name + "." + names[i+1]);
                validate(child != null, "Child lookup must succeed");

                if (!node.children.contains(child)) {
                    node.children.add(child);
                }
            }
            else {
                node.type = tokens[1];
                node.property = tokens[2];
            }
        }
    }

    public static HashSet<Node> parseNodes(List<String> lines) {
        var nodes = new HashMap<String, Node>();
        lines.forEach((line) -> parseLine(nodes, line));

        for (Node node : nodes.values()) {
            String[] tokens = node.name.split("\\.");
            node.name = tokens[tokens.length-1];
        }

        return new HashSet<Node>(nodes.values());
    }

    public static Node findRoot(HashSet<Node> tree) {
        var candidates = new HashSet<Node>(tree);
        tree.forEach((node) -> candidates.removeAll(node.children));
        validate(candidates.size() == 1, "There must be one root");

        for (Node root : candidates) return root;

        return null;
    }

    public static void main(String[] args) {
        var lines = Arrays.asList(
            "AAA.BBB.CCC, value3, value33",
            "AAA.BBB.CCC.EEE.FFF, value5, value55",
            "AAA.BBB, value3, value22",
            "AAA, value1, value11",
            "AAA.DDD, values44, value44"
        );
        Node root = findRoot(parseNodes(lines));
        System.out.println(root.toJSON().toString(2));
    }
}

编译并执行:

javac -cp "json-20190722.jar" Main.java && java -cp "json-20190722.jar;." Main

输出:

{
  "children": [
    {
      "children": [{
        "children": [{
          "children": [{
            "children": [],
            "name": "FFF",
            "property": "value55",
            "type": "value5"
          }],
          "name": "EEE"
        }],
        "name": "CCC",
        "property": "value33",
        "type": "value3"
      }],
      "name": "BBB",
      "property": "value22",
      "type": "value3"
    },
    {
      "children": [],
      "name": "DDD",
      "property": "value44",
      "type": "values44"
    }
  ],
  "name": "AAA",
  "property": "value11",
  "type": "value1"
}
相关问题