Jackson TreeNode浅析、封装json解析工具

1,068 阅读3分钟

TreeNode浅析

Jackson 在反序列化json字符串的时候会将其转换成一棵由JsonNode为节点组成的多节点树。 每个冒号左边的key和右边的value组成JsonNode,JsonNode是抽象类。 每一种value类型(如对象{},数组[],数字)都有其对应的实现类。

下面简单分类下各种类型

类型的枚举

public enum JsonNodeType
{
    ARRAY,
    BINARY,
    BOOLEAN,
    MISSING,
    NULL,
    NUMBER,
    OBJECT,
    POJO,
    STRING
}

通过JsonNode.getNodeType()可以获取当前node的类型

类型分类

上面的枚举可以简单的分为三种,分别使用下面三个方法判断

  • isValueNode()
  • isContainerNode()
  • isMissingNode()

叶子节点

看下isValueNode()实现,除了 ARRAY、OBJECT、MISSING都是值类型。

就相当于树的叶子节点。

class JsonNode {
    public final boolean isValueNode() {
        switch (getNodeType()) {
            case ARRAY: case OBJECT: case MISSING:
                return false;
            default:
                return true;
        }
    }
}

非叶子节点

isContainerNode() 容器节点,节点下面还包含其他的分叉。 主要包含两种类型,OBJECT、ARRAY

class JsonNode {
    public final boolean isContainerNode() {
        final JsonNodeType type = getNodeType();
        return type == JsonNodeType.OBJECT || type == JsonNodeType.ARRAY;
    }
}

遍历这两种节点

  1. ObjectNode 使用ObjectNode.fields()方法获取这个节点下面属性的列表,这个方法返回的是一个迭代器直接循环处理即可。
Iterator<Map.Entry<String, JsonNode>> iterator = node.fields();
while (iterator.hasNext()) {
    Map.Entry<String, JsonNode> entry = iterator.next();
}
  1. ArrayNode ArrayNode.size()方法获取数组长度循环处理。
for (int i = 0; i < node.size(); i++) {
    JsonNode arrChild = node.get(i);
}

封装JacksonUtil

public class JacksonUtil {
    private JacksonUtil() {
        throw new UnsupportedOperationException();
    }

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private static final DefaultPrettyPrinter PRETTY_PRINTER = new DefaultPrettyPrinter();

    static {
        //json中可以有注释
        MAPPER.enable(JsonParser.Feature.ALLOW_COMMENTS);
        //重复检测
        MAPPER.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
        //宽松的json序列化,java类中字段可以和json不完全匹配
        MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        //格式化相关
        DefaultIndenter indenter = new DefaultIndenter("  ", DefaultIndenter.SYS_LF);
        PRETTY_PRINTER.indentArraysWith(indenter);
        PRETTY_PRINTER.indentArraysWith(indenter);
    }

