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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 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异常

相关文章
|
2月前
|
SQL 存储 JSON
SQL,解析 json
SQL,解析 json
75 8
|
2月前
|
JSON 前端开发 JavaScript
json字符串如何转为list对象?
json字符串如何转为list对象?
334 7
|
3月前
|
JSON API 数据格式
requests库中json参数与data参数使用方法的深入解析
选择 `data`或 `json`取决于你的具体需求,以及服务器端期望接收的数据格式。
277 2
|
3月前
|
JSON 前端开发 JavaScript
解析JSON文件
解析JSON文件
136 9
|
2月前
|
JSON JavaScript API
商品详情数据接口解析返回的JSON数据(API接口整套流程)
商品详情数据接口解析返回的JSON数据是API接口使用中的一个重要环节,它涉及从发送请求到接收并处理响应的整个流程。以下是一个完整的API接口使用流程,包括如何解析返回的JSON数据:
|
3月前
|
JSON 数据格式 Python
6-1|Python如何将json转化为字符串写到文件内 还保留json格式
6-1|Python如何将json转化为字符串写到文件内 还保留json格式
|
JSON API 数据格式
4. JSON字符串是如何被解析的?JsonParser了解一下(下)
4. JSON字符串是如何被解析的?JsonParser了解一下(下)
|
7月前
|
SQL JSON 监控
实时计算 Flink版产品使用合集之直接将 JSON 字符串解析为数组的内置函数如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
JSON JavaScript 前端开发
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
|
JSON Java fastjson

推荐镜像

更多