Java 中动态 JSON 对象的使用

最主流的 JSON 处理库是 fasterxml 的Jaskson和 google 的Gson等。(其他的还有比如com.alibaba:fastjsonnet.sf.json-lib:json-lib等)

在已有实体类结构的情况下,现有的 JSON 库可以轻松地把 JSON 文本反序列为实体类。

// Jackson 序列化
com.fasterxml.jackson.databind.ObjectMapper#writeValueAsString
// Jackson 反序列化
com.fasterxml.jackson.databind.ObjectMapper#readValue(java.lang.String, java.lang.Class<T>)
// Gson 序列化
com.google.gson.Gson#toJson(java.lang.Object)
// Gson 反序列化
com.google.gson.Gson#fromJson(java.lang.String, java.lang.Class<T>)

但是如果未知实体类的结构,那么一般操作方式就是直接把 JSON 文本转成java.util.Map,或者JSON 库本身的JSON对象模型

一、Jackson

1. 基本模型

Jaskson的动态JSON对象模型如下图所示:

顶层是TreeNodeJsonNode,其可直接使用的子类主要分为两种,分别是ContainerNodeValueNode

  • 其中ValueNode的众多子类是 JSON 中的各种值类型,例如某个key的value是一个字符串(TextNode)、或者整型(IntNode)、或者null(NullNode)。或者一个单独的值也是符合 JSON 结构规范的。
  • ContainerNode则是最常见的 JSON 结构了,主要分为ObjectNodeArrayNode,分别对应两个顶层结构:花括号({})与方括号([])。

使用方法:

  • 使用com.fasterxml.jackson.databind.ObjectMapper#readTree(java.lang.String)方法把 JSON 文本解析成JsonNode对象。当然这个方法还有其他重载可以接受不同参数,此处不再赘述。
  • 使用com.fasterxml.jackson.databind.node.JsonNodeFactory#*Node方法可以创建各种 Node 对象。ArrayNode/ObjectNode/ObjectMapper 也可以创建各种 Node 对象,是通过调用其内部的JsonNodeFactory实例完成的。

2. 示例

以如下的 JSON 文本举例:

[
    {
        "name": "zhangsan",
        "age": 24
    },
    {
        "name": "lisi",
        "age": 30
    }
]

先在项目的Gradle文件中导入依赖:

implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0-rc2'

引入jackson-databind即可,jackson-databind会自己引入对应的jackson-corejackson-annotations

然后直接上代码片段:

import com.fasterxml.jackson.core.JsonProcessingException;
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.IntNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;

public class JacksonTest {

    /**
     * <pre> json:
     * [
     *     {
     *         "name": "zhangsan",
     *         "age": 24
     *     },
     *     {
     *         "name": "lisi",
     *         "age": 30
     *     }
     * ]
     * </pre>
     */
    private static final String json = "[{\"name\": \"zhangsan\",\"age\": 24},{\"name\": \"lisi\",\"age\": 30}]";

    private static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) {
        JsonNode jsonNode = null;
        try {
            // 把 JSON 文本解析成 JsonNode 对象
            jsonNode = objectMapper.readTree(json);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        // 该 JSON 文本最顶层的 JSON 模型是一个 ArrayNode
        ArrayNode arrayNode = (ArrayNode) jsonNode;
        // ArrayNode 的第一项是一个 ObjectNode
        ObjectNode objectNode = (ObjectNode) arrayNode.get(0);
        // 该 objectNode 中 key 为 "name" 的值
        TextNode name = (TextNode) objectNode.get("name");
        // 该 objectNode 中 key 为 "age" 的值
        IntNode age = (IntNode) objectNode.get("age");

        // 往 ObjectNode 中添加键值对,或者修改 value
        objectNode.put("city", "beijing");
        objectNode.put("age", 25);

        // 往 ArrayNode 中删除一项,或者添加一项
        arrayNode.remove(1);
        arrayNode.add("a new item");
        // JsonNodeFactory.instance.*Node 方法可以创建各种 Node 对象
        arrayNode.add(JsonNodeFactory.instance.nullNode());

        // 直接 print 可以打印 JSON 文本
        System.out.println(jsonNode);

        // ArrayNode/ObjectNode/ObjectMapper 也可以创建各种 Node 对象
        TextNode textNode1 = arrayNode.textNode("test");
        ArrayNode arrayNode1 = objectMapper.createArrayNode();
        System.out.println(textNode1);
    }
}

