Jackson: 全面的JSON处理工具

日常开发中常需要 JsonJavaXml之间的互相转换,相关 数据处理工具(data-processing tools) 有很多,鉴于 Jackson 更加全面且操作简单,这里就介绍一下 Jaxckson 的简单用法。

注意 : jackson-dataformat-xml 是 Jackson 扩展组件,只提供了一些简单的方式操作 xml ,不适合作为操作 xml 的主力工具使用。

下面是一些主流的转换工具:

转换工具 说明
json-lib 老牌的转换工具,功能全面, 但第三方依赖多,效率低等,基本停止更新,不推荐使用
jackson 当前流行的工具,全面且操作简单。
fastjson alibaba 的产品,以快而闻名,操作简单,但 不支持 xml

JSON

Jackson 支持 对 JSON 的读写操作,以下是需要的 maven 依赖:

1
2
3
4
5
6
<!-- https://github.com/FasterXML/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>

Jackson 提供了三种方式处理 JSON

三种方式并不是相互孤立的,可以相互配合使用。

处理方式 说明 适用场景
数据绑定 需要有对应的 Java类 来 支持 JSONPOJO 的互转,包括 JDK对象 和 自定义对象。 适用于数据绑定映射
树模型 以树模型来操作 JSON ,针对树节点操作,更加灵活。 适用于复杂的数据结构
流式API 最佳效率来处理 JSON ,和 树模型 一样良好,操作相对复杂。 适用于追求效率的工作

数据绑定

数据绑定 处理方式极其简单,只需要的提供与 Json 相互对应的 Java类 就可以快速完成 javaJson 的相互转换。 但局限也是在此,需要场景明确数据结构

POJO.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class User {

private String username;

private int age;

// getter and setter ...

@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}

