【JSON 處理】將JSON解析爲樹,並進行層次遍歷和深度優先遍歷

目錄

1. 基本思路

2. 引入依賴

3. 定義節點

4. 定義樹

5. 執行效果



1. 基本思路

我所採用的樹形存儲是 “孩子鏈表表示法” 存儲

即,節點定義除了包含本節點相關信息(節點名、路徑、層級、數據類型和具體數據)之外,還包含子節點鏈表

需要說明的是,我所採用的方法只適用於根節點爲 JSONObject 類型的輸入,不適用於根節點爲 JSONArray

2. 引入依賴

實現藉助於 fastjson,pom 文件加入配置:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>

3. 定義節點

節點定義包含本節點相關信息(節點名、路徑、層級、數據類型和具體數據)和子節點鏈表

package com.amwalle.walle.util;

import java.util.List;

public class JSONNode {
    private String nodeName;

    private String nodePath;

    private int level;

    private String dataType;

    private Object data;

    private List<JSONNode> children;

    public String getNodeName() {
        return nodeName;
    }

    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }

    public String getNodePath() {
        return nodePath;
    }

    public void setNodePath(String nodePath) {
        this.nodePath = nodePath;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public String getDataType() {
        return dataType;
    }

    public void setDataType(String dataType) {
        this.dataType = dataType;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

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

    public void setChildren(List<JSONNode> children) {
        this.children = children;
    }
}

4. 定義樹

層次遍歷藉助 Quene 實現,深度優先遍歷藉助 Stack 實現

package com.amwalle.walle.util;

import com.alibaba.fastjson.*;
import com.alibaba.fastjson.parser.Feature;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;

public class JSONTree {

    public static JSONNode createJSONTree(Object nodeData, String nodeName, String nodePath, int level) {
        JSONNode node = new JSONNode();
        node.setNodeName(nodeName);
        node.setNodePath(nodePath);
        node.setLevel(level);
        node.setData(nodeData);

        if (nodeData == null) {
            node.setDataType(null);
            return node;
        }

        List<JSONNode> childrenList = new LinkedList<>();

        if (nodeData instanceof JSONObject) {
            node.setDataType("Object");


            JSONObject jsonObject = (JSONObject) nodeData;
            Set<String> keySet = jsonObject.keySet();

            level++;
            for (String key : keySet) {
                JSONNode childNode = createJSONTree(jsonObject.get(key), key, nodePath + "/" + key, level);
                childrenList.add(childNode);
            }

            node.setChildren(childrenList);
        } else if (nodeData instanceof JSONArray) {
            node.setDataType("Array");

            JSONArray jsonArray = (JSONArray) nodeData;

            for (int index = 0, size = jsonArray.size(); index < size; index++) {
                // Array 元素,不是單個節點;所以將元素下一級的孩子鏈表整個作爲 Array 的孩子鏈表
                JSONNode childNode = createJSONTree(jsonArray.get(index), nodeName, nodePath + "[" + index + "]", level);
                if (childNode.getChildren() != null) {
                    childrenList.addAll(childNode.getChildren());
                }
            }

            node.setChildren(childrenList);
        } else {
            node.setChildren(null);
            node.setDataType(nodeData.getClass().getName());
        }

        return node;
    }

    public static List<JSONNode> levelTraversal(JSONNode rootNode) {
        if (rootNode == null) {
            return null;
        }

        Queue<JSONNode> queue = new ConcurrentLinkedQueue<>();
        queue.add(rootNode);

        List<JSONNode> nodeList = new LinkedList<>();

        while (!queue.isEmpty()) {
            JSONNode node = queue.poll();
            nodeList.add(node);

            if (node != null) {
                if (node.getChildren() != null) {
                    queue.addAll(node.getChildren());
                }
            }
        }

        return nodeList;
    }

    public static List<JSONNode> depthFirstTraversal(JSONNode rootNode) {
        if (rootNode == null) {
            return null;
        }

        Stack<JSONNode> stack = new Stack<>();
        stack.push(rootNode);

        List<JSONNode> nodeList = new LinkedList<>();

        while (!stack.isEmpty()) {
            JSONNode node = stack.pop();
            nodeList.add(node);

            if (node == null || node.getChildren() == null) {
                continue;
            }

            List<JSONNode> children = node.getChildren();

            for (int index = children.size() - 1; index >= 0; index--) {
                stack.push(children.get(index));
            }
        }

        return nodeList;
    }

    public static void main(String[] args) {
        String data = "{\n" +
                "  \"checked\": false,\n" +
                "  \"dimensions\": {\n" +
                "    \"width\": 5,\n" +
                "    \"height\": 10\n" +
                "  },\n" +
                "  \"id\": 1,\n" +
                "  \"name\": \"A green door\",\n" +
                "  \"price\": 12.5,\n" +
                "  \"tags\": [\n" +
                "    \"home\",\n" +
                "    \"green\"\n" +
                "  ]\n" +
                "}";

        JSONObject jsonObject = JSONObject.parseObject(data, JSONObject.class, Feature.OrderedField);

        JSONNode root = JSONTree.createJSONTree(jsonObject, "root", "#", 0);
        List<JSONNode> list = JSONTree.depthFirstTraversal(root);
        for (JSONNode jsonNode : list) {
            System.out.printf("%" + (jsonNode.getLevel() * 4 + 1) + "s" + "%1$s%2$s%n", " ", jsonNode.getLevel() + "--" + jsonNode.getNodePath());
        }
    }
}

5. 執行效果

層次遍歷結果:

  0--#
      1--#/checked
      1--#/dimensions
      1--#/id
      1--#/name
      1--#/price
      1--#/tags
          2--#/dimensions/width
          2--#/dimensions/height
 

 

深度優先遍歷結果:

0--#
      1--#/checked
      1--#/dimensions
          2--#/dimensions/width
          2--#/dimensions/height
      1--#/id
      1--#/name
      1--#/price
      1--#/tags
 

附加一個對 Array 進行了去重的構造方法:

package com.amwalle.walle.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;

public class JSONTree {

    private static HashSet<String> pathSet = new HashSet<>();

    /**
     * This method is used for creating a JSON Tree
     *
     * @param nodeData Node data
     * @param nodeName Node name
     * @param nodePath Node path
     * @param level    Node level
     * @return The node
     */
    public static JSONNode createJSONTree(Object nodeData, String nodeName, String nodePath, int level) {
        JSONNode node = new JSONNode();
        node.setNodeName(nodeName);
        node.setNodePath(nodePath);
        node.setLevel(level);
        node.setData(nodeData);

        if (nodeData == null) {
            node.setDataType(null);
            return node;
        }

        node.setDataType(nodeData.getClass().getName());

        List<JSONNode> childrenList = new LinkedList<>();

        if (nodeData instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) nodeData;
            Set<String> keySet = jsonObject.keySet();

            level++;
            for (String key : keySet) {
                JSONNode childNode = createJSONTree(jsonObject.get(key), key, nodePath + "/" + key, level);
                childrenList.add(childNode);
            }

            node.setChildren(childrenList);
        } else if (nodeData instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) nodeData;

            for (int index = 0, size = jsonArray.size(); index < size; index++) {
                // Array 元素,不是單個節點;所以將元素下一級的孩子鏈表整個作爲 Array 的孩子鏈表
                JSONNode childNode = createJSONTree(jsonArray.get(index), nodeName, nodePath + "[" + index + "]", level);
                if (childNode.getChildren() != null) {
                    childrenList.addAll(childNode.getChildren());
                }
            }

            node.setChildren(childrenList);
        } else {
            node.setChildren(null);
        }

        return node;
    }

    /**
     * This method is used for creating a JSON tree, in which all path for node is unique.
     *
     * @param nodeData Node data
     * @param nodeName Node name
     * @param nodePath Node path
     * @param level    Node level
     * @return Node
     */
    public static JSONNode createDeduplicateJSONTree(Object nodeData, String nodeName, String nodePath, int level) {
        JSONNode node = new JSONNode();
        node.setNodeName(nodeName);
        node.setNodePath(nodePath);
        node.setLevel(level);
        node.setData(nodeData);

        if (nodeData == null) {
            node.setDataType(null);
            return node;
        }

        node.setDataType(NodeType.getJSONTypeByJavaType(nodeData.getClass().getSimpleName()));

        List<JSONNode> childrenList = new LinkedList<>();

        if (nodeData instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) nodeData;
            Set<String> keySet = jsonObject.keySet();

            level++;
            for (String key : keySet) {
                JSONNode childNode = createDeduplicateJSONTree(jsonObject.get(key), key, nodePath + "/" + key, level);
                if (childNode != null) {
                    childrenList.add(childNode);
                }
            }

            node.setChildren(childrenList);
        } else if (nodeData instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) nodeData;

            for (Object aJsonArray : jsonArray) {
                // Array 元素,不是單個節點;所以將元素下一級的孩子鏈表整個作爲 Array 的孩子鏈表
                JSONNode childNode = createDeduplicateJSONTree(aJsonArray, nodeName, nodePath, level);

                if (childNode != null && childNode.getChildren() != null) {
                    childrenList.addAll(childNode.getChildren());
                }
            }

            node.setChildren(childrenList);
        } else {
            // 如果路徑已經存在,則捨棄該節點
            if (!pathSet.add(node.getNodePath())) {
                return null;
            }
            node.setChildren(null);
        }

        return node;
    }

    public static List<JSONNode> levelTraversal(JSONNode rootNode) {
        if (rootNode == null) {
            return null;
        }

        Queue<JSONNode> queue = new ConcurrentLinkedQueue<>();
        queue.add(rootNode);

        List<JSONNode> nodeList = new LinkedList<>();

        while (!queue.isEmpty()) {
            JSONNode node = queue.poll();
            nodeList.add(node);

            if (node != null) {
                if (node.getChildren() != null) {
                    queue.addAll(node.getChildren());
                }
            }
        }

        return nodeList;
    }

    public static List<JSONNode> depthFirstTraversal(JSONNode rootNode) {
        if (rootNode == null) {
            return null;
        }

        Stack<JSONNode> stack = new Stack<>();
        stack.push(rootNode);

        List<JSONNode> nodeList = new LinkedList<>();

        while (!stack.isEmpty()) {
            JSONNode node = stack.pop();
            nodeList.add(node);

            if (node == null || node.getChildren() == null) {
                continue;
            }

            List<JSONNode> children = node.getChildren();

            for (int index = children.size() - 1; index >= 0; index--) {
                stack.push(children.get(index));
            }
        }

        return nodeList;
    }

    public static JSONObject createJSONSchema(JSONNode jsonNode, JSONObject jsonObject, String id) {
        jsonObject.fluentPut("$id", id);
        jsonObject.fluentPut("type", jsonNode.getDataType());

        List<JSONNode> children = jsonNode.getChildren();

        if (children == null) {
            return jsonObject;
        }

        JSONObject schema = JSONObject.parseObject("{}", JSONObject.class, Feature.OrderedField);
        for (JSONNode node : children) {
            JSONObject childSchema = JSONObject.parseObject("{}", JSONObject.class, Feature.OrderedField);

            createJSONSchema(node, childSchema, id + "properties/" + node.getNodeName());
            schema.fluentPut(node.getNodeName(), childSchema);
        }

        // TODO 在這裏區分子節點的類別,然後判斷應該加入什麼關鍵字
        if ("array".equals(jsonNode.getDataType())) {
            JSONObject arraySchema = JSONObject.parseObject("{}", JSONObject.class, Feature.OrderedField);
            arraySchema.fluentPut("$id", id + "/items");

            if (jsonNode.getChildren().isEmpty()) {
                Object data = jsonNode.getData();
                JSONArray dataArray = (JSONArray) data;
                if (!dataArray.isEmpty()) {
                    arraySchema.fluentPut("type", NodeType.getJSONTypeByJavaType(dataArray.get(0).getClass().getSimpleName()));
                }
            } else {
                arraySchema.fluentPut("type", jsonNode.getChildren().get(0).getDataType());
                arraySchema.fluentPut("properties", schema);
            }

            jsonObject.fluentPut("items", arraySchema);
        } else {
            jsonObject.fluentPut("properties", schema);
        }

        return jsonObject;
    }

    public static void main(String[] args) {
        String data = "{\n" +
                "    \"TestNull\": null,\n" +
                "    \"country\": [\n" +
                "        {\n" +
                "            \"A\": \"A\",\n" +
                "            \"B\": \"B\",\n" +
                "            \"C\": \"C\"\n" +
                "        },\n" +
                "        {\n" +
                "            \"C\": \"C\",\n" +
                "            \"D\": \"D\"\n" +
                "        }\n" +
                "    ],\n" +
                "    \"fast_open\": false,\n" +
                "    \"local_address\": \"127.0.0.1\",\n" +
                "    \"local_port\": 1080,\n" +
                "    \"method\": \"aes-256-cfb\",\n" +
                "    \"password\": \"test%\",\n" +
                "    \"server\": \"0.0.0.0\",\n" +
                "    \"server_port\": 8388,\n" +
                "    \"test\": [\n" +
                "        \"hello\",\n" +
                "        \"world\"\n" +
                "    ],\n" +
                "    \"timeout\": 300,\n" +
                "    \"workers\": 1\n" +
                "}";

        JSONObject jsonObject = JSONObject.parseObject(data, JSONObject.class, Feature.OrderedField);

        JSONNode root = JSONTree.createDeduplicateJSONTree(jsonObject, "root", "#", 0);

        JSONObject schema = JSONObject.parseObject("{}", JSONObject.class, Feature.OrderedField);
        assert root != null;
        createJSONSchema(root,schema,"/");
        System.out.println(schema.toJSONString());

        List<JSONNode> list = JSONTree.depthFirstTraversal(root);

        assert list != null;
        for (JSONNode jsonNode : list) {
            System.out.printf("%" + (jsonNode.getLevel() * 4 + 1) + "s" + "%1$s%2$s%n", " ", jsonNode.getLevel() + "--" + jsonNode.getNodePath() + "--" + jsonNode.getDataType() + jsonNode.getChildren());
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章