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异常

相关文章
|
10月前
|
JSON 人工智能 Go
在Golang中序列化JSON字符串的教程
在Golang中,使用`json.Marshal()`可将数据结构序列化为JSON格式。若直接对JSON字符串进行序列化,会因转义字符导致错误。解决方案包括使用`[]byte`或`json.RawMessage()`来避免双引号被转义,从而正确实现JSON的序列化与反序列化。
579 7
|
数据采集 JSON 数据可视化
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
814 4
JSON数据解析实战:从嵌套结构到结构化表格
|
存储 机器学习/深度学习 缓存
🚀 力扣热题 394:字符串解码(详细解析)(Go语言版)
文章提供了两种解法:栈结构和递归解法。栈解法通过维护数字栈与字符串栈,依次处理 `[` 和 `]`,构造解码结果;递归解法则利用函数调用逐层解析嵌套结构。两者时间复杂度均为 $O(n)$,空间复杂度也为 $O(n)$。栈解法直观易懂,适合初学者;递归解法优雅简洁,适合处理深度嵌套规则。掌握这两种方法,可灵活应对类似问题,提升解题能力。
469 11
|
JSON 前端开发 搜索推荐
关于商品详情 API 接口 JSON 格式返回数据解析的示例
本文介绍商品详情API接口返回的JSON数据解析。最外层为`product`对象,包含商品基本信息(如id、name、price)、分类信息(category)、图片(images)、属性(attributes)、用户评价(reviews)、库存(stock)和卖家信息(seller)。每个字段详细描述了商品的不同方面,帮助开发者准确提取和展示数据。具体结构和字段含义需结合实际业务需求和API文档理解。
|
JSON 小程序 UED
微信小程序 app.json 配置文件解析与应用
本文介绍了微信小程序中 `app.json` 配置文件的详细
2263 12
|
JSON 缓存 API
解析电商商品详情API接口系列,json数据示例参考
电商商品详情API接口是电商平台的重要组成部分,提供了商品的详细信息,支持用户进行商品浏览和购买决策。通过合理的API设计和优化,可以提升系统性能和用户体验。希望本文的解析和示例能够为开发者提供参考,帮助构建高效、可靠的电商系统。
659 12
|
JSON JavaScript 前端开发
处理从API返回的JSON数据时返回Unicode编码字符串怎么处理
在处理API返回的JSON数据时,遇到类似`\u7f51\u7edc\u8fde\u63a5\u9519\u8bef`的Unicode编码字符串,可使用JavaScript内置方法转换为可读文字。主要方法包括:1. 使用`JSON.parse`自动解析;2. 使用`decodeURIComponent`和`escape`组合解码;3. 在API调用中直接处理响应数据。这些方法能有效处理多语言内容,确保正确显示非ASCII字符。
|
JSON API 数据格式
4. JSON字符串是如何被解析的?JsonParser了解一下(下)
4. JSON字符串是如何被解析的?JsonParser了解一下(下)
|
JSON JavaScript 前端开发
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
4. JSON字符串是如何被解析的?JsonParser了解一下(中)
|
JSON Java fastjson

推荐镜像

更多
  • DNS