数据绑定 需要 ObjectMapper 对象,该对象可以重用。该方式同样适用于 MapList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
public void test() {
User user = new User();
user.setUsername("Jack");
user.setAge(18);

ObjectMapper objectMapper = new ObjectMapper();
try {
// object to json
String strJson = objectMapper.writeValueAsString(user);
System.out.println(strJson);
// {"username":"Jack","age":18}

// json to object
User obj = objectMapper.readValue(strJson, User.class);
System.out.println(obj);
// User{username='Jack', age=18}

} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

树模型

树模型 引入了 JsonNode 对象,允许使用操作树节点的方式对 json 进行读写,并且利于遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Test
public void test() {

ObjectMapper objectMapper = new ObjectMapper();

try {
// json wirte
JsonNode rootNode1 = objectMapper.createObjectNode();
((ObjectNode) rootNode1).put("username", "Jack");
((ObjectNode) rootNode1).put("age", 18);
String strJson = objectMapper.writeValueAsString(rootNode1);
System.out.println(strJson);
// {"username":"Jack","age":18}

// json read
JsonNode rootNode2 = objectMapper.readTree(strJson);
System.out.println(rootNode2);
// {"username":"Jack","age":18}

// iter
Iterator<String> fields = rootNode2.fieldNames();
while (fields.hasNext()){
String field = fields.next();
System.out.println(field + ":" + rootNode2.get(field));
}
// username:"Jack"
// age:18

} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

当然此时如果不想使用 JsonNode 对象,可以使用如下方式转换为对应的 Java对象

1
2
3
4
// JsonNode to POJO
User obj = objectMapper.treeToValue(rootNode2, User.class);
System.out.println(obj);
// User{username='Jack', age=18}

流式API

流式API 引入了 JsonParserJsonGenerator 两个对象,JsonGenerator 负责写操作, JsonParser 负责读操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 流式写入:JsonGenerator
*/
@Test
public void test(){
StringWriter sw = new StringWriter();
JsonGenerator jsonGen = null;
try {
jsonGen = new JsonFactory().createGenerator(sw);
jsonGen.writeStartObject();

jsonGen.writeStringField("username", "Jack");
jsonGen.writeNumberField("age", 18);

jsonGen.writeEndObject();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jsonGen != null) {
try {
jsonGen.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

// jsonGenerator close 后内容才会被写入
System.out.println(sw.toString());
// {"username":"Jack","age":18,}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 流式读取:JsonParser
*/
@Test
public void test() {
String strJson = "{\"age\":28,\"username\":\"Jack\"}";
JsonParser jsonParser = null;
try {
jsonParser = new JsonFactory().createParser(strJson);
if (jsonParser.nextToken() != JsonToken.START_OBJECT) {
throw new IOException("解析失败:非JSON格式");
}
while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jsonParser.getCurrentName();
if ("username".equals(fieldname)){
jsonParser.nextToken();
System.out.println(fieldname + ":" + jsonParser.getText());
// username:Jack
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (jsonParser != null) {
try {
jsonParser.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

由以上的代码,可以看出单纯的 流操作 比较繁杂,操作起来有些得不偿失。

正如之前所说,这三种方式并不是相互孤立的,可以相互配合使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* 流式操作配合数据绑定
*/
@Test
public void test() throws ParseException {
User user = new User();
user.setUsername("Jack");
user.setAge(19);

// object to json
JsonFactory jsonFactory = new JsonFactory();
JsonGenerator jsonGen = null;
StringWriter sw = new StringWriter();

try {
jsonGen = jsonFactory.createGenerator(sw);

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(jsonGen, user);

} catch (IOException e) {
e.printStackTrace();
} finally {
if (jsonGen != null) {
try {
jsonGen.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

System.out.println(sw.toString());
// {"username":"Jack","age":19}


JsonParser jsonParser = null;
// json to object
try {
jsonParser = jsonFactory.createParser(sw.toString());

ObjectMapper objectMapper = new ObjectMapper();
User obj = objectMapper.readValue(sw.toString(), User.class);
System.out.println(obj);
// User{username='Jack', age=19}

} catch (IOException e) {
e.printStackTrace();
}finally {
if (jsonParser != null) {
try {
jsonParser.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

JSON 与 XML

Jackson 支持 对 XML 的简单读写操作,以下是需要的 maven 依赖:

1
2
3
4
5
6
<!-- https://github.com/FasterXML/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.2</version>
</dependency>

对 XML 的操作类似于 JSON,这里仅展示 JSON 与 XML 的互转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Test
public void test() {
String userJson1 = "{\"age\":28,\"username\":\"Jack\"}";

ObjectMapper objectMapper = new ObjectMapper();
XmlMapper xmlMapper = new XmlMapper();
StringWriter sw = new StringWriter();
try {
// json to xml
JsonNode root = objectMapper.readTree(userJson1);
xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
String xml = xmlMapper.writeValueAsString(root);
System.out.println(xml);
// <ObjectNode>
// <age>28</age>
// <username>Jack</username>
// </ObjectNode>

// xml to json
JsonParser jsonParser = xmlMapper.getFactory().createParser(xml);
JsonGenerator jsonGenerator = objectMapper.getFactory().createGenerator(sw);
while (jsonParser.nextToken() != null) {
jsonGenerator.copyCurrentEvent(jsonParser);
}
jsonParser.close();
jsonGenerator.close();

System.out.println(sw.toString());
// {"age":"28","username":"Jack"}
} catch (IOException e) {
e.printStackTrace();
}

}

Jackson配置: Feature

Jackson 预定义了一些配置,可以通过 configure方法 启用和禁用相关特性( Feature ),在 Jackson2.5 又进行了细化,新添加了 enable方法disable方法 ,两种方式都可以使用,但推荐后者。

1
2
3
4
5
6
7
// 启用与禁用:缩进特性
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.configure(SerializationFeature.INDENT_OUTPUT, false);

// Jackson2.5 后的等效方法
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.disable(SerializationFeature.INDENT_OUTPUT);

常用的配置

这里列举了一些常用的配置,基本可以满足日常开发。

Jackson 的 Json解析 默认是严格按照 JSON标准 的,而 JavaScript 中的 json 并不严格按照这个标准的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 支持单引号(兼容 js )
mapper.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
// 支持字段名不带引号(兼容 js )
mapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES);

// 接收 "" 等同 null
mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
// 时间类型正常输出(默认Date , Calendar会输出为数字,这里禁用)
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 遇到未知属性时不抛出异常
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

// null 属性不进行序列化
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

Jackson特性( JacksonFeatures )主要包含以下两种,并含有默认值。

  • 数据绑定特性 : MapperFeature , SerializationFeature , DeserializationFeature
  • 流式API特性:JsonFactory.Feature , JsonParser.Feature , JsonGenerator.Feature

类型格式化

Jackson 对于不同的数据类型,实现不同的格式化,这里以 日期类型 为例说明。

默认行为

默认序列化

日期类型默认序列化为时间戳(数值型),即 Java 中的日期类型 会默认转换为 JSON中的数值类型 : 距离 1970-01-01 08:00:00 的毫秒差。

默认反序列化

对于日期类型,Jackson 有默认的反序列化格式 StdDateFormat ,即 以下格式字符串 可以转换为 Java 中的日期类型。

JSON数值型 也可以被转换为 java日期类型

1
2
3
4
DATE_FORMAT_STR_ISO8601, // "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
"yyyy-MM-dd'T'HH:mm:ss.SSS", // ISO-8601 but no timezone
DATE_FORMAT_STR_RFC1123, // "EEE, dd MMM yyyy HH:mm:ss zzz"
DATE_FORMAT_STR_PLAIN //"yyyy-MM-dd"

自定义格式化: Module

Jackson 提供了功能扩展接口 Module(模块) , 一般使用 SimpleModule 就可以了, 向其添加 序列化器反序列化器 ,再 注册模块

这种方式会覆盖 StdDateFormat ,但是数值型还是可以被正常解析的。

1
2
3
4
5
6
// 定义扩展模块
SimpleModule module = new SimpleModule();
// 添加自定义序列化器
module.addSerializer(Date.class, new DateSerializer(false, new SimpleDateFormat("yyyy-MM-dd")));
// 注册扩展模块
mapper.registerModule(module);

实际上,对于日期类型可以直接使用以下方式简单实现:

两种方式可以并存, module 的优先级更高,推荐使用 module 统一管理扩展。

1
2
// 自定义 序列化格式 与 反序列化格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));