08_jackson-1

第七十二章 Jackson库之一 (解析和生成JSON)

1 简介

在开发项目时,一个常见的应用场景是将内存中的对象转换为一个JSON字符串,或者将一个JSON字符串转换为一个内存中的对象。上述的这两个过程被称为数据序列化(Data Serialization)和数据反序列化(Data Deserialization)。

在Java第三方代码库中,json-simpleGSONJSONPJackson都是非常流行的JSON代码库。本文将重点介绍Jackson库的使用方法,因为Jackson库有以下诸多优点。

  1. Jackson提供简单、易用的开发接口,开发人员只需编写极少的代码就可以解析或者生成JSON字符串。
  2. Jackson的处理速度非常快,占用内存小。所以,Jackson适用于绝大多数的Java项目。
  3. Jackson不依赖于任何代码库。单独使用Jackson就可以处理JSON字符串。
  4. Jackson是开源项目。

我们将在四篇文章中介绍Jackson库的使用方法。此篇文章首先介绍Jackson库的内部结构和各部件的功能,并讲解Jackson底层部件的工作原理。我们将在下一篇重点介绍Jackson的编程接口和使用方法,并使用示例展示几种JSON字符串转换的不同应用场景。第三篇和第四篇会分别介绍XML文档和YAML文档的生成和解析方法。

2 Jackson内部结构

Jackson库的核心内容分为三个模块。

  1. Streaming API:Jackson Streaming API模块实现了底层解析和生成JSON字符串的功能。在解析JSON字符串的过程中,该模块根据JSON的规则将输入字符串分解为一组有序的Token,并允许上层模块根据业务需求处理这些Token。在生成JSON字符串的过程中,该模块根据上层给出的数据将其“拼接”成一个紧凑的、合法的JSON字符串。
  2. Databind:Jackson Databind模块负责建立Java对象与JSON字符串之间的对应关系。通过使用Data Binding,开发人员能够方便的实现对象和JSON文档之间的互转。
  3. Annotations:Jackson Annotations模块为开发人员提供了方便的标注。通过使用这些标注,开发人员能够方便灵活的控制JSON序列化和反序列化的过程,以及对象和JSON字符串的映射关系。

以上三个模块之间的依赖关系如下。

图一  Jackson模块依赖图

图一 Jackson模块依赖图

3 Streaming API的用法

Streaming API提供了一套底层接口用于解析和生成JSON字符串。我们将在下面的两个小节中分别介绍JSON字符串解析和生成的使用方法。

如果使用Maven管理项目,需要将如下依赖关系加入项目的pom.xml文件中。

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.11.1</version>
</dependency>

如果使用Gradle管理项目,需要将如下依赖加入项目的build.gradle文件中。

repositories {
  mavenCentral()
}

dependencies {
  implementation 'com.fasterxml.jackson.core:jackson-core:2.11.1'
}

3.1 JSON文档解析与JsonParser类

在底层接口中,JsonParser对象用于解析JSON字符串。在解析的过程中,JsonParser对象会通过nextToken()函数不断地向上层逻辑返回解析的Token,直到isClose()函数返回true为止。上述的Token实际上代表着JSON字符串中的关键字段或者分隔符。

如下面的代码所示。在JsonParserExample类的main()函数中,我们定义了一个JSON字符串。该字符串包含两个键值对(name和age)。name的值是字符串Adam;age的值是整数18。

随后,我们创建了一个JsonFactory对象,再由这个factory对象创建一个JsonParser对象。此时,这个JsonParser对象已经准备好开始解析JSON字符串了。最后,我们使用一个while循环,遍历JSON字符串中所有的token。nextToken()函数返回当前的Token。当所有的Token都被读取之后,nextToken()函数返回null。我们在循环中,每获得一个token,则打印这个token的类型。

import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class JsonParserExample {
    public static void main(String[] args) {
        String aJson = "{ \"name\" : \"Adam\", \"age\" : 18 }";

        try {
            JsonFactory factory = new JsonFactory();
            JsonParser  parser  = factory.createParser(aJson);
            
            // 使用parser对象遍历所有的token
            while(true){
                JsonToken token = parser.nextToken();
                if (token == null)
                    break;
    
                // 打印Token的内容
                System.out.println("token = " + token);
            }
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

如下是该程序输出的Token的类型。

token = START_OBJECT
token = FIELD_NAME
token = VALUE_STRING
token = FIELD_NAME
token = VALUE_NUMBER_INT
token = END_OBJECT

这些Token与JSON字符串中内容的对照如下图所示。

图二  Token与JSON字符串内容对照图

图二 Token与JSON字符串内容对照图

在获得一个JsonToken对象之后,可以通过判断JsonToken对象的类型来获取Token对应的名称和值。如下面的代码所示,如果当前的Token是FIELD_NAME的话,我们可以调用getCurrentName()方法来获取名称。然后再调用nextToken()前进到下一个Token。这个Token应该对应的是值。所以,可以根据值的类型,分别使用getValueAsString()或者getValueAsInt()获取值。

import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;

public class JsonParserExample {
    public static void main(String[] args) {
        String aJson = "{ \"name\" : \"Adam\", \"age\" : 18 }";

        try {
            JsonFactory factory = new JsonFactory();
            JsonParser  parser  = factory.createParser(aJson);
            
            while(true){
                JsonToken jsonToken = parser.nextToken();
                if (jsonToken == null)
                    break;
    
                if(JsonToken.FIELD_NAME.equals(jsonToken)){
                    String fieldName = parser.getCurrentName();
                    jsonToken = parser.nextToken();
            
                    if("name".equals(fieldName)){
                        System.out.println(fieldName + "=" + parser.getValueAsString());
                    } else if ("age".equals(fieldName)){
                        System.out.println(fieldName + "=" + parser.getValueAsInt());
                    }
                }
            }
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

程序输出的结果为:

name=Adam
age=18

3.2 JSON文档生成与JsonGenerator类

JSON文档的生成是通过使用JsonGenerator完成的。为了生成上述相同的JSON文档,我们首先创建一个JsonFactory对象,并由这个factory对象,创建一个JsonGenerator对象。然后,向JsonGenerator对象添加Token和键值对。writeStartObject()和writeEndObject()方法分别向generator对象添加左花括号和右花括号。writeStringField()和writeNumberField()方法分别添加键值对。最后,在关闭generator对象后,JSON字符串会被写入jsonBuffer的对象中,最后将生成的JSON字符串打印出来。

import java.io.IOException;
import java.io.ByteArrayOutputStream;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonEncoding;

public class JsonGeneratorExample {
    public static void main(String[] args) {
        try {
            ByteArrayOutputStream jsonBuffer = new ByteArrayOutputStream();

            JsonFactory factory = new JsonFactory();
            JsonGenerator generator = factory.createGenerator(jsonBuffer, JsonEncoding.UTF8);
            
            generator.writeStartObject();
            generator.writeStringField("name", "Adam");
            generator.writeNumberField("age", 18);
            generator.writeEndObject();
            generator.close();
    
            System.out.println(jsonBuffer.toString("utf-8"));
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }
    }
}

程序的输出为一个紧凑格式的JSON字符串,如下所示。

{"name":"Adam","age":18}

4 结语

本章介绍了Jackson的内部结构和Stream API的使用方法。我们将在下一章介绍Jackson库的另外两个模块:Data Binding和Annotation。

 

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.