参考:
https://www.baeldung.com/jackson-mapping-dynamic-object
https://www.baeldung.com/jackson-json-node-tree-model

二、Gson

1. 基本模型

相比JacksonGson的动态JSON对象模型就比较简洁:

  • 顶层是JsonElement,其可直接使用的子类主要分为四种:JsonObjectJsonArrayJsonNullJsonPrimitive。前三个可以从字面理解,JsonPrimitive则是各种基本值类型,大概类似于 Jackson 的ValueNode除去NullNode

注意gsoncom.google.gson.JsonObjectjson-libnet.sf.json.JSONObjectfastjsoncom.alibaba.fastjson.JSONObject 区分一下,不要用混了。

使用方法:

  • 使用com.google.gson.JsonParser#parseString方法把 JSON 文本解析成JsonElement对象。当然这个方法还有其他类似的比如parseReader,此处不再赘述。
  • JsonElement的各个子类都可以直接new出来,其中JsonNull则推荐使用JsonNull.INSTANCE

2. 示例

依旧以上面的 JSON 文本举例,先在项目的Gradle文件中导入依赖:

implementation 'com.google.code.gson:gson:2.8.6'

然后直接上代码片段:

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

public class GsonTest {

    /**
     * <pre> json:
     * [
     *     {
     *         "name": "zhangsan",
     *         "age": 24
     *     },
     *     {
     *         "name": "lisi",
     *         "age": 30
     *     }
     * ]
     * </pre>
     */
    private static final String json = "[{\"name\": \"zhangsan\",\"age\": 24},{\"name\": \"lisi\",\"age\": 30}]";

    public static void main(String[] args) {
        // 把 JSON 文本解析成 JsonElement 对象
        JsonElement jsonElement = JsonParser.parseString(json);
        // 该 JSON 文本最顶层的 JSON 模型是一个 JsonArray
        JsonArray jsonArray = (JsonArray) jsonElement;
        // JsonArray 的第一项是一个 JsonObject
        JsonObject jsonObject = (JsonObject) jsonArray.get(0);
        // 该 JsonObject 中 key 为 "name" 的值
        JsonPrimitive name = (JsonPrimitive) jsonObject.get("name");
        // 该 JsonObject 中 key 为 "age" 的值
        JsonPrimitive age = (JsonPrimitive) jsonObject.get("age");

        // 往 JsonObject 中添加键值对,或者修改 value
        jsonObject.addProperty("city", "beijing");
        jsonObject.addProperty("age", 25);

        // 往 JsonArray 中删除一项,或者添加一项
        jsonArray.remove(1);
        jsonArray.add("first new item");

        // 各种 Json 模型对象可以直接 new 出来,null 则推荐 JsonNull.INSTANCE
        jsonArray.add(new JsonPrimitive("second new item"));

        // 直接 print 可以打印 JSON 文本
        System.out.println(jsonElement);
    }
}

三、总结

介绍了JasksonGson的动态JSON对象的模型与基本使用方式,至于这两个 JSON 库基础的序列化与反序列化、或者动态JSON对象更进一步的使用方式 可以自己读一下二者的文档(Gson Jackson)。个人虽然平时用Jackson比较多,但是感觉Gson更加简洁。

TODO 有空更一篇 JsonPath 的介绍。

posted @ 2022-05-03 21:07  Recycer  阅读(1871)  评论(0编辑  收藏  举报