4. JSON字符串是如何被解析的?JsonParser了解一下(上)

简介: 4. JSON字符串是如何被解析的?JsonParser了解一下(上)

版本约定


  • Jackson版本:2.11.0
  • Spring Framework版本:5.2.6.RELEASE
  • Spring Boot版本:2.3.0.RELEASE


小贴士:截止到本文,本系列前面所有示例都只仅仅导入jackson-core而已,后续若要新增jar包我会额外说明,否则相同


正文


什么叫读JSON?就是把一个JSON 字符串 解析为对象or树模型嘛,因此也称作解析JSON串。Jackson底层流式API使用JsonParser来完成JSON字符串的解析。


最简使用Demo


准备一个POJO:

@Data
public class Person {
    private String name;
    private Integer age;
}

测试用例:把一个JSON字符串绑定(封装)进一个POJO对象里

@Test
public void test1() throws IOException {
    String jsonStr = "{\"name\":\"YourBatman\",\"age\":18}";
    Person person = new Person();
    JsonFactory factory = new JsonFactory();
    try (JsonParser jsonParser = factory.createParser(jsonStr)) {
        // 只要还没结束"}",就一直读
        while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
            String fieldname = jsonParser.getCurrentName();
            if ("name".equals(fieldname)) {
                jsonParser.nextToken();
                person.setName(jsonParser.getText());
            } else if ("age".equals(fieldname)) {
                jsonParser.nextToken();
                person.setAge(jsonParser.getIntValue());
            }
        }
        System.out.println(person);
    }
}


运行程序,输出:

Person(name=YourBatman, age=18)



成功把一个JSON字符串的值解析到Person对象。你可能会疑问,怎么这么麻烦?那当然,这是底层流式API,纯手动档嘛。你获得了性能,可不要失去一些便捷性嘛。


小贴士:底层流式API一般面向“专业人士”,应用级开发使用高阶API ObjectMapper即可。当然,读完本系列就能让你完全具备“专业人士”的实力😄


JsonParser针对不同的value类型,提供了非常多的方法用于实际值的获取。


直接值获取:

// 获取字符串类型
public abstract String getText() throws IOException;
// 数字Number类型值 标量值(支持的Number类型参照NumberType枚举)
public abstract Number getNumberValue() throws IOException;
public enum NumberType {
    INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL
};
public abstract int getIntValue() throws IOException;
public abstract long getLongValue() throws IOException;
...
public abstract byte[] getBinaryValue(Base64Variant bv) throws IOException;


这类方法可能会抛出异常:比如value值本不是数字但你调用了getInValue()方法~


小贴士:如果value值是null,像getIntValue()、getBooleanValue()等这种直接获取方法是会抛出异常的,但getText()不会


带默认值的值获取,具有更好安全性:


public String getValueAsString() throws IOException {
    return getValueAsString(null);
}
public abstract String getValueAsString(String def) throws IOException;
...
public long getValueAsLong() throws IOException {
    return getValueAsLong(0);
}
public abstract long getValueAsLong(long def) throws IOException;
...


此类方法若碰到数据的转换失败时,不会抛出异常,把def作为默认值返回。


组合方法


JsonGenerator一样,JsonParser也提供了高钙片组合方法,让你更加便捷的使用。


image.png

自动绑定

听起来像高级功能,是的,它必须依赖于ObjectCodec去实现,因为实际是全部委托给了它去完成的,也就是我们最为熟悉的readXXX系列方法:


image.png


我们知道,ObjectMapper就是一个ObjectCodec,它属于高级API,本文显然不会用到ObjectMapper它喽,因此我们自己手敲一个实现来完成此功能。


自定义一个ObjectCodec,Person类专用:用于把JSON串自动绑定到实例属性。

public class PersonObjectCodec extends ObjectCodec {
  ...
    @SneakyThrows
    @Override
    public <T> T readValue(JsonParser jsonParser, Class<T> valueType) throws IOException {
        Person person = (Person) valueType.newInstance();
        // 只要还没结束"}",就一直读
        while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
            String fieldname = jsonParser.getCurrentName();
            if ("name".equals(fieldname)) {
                jsonParser.nextToken();
                person.setName(jsonParser.getText());
            } else if ("age".equals(fieldname)) {
                jsonParser.nextToken();
                person.setAge(jsonParser.getIntValue());
            }
        }
        return (T) person;
    }
  ...
}


有了它,就可以实现我们的自动绑定了,书写测试用例:

@Test
public void test3() throws IOException {
    String jsonStr = "{\"name\":\"YourBatman\",\"age\":18, \"pickName\":null}";
    JsonFactory factory = new JsonFactory();
    try (JsonParser jsonParser = factory.createParser(jsonStr)) {
        jsonParser.setCodec(new PersonObjectCodec());
        System.out.println(jsonParser.readValueAs(Person.class));
    }
}


