奇葩需求,将json字符串转换映射到某种父子结构id,pid的树形结构数组存入数据库

前言

最近遇到一个棘手的问题,需要将大几十个固定格式的 json 数据(可以理解为echarts那样的,每个json之间格式不是很固定,但是最终的字段的值需要是业务的实际数值),将几十个接口工作量缩减为几个类型的值的处理是比较好的处理方式了。时间紧任务重,真的写大几十个接口,那不知道要写猴年马月去了,于是就将json转换为每个节点用 code、pcode标识的一个json树数组。对应的节点再标识一下处理的逻辑,将值替换为实际业务数值给返回去,这个且按下不谈,本文就是浅谈一下将json字符串转为json树数组的一个过程
例子:
原版的json如下:

{
    "code": 1,
    "pcode": {
        "a": 123123
    },
    "name": "code",
    "type": [{
        "a": 1
    }, {
        "b": 2
    }],
    "value": "200"
}

转换为存入数据库的格式如下:

[
    {"code":0,"pcode":null,"name":null,"value":"","type":2},
    {"code":1,"pcode":0,"name":"code","value":"1","type":4},
    {"code":1,"pcode":0,"name":"pcode","value":"","type":2},
    {"code":2,"pcode":1,"name":"a","value":"123123","type":4},
    {"code":1,"pcode":0,"name":"name","value":"code","type":3},
    {"code":1,"pcode":0,"name":"type","value":"","type":1},
    {"code":3,"pcode":1,"name":"a","value":"1","type":4},
    {"code":4,"pcode":1,"name":"b","value":"2","type":4},
    {"code":1,"pcode":0,"name":"value","value":"200","type":3}
]

1.json转换成json树数组的原则

其实上述的 code 和 pcode 表示是参考 JSON 的一种配置格式—— TOML格式配置
将它的表头和键名定义为 pcode和code,键值类型使用专门的 type字段标识,键值以字符串放入value
具体的可以访问网址:https://toml.io/cn/v1.0.0

目前该 json 树数组的表示只有三个遵循的原则
1.同级的节点其 code 和 pcode 的值相同
2.从上到下每遇到一个新的 Object 或 Array 其子节点都会一个唯一的新值
3. 使用 type 标明该属性是基本类型还是对象数组类型(1Array 2Object 3字符串 4数值 5布尔)

2.对应的转换和还原逻辑

完整代码如下:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

public class TestJsonCoverToJsonTree {

    // jsonTree 还原为 json
    // json转换为jsonTree
    public static void main(String[] args) throws IOException {
        String json = "{\"code\":1,\"pcode\":{\"a\":123123},\"name\":\"code\",\"type\":[{\"a\":1},{\"b\":2}],\"value\":\"200\"}";

        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(json);

        List<NodeInfo> list = new ArrayList<>();
        int startId = 1;
        NodeInfo rootVo = new NodeInfo();
        rootVo.setCode(0);
        rootVo.setType(coverJsonNodeType(jsonNode.getNodeType()));
        list.add(rootVo);
        getList(jsonNode, startId, 0, list);

        System.out.println(objectMapper.writeValueAsString(list));
    }

    public static void main2(String[] args) throws JsonProcessingException {
        String json = String json = "[{\"code\":0,\"pcode\":null,\"name\":null,\"value\":null,\"type\":2},{\"code\":1,\"pcode\":0,\"name\":\"code\",\"value\":\"1\",\"type\":4},{\"code\":1,\"pcode\":0,\"name\":\"pcode\",\"value\":\"\",\"type\":2},{\"code\":2,\"pcode\":1,\"name\":\"a\",\"value\":\"123123\",\"type\":4},{\"code\":1,\"pcode\":0,\"name\":\"name\",\"value\":\"code\",\"type\":3},{\"code\":1,\"pcode\":0,\"name\":\"type\",\"value\":\"\",\"type\":1},{\"code\":3,\"pcode\":1,\"name\":\"a\",\"value\":\"1\",\"type\":4},{\"code\":4,\"pcode\":1,\"name\":\"b\",\"value\":\"2\",\"type\":4},{\"code\":1,\"pcode\":0,\"name\":\"value\",\"value\":\"200\",\"type\":3}]";
        ObjectMapper objectMapper = new ObjectMapper();
        List<NodeInfo> list = objectMapper.readValue(json, new TypeReference<List<NodeInfo>>() {});

        String jsonResult = getJsonTreeString(list);
        System.out.println(jsonResult);

    }

