Преобразование вложенной разделенной строки в объект - PullRequest
1 голос
/ 09 мая 2020

Я пытаюсь преобразовать вложенный файл, разделенный точками, например:

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 голосов
/ 10 мая 2020

Вероятно, есть более сжатые и элегантные способы сделать это, но они должны быть достаточно устойчивыми к ошибкам, отсутствующим узлам и несортированным данным (см. Пример ввода ниже; я предполагаю, что вы можете прочитать файл, поэтому я начну список строк).

Для начала неплохо использовать org.json или какую-нибудь другую библиотеку, чтобы не изобретать колесо и не разбираться с различными крайними случаями в процессе сериализации. Если вы имеете дело с простыми строками, какими кажетесь, это может быть выполнимо, но свертывание их самостоятельно должно быть последним средством. через путь, создавая Node объекты для любых отсутствующих элементов и сохраняя их в ha sh с ключом по имени пути. Последние элементы в пути каждой строки - это листья, и им будут назначены свойства для этой строки, в то время как внутренние узлы ищут и добавляют потомков из ранее проанализированных узлов с помощью ha sh. Это создает связанную n-мерную древовидную структуру.

Следующим шагом является поиск root (мы утверждаем, что существует только один root) и запуск сериализатора JSON, начиная с листьев и рекурсивная передача сериализованных объектов обратно родителям.

Обратите внимание, что org.JSON будет отсортировать ключи, но результат будет в такой же степени соответствовать JSON spe c, как и ваш ожидаемый результат, что гарантирует отсутствие упорядочения для свойства объекта.

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"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...