Jackson 2.x 系列【3】解析器 JsonParser

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-seata-demo

文章目录

    • [1. 前言](#1. 前言)
    • [2. 解析原理](#2. 解析原理)
    • [3. 案例演示](#3. 案例演示)
      • [3.1 创建 JsonParser](#3.1 创建 JsonParser)
      • [3.2 解析](#3.2 解析)
      • [3.3 读取](#3.3 读取)
      • [3.4 测试](#3.4 测试)

1. 前言

在上一篇文档中,我们使用JsonGenerator生成了一个JSON文件,接下来我们学习使用JsonParser进行解析。

2. 解析原理

JsonParser进行解析的大致流程如下:

  1. 从头开始扫描JSON字符串
  2. 依次识别每个JSON组成元素
  3. 解析到结束位置,关闭解析器


JSON格式是非常标准的,JsonParser会从头到尾识别每一个组成元素,比如识别到对象开始符号{时,会生成一个对应JsonToken令牌对象,其中包含了元素标识START_OBJECT

JsonToken对象是一个枚举类,用于判断解析元素的类型:

java 复制代码
public enum JsonToken {
	// 当前无法返回
    NOT_AVAILABLE((String)null, -1),
	// 对象开始符号:{
    START_OBJECT("{", 1),
    // 对象结束符号:}
    END_OBJECT("}", 2),
    // 数组开始符号
    START_ARRAY("[", 3),
    // 数组结束符号
    END_ARRAY("]", 4),
    // 当时是属性名称
    FIELD_NAME((String)null, 5),
    // 嵌入对象
    VALUE_EMBEDDED_OBJECT((String)null, 12),
    // 字符串类型值
    VALUE_STRING((String)null, 6),
    // INT类型值
    VALUE_NUMBER_INT((String)null, 7),
    // FLOAT类型值
    VALUE_NUMBER_FLOAT((String)null, 8),
    // True 类型值
    VALUE_TRUE("true", 9),
    // False 类型值
    VALUE_FALSE("false", 10),
    // null 值
    VALUE_NULL("null", 11);
}

3. 案例演示

演示需求: 将之前生成的JSON文件反序列化为User对象。

3.1 创建 JsonParser

JsonParser是读取JSON内容API的基类,它有很多实现子类:

其实例也是由JsonFactory创建,JsonFactory提供了多种创建方法:

java 复制代码
    public abstract JsonParser createParser(byte[] data) throws IOException;
    public abstract JsonParser createParser(byte[] data, int offset, int len) throws IOException;
    public abstract JsonParser createParser(char[] content) throws IOException;
    public abstract JsonParser createParser(char[] content, int offset, int len) throws IOException;
    public abstract JsonParser createParser(DataInput in) throws IOException;
    public abstract JsonParser createParser(File f) throws IOException;
    public abstract JsonParser createParser(InputStream in) throws IOException;
    public abstract JsonParser createParser(Reader r) throws IOException;
    public abstract JsonParser createParser(String content) throws IOException;
    public abstract JsonParser createParser(URL url) throws IOException;

这里我们直接通过文件对象创建:

java 复制代码
        // 1. 创建 JsonParser
        JsonFactory jsonFactory = JsonFactory.builder().build();
        File file = new File("E:\\TD\\pearl\\study-jackson-demo\\jackson-core-demo\\src\\main\\java\\com\\pearl\\jacksoncore\\demo\\file\\user.json");
        JsonParser jsonParser = jsonFactory.createParser(file);

上面创建的JsonParser 实例类型是UTF8StreamJsonParser

3.2 解析

JsonParser提供了isClosed()方法判断是否关闭,nextToken()方法解析下一个元素,使用这两个方法循环解析JSON的所有元素:

java 复制代码
        while (!jsonParser.isClosed()) {
            JsonToken jsonToken = jsonParser.nextToken();
            System.out.println("当前解析到的令牌类型:" + jsonToken);
        }

所有元素类型如下:

java 复制代码
当前解析到的令牌类型:START_OBJECT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_STRING
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:START_OBJECT
当前解析到的令牌类型:FIELD_NAME
当前解析到的令牌类型:VALUE_NUMBER_INT

3.3 读取

JsonParser提供了多个将JSON解析为对象或树模型的方法:

查看readValueAs方法,可以看到实际是调用ObjectCodec对象的读取方法:

java 复制代码
    public <T> T readValueAs(Class<T> valueType) throws IOException {
        return this._codec().readValue(this, valueType);
    }
    protected ObjectCodec _codec() {
        ObjectCodec c = this.getCodec();
        if (c == null) {
            throw new IllegalStateException("No ObjectCodec defined for parser, needed for deserialization");
        } else {
            return c;
        }
    }

ObjectCodec是一个抽象类,定义了JsonParserJsonGenerator用于序列化和反序列化POJO对象的正则表达式,其标准实现为jackson-databind模块中的ObjectMapper类。ObjectCodec是非常重要的一个类,JSONPOJO转换的规则和逻辑需要实现该抽象类。

ObjectCodec中可以看到定义了很多读写方法,同时它继承了TreeCodec抽象类,说明也具备读取为树模型的能力:

java 复制代码
public abstract class ObjectCodec extends TreeCodec implements Versioned {
 
    public abstract Version version();

    public abstract <T> T readValue(JsonParser var1, Class<T> var2) throws IOException;

    public abstract <T> T readValue(JsonParser var1, TypeReference<T> var2) throws IOException;

    public abstract <T> T readValue(JsonParser var1, ResolvedType var2) throws IOException;

    public abstract <T> Iterator<T> readValues(JsonParser var1, Class<T> var2) throws IOException;

    public abstract <T> Iterator<T> readValues(JsonParser var1, TypeReference<T> var2) throws IOException;

    public abstract <T> Iterator<T> readValues(JsonParser var1, ResolvedType var2) throws IOException;

    public abstract void writeValue(JsonGenerator var1, Object var2) throws IOException;

    public abstract <T extends TreeNode> T readTree(JsonParser var1) throws IOException;

    public abstract void writeTree(JsonGenerator var1, TreeNode var2) throws IOException;

    public abstract TreeNode createObjectNode();

    public abstract TreeNode createArrayNode();

    public abstract JsonParser treeAsTokens(TreeNode var1);

    public abstract <T> T treeToValue(TreeNode var1, Class<T> var2) throws JsonProcessingException;

    /** @deprecated */
    @Deprecated
    public JsonFactory getJsonFactory() {
        return this.getFactory();
    }

    public JsonFactory getFactory() {
        return this.getJsonFactory();
    }
}

我们自定义一个StudyObjectCodec,这里只实现readValue(JsonParser jsonParser, Class<T> aClass)方法,也只解析了User的几个属性,因为解析比生成复杂的多,这里只是简单演示:

java 复制代码
public class StudyObjectCodec extends ObjectCodec {
 	
 	// 省略其他实现
 
    @Override
    public <T> T readValue(JsonParser jsonParser, Class<T> aClass) throws IOException {
        // 1. 创建用户对象
        User user = new User();
        // 2. 循环解析每一个元素
        while (!jsonParser.isClosed()) {
            // 解析到下一个元素
            JsonToken jsonToken = jsonParser.nextToken();
            System.out.println("当前解析到的令牌类型:" + jsonToken);
            // 2.1 如果是属性名称类型
            if (JsonToken.FIELD_NAME.equals(jsonToken)) {
                String currentName = jsonParser.currentName(); // 获取属性名
                System.out.println("属性名称:" + currentName);
                if ("id".equals(currentName) ) {
                    jsonParser.nextToken(); // 解析到下一个元素,即为属性对应的值
                    Long userId = jsonParser.getLongValue();
                    user.setId(userId);
                }

                if ("name".equals(currentName)) {
                    jsonParser.nextToken();
                    String userName = jsonParser.getValueAsString();
                    user.setName(userName);
                }
                if ("age".equals(currentName)) {
                    jsonParser.nextToken();
                    int age = jsonParser.getIntValue();
                    user.setAge(age);
                }
            }
        }
        // 3. 返回
        return (T) user;
    }

最后设置ObjectCodec,完整代码如下:

java 复制代码
public class JsonParserDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建 JsonParser
        JsonFactory jsonFactory = JsonFactory.builder().build();
        File file = new File("E:\\TD\\pearl\\study-jackson-demo\\jackson-core-demo\\src\\main\\java\\com\\pearl\\jacksoncore\\demo\\file\\user.json");
        JsonParser jsonParser = jsonFactory.createParser(file);
        // 2. 反序列化 
        jsonParser.setCodec(new StudyObjectCodec());
        User user = jsonParser.readValueAs(User.class);
        System.out.println(user);
    }
}

3.4 测试

运行测试案例,查看控制台:

java 复制代码
User{id=1701893746586685440, name='坤坤', age=18, org=null, roleList=null}
相关推荐
qq_三哥啊2 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis
别惹CC3 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
人工智能·spring boot·spring
寒士obj3 小时前
Spring事物
java·spring
IT毕设实战小研11 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋13 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员16 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
甄超锋17 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠18 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven
还是大剑师兰特19 小时前
Spring面试题及详细答案 125道(1-15) -- 核心概念与基础1
spring·大剑师·spring面试题·spring教程
python_13621 小时前
web请求和响应
java·spring·github