运行程序,输出:

Person(name=YourBatman, age=18)



这就是ObjectMapper自动绑定的核心原理所在,其它更为强大能力将在后续章节详细展开。


JsonToken


在上例解析过程中,有一个非常重要的角色,那便是:JsonToken。它表示解析JSON内容时,用于返回结果的基本标记类型的枚举。

public enum JsonToken {
  NOT_AVAILABLE(null, JsonTokenId.ID_NOT_AVAILABLE),
  START_OBJECT("{", JsonTokenId.ID_START_OBJECT),
  END_OBJECT("}", JsonTokenId.ID_END_OBJECT),
  START_ARRAY("[", JsonTokenId.ID_START_ARRAY),
  END_ARRAY("]", JsonTokenId.ID_END_ARRAY),
  // 属性名(key)
  FIELD_NAME(null, JsonTokenId.ID_FIELD_NAME),
  // 值(value)
  VALUE_EMBEDDED_OBJECT(null, JsonTokenId.ID_EMBEDDED_OBJECT),
  VALUE_STRING(null, JsonTokenId.ID_STRING),
  VALUE_NUMBER_INT(null, JsonTokenId.ID_NUMBER_INT),
  VALUE_NUMBER_FLOAT(null, JsonTokenId.ID_NUMBER_FLOAT),
  VALUE_TRUE("true", JsonTokenId.ID_TRUE),
  VALUE_FALSE("false", JsonTokenId.ID_FALSE),
  VALUE_NULL("null", JsonTokenId.ID_NULL),
}


为了辅助理解,A哥用一个例子,输出各个部分一目了然:


@Test
public void test2() throws IOException {
    String jsonStr = "{\"name\":\"YourBatman\",\"age\":18, \"pickName\":null}";
    System.out.println(jsonStr);
    JsonFactory factory = new JsonFactory();
    try (JsonParser jsonParser = factory.createParser(jsonStr)) {
        while (true) {
            JsonToken token = jsonParser.nextToken();
            System.out.println(token + " -> 值为:" + jsonParser.getValueAsString());
            if (token == JsonToken.END_OBJECT) {
                break;
            }
        }
    }
}


运行程序,输出


{"name":"YourBatman","age":18, "pickName":null}
START_OBJECT -> 值为:null
FIELD_NAME -> 值为:name
VALUE_STRING -> 值为:YourBatman
FIELD_NAME -> 值为:age
VALUE_NUMBER_INT -> 值为:18
FIELD_NAME -> 值为:pickName
VALUE_NULL -> 值为:null
END_OBJECT -> 值为:null


从左至右解析,一一对应。各个部分用下面这张图可以简略表示出来:



image.png




小贴士:解析时请确保你的的JSON串是合法的,否则抛出JsonParseException异常

相关文章
|
3天前
|
存储 JSON 数据处理
从JSON数据到Pandas DataFrame:如何解析出所需字段
从JSON数据到Pandas DataFrame:如何解析出所需字段
14 1
|
1天前
|
JSON JavaScript 前端开发
js将json字符串还原为json对象
【5月更文挑战第14天】js将json字符串还原为json对象
12 1
|
3天前
|
SQL 缓存 JavaScript
深入解析JavaScript中的模板字符串
深入解析JavaScript中的模板字符串
14 1
|
3天前
|
弹性计算 运维 Shell
|
3天前
|
JSON 安全 Swift
【Swift开发专栏】Swift中的JSON解析与处理
【4月更文挑战第30天】本文介绍了Swift中的JSON解析与处理。首先,讲解了JSON的基础,包括其键值对格式和在Swift中的解析与序列化方法。接着,展示了如何使用`Codable`协议简化JSON操作,以及处理复杂结构的示例。通过这些内容,读者能掌握在Swift中高效地处理JSON数据的方法。
|
3天前
|
分布式计算 DataWorks 关系型数据库
DataWorks产品使用合集之在DataWorks中,使用JSON解析函数将MySQL表中的字段解析成多个字段将这些字段写入到ODPS(MaxCompute)中如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
29 3
|
3天前
|
存储 JSON DataWorks
DataWorks产品使用合集之DataWorks将 MongoDB 中的数组类型写入到 DataWorks 的单个字段时,表示为字符串格式而非 JSON 格式如何解决
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
26 3
|
3天前
|
JSON 分布式计算 大数据
MaxCompute产品使用合集之大数据计算MaxCompute 要提取JSON字符串中的所有key-value对,我该怎么操作
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
3天前
|
JSON 前端开发 Java
Json格式数据解析
Json格式数据解析
|
1天前
|
JSON NoSQL MongoDB
实时计算 Flink版产品使用合集之要将收集到的 MongoDB 数据映射成 JSON 对象而非按字段分割,该怎么操作
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
23 1

热门文章

最新文章

推荐镜像

更多