    /**
     * 获取子节点
     * 开始
     */
    private static void getList(JsonNode node, Integer id, Integer pid, List<NodeInfo> list) {
        if (node.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> next = fields.next();
                JsonNode jsonNode = next.getValue();
                NodeInfo nodeInfo = new NodeInfo();
                nodeInfo.setCode(id);
                nodeInfo.setPcode(pid);
                nodeInfo.setName(next.getKey());
                nodeInfo.setValue(jsonNode.asText());
                nodeInfo.setType(coverJsonNodeType(jsonNode.getNodeType()));
                list.add(nodeInfo);
                if (jsonNode.isArray() || jsonNode.isObject()) {
                    // 从上到下每遇到一个新的 Object 或 Array 其子节点都会一个 唯一的新值
                    OptionalInt max = list.stream().mapToInt(v -> v.getCode()).max();
                    getList(jsonNode, max.getAsInt() + 1, id, list);
                }
            }
        } else if (node.isArray()) {
            Iterator<JsonNode> elements = node.elements();
            while (elements.hasNext()) {
                JsonNode next = elements.next();
                OptionalInt max = list.stream().mapToInt(v -> v.getCode()).max();
                getList(next, max.getAsInt() + 1, pid, list);
            }
        }
    }

    @Data
    public static class NodeInfo {
        /** code编码 */
        private Integer code;
        /** 父级code */
        private Integer pcode;
        /** json的key */
        private String name;
        /** 值 */
        private String value;
        /** 数据类型: 1array、2object、3字符串、4数值、5布尔 */
        private Integer type;

    }

    private static int coverJsonNodeType(JsonNodeType type) {
        switch (type) {
            case ARRAY:
                return 1;
            case OBJECT:
                return 2;
            case NUMBER:
                return 4;
            case BOOLEAN:
                return 5;
            default:
                // 返回字符串
                return 3;
        }
    }

    /**
     * 获取结果字符串
     *
     * @param data 包含顺序的json节点结果集合
     * @return
     */
    public static String getJsonTreeString(List<NodeInfo> data) {
        if (data == null || data.isEmpty()) {
            return null;
        }

        Hashtable<Integer, Stack<JsonNode>> stackTable = new Hashtable<>();
        Iterator<NodeInfo> voIterator = data.iterator();

        JsonNodeFactory factory = JsonNodeFactory.instance;
        NodeInfo rootVo = voIterator.next();
        if (rootVo.getType().equals(1)) {
            ArrayNode rootNode = factory.arrayNode();
            Stack<JsonNode> nodeStack = new Stack<>();
            nodeStack.push(rootNode);
            stackTable.put(0, nodeStack);
        } else if (rootVo.getType().equals(2)) {
            ObjectNode rootNode = factory.objectNode();
            Stack<JsonNode> nodeStack = new Stack<>();
            nodeStack.push(rootNode);
            stackTable.put(0, nodeStack);
        }

        // 顺序依次遍历,只走一遍,该list必须是json树的构建顺序
        // 需要判断pcode 如果父节点是 object 的,需要根据当前节点类型考虑、如果父节点是 arr类型就 下一个
        while (voIterator.hasNext()) {
            NodeInfo vo = voIterator.next();
            Integer code = vo.getCode();
            Integer pcode = vo.getPcode();
            // 去获取当前code节点的栈
            Stack<JsonNode> arrOrObjStack = stackTable.getOrDefault(code, new Stack<>());
            boolean ifNullStack = arrOrObjStack.isEmpty();

            JsonNode valueNode = cover2JsonNode(factory, vo.getType(), vo.getValue());
            // 去获取父节点的nodel,父节点必须有
            Stack<JsonNode> pStack = stackTable.get(pcode);
            JsonNode pNode = pStack.peek();

            if (pNode.isArray()) {
                if (ifNullStack) {
                    JsonNode jsonNode = null;
                    if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                        ObjectNode objectNode = factory.objectNode();
                        objectNode.set(vo.getName(), valueNode);
                        jsonNode = objectNode;
                    } else {
                        jsonNode = valueNode;
                    }
                    ((ArrayNode) pNode).add(jsonNode);
                    arrOrObjStack = new Stack<>();
                    arrOrObjStack.push(jsonNode);
                    stackTable.put(code, arrOrObjStack);
                } else {
                    // 节点之前有
                    if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                        JsonNode jsonNode = arrOrObjStack.peek();
                        if (jsonNode.isArray()) {
                            // do nothing
                        } else if (jsonNode.isObject()) {
                            ((ObjectNode) jsonNode).set(vo.getName(), valueNode);
                        }
                    } else {
                        // 集合节点放在栈顶
                        JsonNode node = arrOrObjStack.peek();
                        if (node.isArray()) {
                            ((ArrayNode) node).add(valueNode);
                        } else if (node.isObject()) {
                            ((ObjectNode) node).set(vo.getName(), valueNode);
                        }
                        arrOrObjStack.push(valueNode);
                    }
                }

            } else if (pNode.isObject()) {

                if (ifNullStack) {
                    ((ObjectNode) pNode).set(vo.getName(), valueNode);
                    // 对于是 arr或者 obj 的 需要放入栈顶
                    if (vo.getType().equals(1) || vo.getType().equals(2)) {
                        arrOrObjStack = new Stack<>();
                        arrOrObjStack.push(valueNode);
                        stackTable.put(code, arrOrObjStack);
                    }
                } else {
                    // 节点之前有
                    if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                        JsonNode jsonNode = arrOrObjStack.peek();
                        if (jsonNode.isArray()) {
                            // do nothing
                        } else if (jsonNode.isObject()) {
                            ((ObjectNode) jsonNode).set(vo.getName(), valueNode);
                        }
                    } else {
                        // 集合节点放在栈顶
                        ((ObjectNode) pNode).set(vo.getName(), valueNode);
                        arrOrObjStack.push(valueNode);
                    }
                }
            }
        }

        Stack<JsonNode> rootStatic = stackTable.get(0);
        JsonNode rootNode = rootStatic.peek();
        return rootNode.toString();
    }

    private static JsonNode cover2JsonNode(JsonNodeFactory factory, Integer type, String value) {
        switch (type) {
            case 1:
                ArrayNode arrayNode = factory.arrayNode();
                return arrayNode;
            case 2:
                return factory.objectNode();
            case 3: // 字符串
                return factory.textNode(value);
            case 4: // 数值
                if (NumberUtils.isCreatable(value)) {
                    Number number = NumberUtils.createNumber(value);
                    if (number instanceof Integer) {
                        return factory.numberNode((Integer) number);
                    } else if (number instanceof Short) {
                        return factory.numberNode((Short) number);
                    } else if (number instanceof Float) {
                        return factory.numberNode((Float) number);
                    } else if (number instanceof Double) {
                        return factory.numberNode((Double) number);
                    } else if (number instanceof Long) {
                        return factory.numberNode((Long) number);
                    } else if (number instanceof BigInteger) {
                        return factory.numberNode((BigInteger) number);
                    } else if (number instanceof BigDecimal) {
                        return factory.numberNode((BigDecimal) number);
                    } else {
                        return factory.numberNode(0);
                    }
                } else {
                    return factory.numberNode(0);
                }
            case 5: // 布尔
                Boolean aBoolean = BooleanUtils.toBooleanObject(value);
                return factory.booleanNode(aBoolean);
            default:
                return factory.nullNode();
        }
    }

}