    /**
     * 对象 转换成json string
     */
    public static String toJson(Object obj) {
        try {
            return MAPPER.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 格式化之后的json string
     */
    public static String toPrettyJson(Object obj) {
        try {
            return MAPPER.writer(PRETTY_PRINTER).writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 反序列化json
     * @return 入参的类型泛型实例化对象
     */
    public static <T> T fromJson(String json, Class<T> valueType) {
        try {
            return MAPPER.readValue(json, valueType);
        } catch (JsonProcessingException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 反序列化json
     * @return JavaType对象的类型实例
     */
    public static <T> T fromJson(String json, JavaType javaType) {
        try {
            return MAPPER.readValue(json, javaType);
        } catch (JsonProcessingException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 反序列化json,转换成集合,集合中的元素为泛型对象
     * @return JavaType对象的类型实例
     */
    public static <E, T extends Collection<E>> T ofJsonCollection(String json,
            Class<? extends Collection> CollectionType, Class<E> itemType) {
        try {
            return MAPPER.readValue(json, MAPPER.getTypeFactory().constructCollectionType(CollectionType, itemType));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 反序列化json,转换成Map
     * @return JavaType对象的类型实例
     */
    public static <K, V, T extends Map<K, V>> T ofJsonMap(String json,
            Class<? extends Map> mapType, Class<K> keyType, Class<V> valueType) {
        try {
            return MAPPER.readValue(json, MAPPER.getTypeFactory().constructMapType(mapType, keyType, valueType));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

使用 MAPPER.getTypeFactory().constructType()MAPPER.getTypeFactory().constructMapType等方法可以扩展出多种类型的嵌套

使用Jackson处理json嵌套

json 字符串中存在多层嵌套json的情况,使用jackson将嵌套的json解开。 并且支持按照原来的嵌套层级进行反编码

package com.peng.jackson;

import static com.fasterxml.jackson.databind.node.JsonNodeType.STRING;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.tuple.Pair;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
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.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * JacksonConverter
 *
 * @author lupeng
 * Created on 2022-04-16
 */
public class JacksonConverter {

    public static void main(String[] args) throws JsonProcessingException {
        String json = """
                {
                  "arr": [
                    {
                      "name": "jack",
                      "age": 11,
                      "factor": 0.66
                    },
                    {
                      "name": "guoguo",
                      "age": 2,
                      "factor": 0.99
                    }
                  ],
                  "size": 1234567890,
                  "data": "{\"extra\":{\"firstName\":\"zhang\",\"lastName\":\"san\"}, \"arr1\":[1,2,3]}"
                }
                """.strip();
        JacksonConverter converter = new JacksonConverter();
        JsonNode decode = converter.decode(json);
        String jsonstr = MAPPER.writeValueAsString(decode);
        System.out.println(jsonstr);

        System.out.println("--------------------------");

        JsonNode encode = converter.encode(jsonstr);
        String string = MAPPER.writeValueAsString(encode);
        System.out.println(string);
    }

    private static final ObjectMapper MAPPER = new ObjectMapper();

    static {
        //json中可以有注释
        MAPPER.enable(JsonParser.Feature.ALLOW_COMMENTS);
        //重复检测
        MAPPER.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);
        //宽松的json序列化,java类中字段可以和json不完全匹配
        MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }

    private Set<String> convertedJsonNodes = new HashSet<>();

    public JsonNode decode(String json) {
        try {
            JsonNode root = MAPPER.readTree(json);
            String path = "$";
            recursiveParser(root, path, false);
            return root;
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    public JsonNode encode(String json) {
        try {
            JsonNode root = MAPPER.readTree(json);
            String path = "$";
            recursiveParser(root, path, true);
            return root;
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    private void recursiveParser(JsonNode node, String path, boolean isEncode) {
        // 获取jackson的Node节点类型
        JsonNodeType nodeType = node.getNodeType();
        switch (nodeType) {
            case OBJECT -> {
                ObjectNode objNode = (ObjectNode) node;
                Iterator<Map.Entry<String, JsonNode>> iterator = objNode.fields();
                while (iterator.hasNext()) {
                    Map.Entry<String, JsonNode> entry = iterator.next();
                    String key = entry.getKey();
                    JsonNode value = entry.getValue();
                    String currPath = path + "." + key;
                    if (isEncode && convertedJsonNodes.contains(currPath)) {
                        recursiveParser(value, currPath, true);
                        ((ObjectNode) node).put(key, value.toString());
                        continue;
                    }
                    Pair<Boolean, JsonNode> result = tryDecodeJsonNode(value);
                    if (result.getLeft()) {
                        convertedJsonNodes.add(currPath);
                        JsonNode decodeJsonNode = result.getRight();
                        objNode.put(key, decodeJsonNode);
                        value = decodeJsonNode;
                    }
                    recursiveParser(value, currPath, isEncode);
                }
            }
            case ARRAY -> {
                ArrayNode arrayNode = (ArrayNode) node;
                for (int i = 0; i < arrayNode.size(); i++) {
                    JsonNode value = arrayNode.get(i);
                    String currPath = path + "[" + i + "]";
                    if (isEncode && convertedJsonNodes.contains(currPath)) {
                        recursiveParser(value, currPath, true);
                        arrayNode.insert(i, value.toString());
                        continue;
                    }
                    Pair<Boolean, JsonNode> result = tryDecodeJsonNode(value);
                    if (result.getLeft()) {
                        convertedJsonNodes.add(currPath);
                        JsonNode decodeJsonNode = result.getRight();
                        ((ArrayNode) node).insert(i, decodeJsonNode);
                        value = decodeJsonNode;
                    }
                    recursiveParser(value, currPath, isEncode);
                }
            }
        }
    }

    private Pair<Boolean, JsonNode> tryDecodeJsonNode(JsonNode jsonNode) {
        if (jsonNode.getNodeType() != STRING) {
            return Pair.of(false, null);
        }
        try {
            JsonNode node = MAPPER.readTree(jsonNode.asText());
            return Pair.of(true, node);
        } catch (JsonProcessingException e) {
            return Pair.of(false, null);
        }
    }

}