总结:

这里的我主要讲一下转为树结构和将树结构还原的关键代码,方法中使用了 fastxml 的 JsonNode 来遍历 json 和还原时填充 json
遍历json主要是如下代码:

if (node.isObject()) {
    Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
    while (fields.hasNext()) {
        Map.Entry<String, JsonNode> next = fields.next();
        JsonNode jsonNode = next.getValue();
        // next.getKey() ——> key
        // jsonNode.asText() ——> value
        // jsonNode.getNodeType() ——> 得到JsonNodeType
    }
} else if (node.isArray()) {
    Iterator<JsonNode> elements = node.elements();
    while (elements.hasNext()) {
        JsonNode next = elements.next();
        // next 是 [] 数组的每个节点
    }
}

还原 json 也主要是利用 fastxml 的 JsonNode来填充 json 的节点内容,ObjectNode 和 ArrayNode 都是 JsonNode 类的扩展,不同的是 JsonNode 是只读的,而 ObjectNode 和 ArrayNode 是可以修改的,使用 JsonNodeFactory 去创建对应类型的 JsonNode。

JsonNodeFactory factory = JsonNodeFactory.instance();
ValueNode integerNode = factory.numberNode( 1);
ValueNode doubleNode = factory.numberNode(1D);
BooleanNode booleanNode = factory.booleanNode(false);
TextNode textNode = factory.textNode("");

ArrayNode arrayNode = factory.arrayNode();
arrayNode.add(textNode);
arrayNode.add(1);
arrayNode.add("1");

ObjectNode objectNode = factory.objectNode();
objectNode.set("key", textNode);

将树形结构的顺序数组还原为 json 字符串,主要是如下的方法,其中依据前面说的“遵循的三个原则”,这儿使用了同级节点使用栈进行存储节点类型是对象和数组的节点,在开始构造前,确保栈中有一个根节点,所有的操作都是基于这个根节点去依次构造填充。也就是进入循环前先将根节点放入code=0的 JsonNode 栈,由于子对象被指定给父对象的对象引用,因此在完成遍历后,根节点拥有整个 json 的全部信息,返回这个最终的 json 既可

/**
  * 获取结果字符串
  *
  * @param data 包含顺序的json节点结果集合
  * @return
  */
 public static String getJsonTreeString(List<NodeInfo> data) {
     if (data == null || data.isEmpty()) {
         return null;
     }

     Hashtable<Integer, Stack<JsonNode>> stackTable = new Hashtable<>();
     Iterator<NodeInfo> voIterator = data.iterator();

     JsonNodeFactory factory = JsonNodeFactory.instance;
     NodeInfo rootVo = voIterator.next();
     if (rootVo.getType().equals(1)) {
         ArrayNode rootNode = factory.arrayNode();
         Stack<JsonNode> nodeStack = new Stack<>();
         nodeStack.push(rootNode);
         stackTable.put(0, nodeStack);
     } else if (rootVo.getType().equals(2)) {
         ObjectNode rootNode = factory.objectNode();
         Stack<JsonNode> nodeStack = new Stack<>();
         nodeStack.push(rootNode);
         stackTable.put(0, nodeStack);
     }

     // 顺序依次遍历,只走一遍,该list必须是json树的构建顺序
     // 需要判断pcode 如果父节点是 object 的,需要根据当前节点类型考虑、如果父节点是 arr类型就 下一个
     while (voIterator.hasNext()) {
         NodeInfo vo = voIterator.next();
         Integer code = vo.getCode();
         Integer pcode = vo.getPcode();
         // 去获取当前code节点的栈
         Stack<JsonNode> arrOrObjStack = stackTable.getOrDefault(code, new Stack<>());
         boolean ifNullStack = arrOrObjStack.isEmpty();

         JsonNode valueNode = cover2JsonNode(factory, vo.getType(), vo.getValue());
         // 去获取父节点的nodel,父节点必须有
         Stack<JsonNode> pStack = stackTable.get(pcode);
         JsonNode pNode = pStack.peek();

         if (pNode.isArray()) {
             if (ifNullStack) {
                 JsonNode jsonNode = null;
                 if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                     ObjectNode objectNode = factory.objectNode();
                     objectNode.set(vo.getName(), valueNode);
                     jsonNode = objectNode;
                 } else {
                     jsonNode = valueNode;
                 }
                 ((ArrayNode) pNode).add(jsonNode);
                 arrOrObjStack = new Stack<>();
                 arrOrObjStack.push(jsonNode);
                 stackTable.put(code, arrOrObjStack);
             } else {
                 // 节点之前有
                 if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                     JsonNode jsonNode = arrOrObjStack.peek();
                     if (jsonNode.isArray()) {
                         // do nothing
                     } else if (jsonNode.isObject()) {
                         ((ObjectNode) jsonNode).set(vo.getName(), valueNode);
                     }
                 } else {
                     // 集合节点放在栈顶
                     JsonNode node = arrOrObjStack.peek();
                     if (node.isArray()) {
                         ((ArrayNode) node).add(valueNode);
                     } else if (node.isObject()) {
                         ((ObjectNode) node).set(vo.getName(), valueNode);
                     }
                     arrOrObjStack.push(valueNode);
                 }
             }

         } else if (pNode.isObject()) {

             if (ifNullStack) {
                 ((ObjectNode) pNode).set(vo.getName(), valueNode);
                 // 对于是 arr或者 obj 的 需要放入栈顶
                 if (vo.getType().equals(1) || vo.getType().equals(2)) {
                     arrOrObjStack = new Stack<>();
                     arrOrObjStack.push(valueNode);
                     stackTable.put(code, arrOrObjStack);
                 }
             } else {
                 // 节点之前有
                 if (vo.getType().equals(3) || vo.getType().equals(4) || vo.getType().equals(5)) {
                     JsonNode jsonNode = arrOrObjStack.peek();
                     if (jsonNode.isArray()) {
                         // do nothing
                     } else if (jsonNode.isObject()) {
                         ((ObjectNode) jsonNode).set(vo.getName(), valueNode);
                     }
                 } else {
                     // 集合节点放在栈顶
                     ((ObjectNode) pNode).set(vo.getName(), valueNode);
                     arrOrObjStack.push(valueNode);
                 }
             }
         }
     }

     Stack<JsonNode> rootStatic = stackTable.get(0);
     JsonNode rootNode = rootStatic.peek();
     return rootNode.toString();
 }

以上就是全部内容了,祝大家玩的愉快~~!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值