1.概述
Jackson 和 FastJson 一样,是一个 Java 语言编写的,可以进行 JSON 处理的开源工具库,Jackson 的使用非常广泛,Spring 框架默认使用 Jackson 进行 JSON 处理。
Jackson 有三个核包,分别是 Streaming、Databid、Annotations,通过这些包可以方便的对 JSON 进行操作。
- Streaming 在
jackson-core
模块。 定义了一些流处理相关的 API 以及特定的 JSON 实现。 - Annotations 在
jackson-annotations
模块,包含了 Jackson 中的注解。 - Databind 在
jackson-databind
模块, 在Streaming
包的基础上实现了数据绑定,依赖于Streaming
和Annotations
包。
得益于 Jackson 高扩展性的设计,有很多常见的文本格式以及工具都有对 Jackson 的相应适配,如 CSV、XML、YAML 等。
2.Jackson Maven 依赖
在使用 Jackson 时,大多数情况下我们只需要添加 jackson-databind
依赖项,就可以使用 Jackson 功能了,它依赖了下面两个包。
- com.fasterxml.jackson.core:jackson-annotations
- com.fasterxml.jackson.core:jackson-core
xml
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<!-- 引入单元测试模块 -->
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<!-- 引入lombok模块 -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
3.ObjectMapper 对象映射器
ObjectMapper
是 Jackson 库中最常用的一个类,使用它可以进行 Java 对象和 JSON 字符串之间快速转换。 Jackson 中的 ObjectMapper
就如同 FastJson 中的 JSON 类。
这个类中有一些常用的方法:
readValue()
方法可以进行 JSON 的反序列化操作,比如:将字符串、文件流、字节流、字节数组等转换成 Java 对象。writeValue()
方法可以进行 JSON 的序列化操作,可以将 Java 对象转换成 JSON 字符串。
大多数情况下,ObjectMapper
的工作原理是通过 Java Bean 对象的 Get/Set 方法进行转换时映射的,所以正确编写 Java 对象的 Get/Set 方法尤为重要,不过 ObjectMapper
也提供了诸多配置,比如可以通过配置或者注解的形式对 Java 对象和 JSON 字符串之间的转换过程进行自定义。
4.Jackson JSON 基本操作
4.1 Jackson JSON 序列化
编写一个 Person 类,定义三个属性,名称、年龄以及技能, 将 Java 对象(Person)转换成 JSON 字符串。
java
import lombok.Data;
import java.util.List;
// Person 类
@Data
public class Person {
private String name;
private Integer age;
private List<String> skillList;
}
// 将Person转换为Json对象
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import java.util.Arrays;
public class PersonTest {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void pojoToJsonString() throws JsonProcessingException {
Person person = new Person();
person.setName("Ayang");
person.setAge(18);
person.setSkillList(Arrays.asList("java", "c++"));
String json = objectMapper.writeValueAsString(person);
System.out.println(json);
String expectedJson = "{\"name\":\"Ayang\",\"age\":18,\"skillList\":[\"java\",\"c++\"]}";
Assertions.assertEquals(json, expectedJson);
}
}
// 输出的 JSON 字符串:
// {"name":"Ayang","age":18,"skillList":["java","c++"]}
4.2 Jackson JSON 反序列化
将Json字符串反序列化为Java对象(Person类)
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class PersonTest01 {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void jsonStringToPojo() throws JsonProcessingException {
String expectedJson = "{\"name\":\"Ayang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";
// readValue方法的输入来源可以是很多种:文件/输入数据(字节数组、输入流等)
Person person = objectMapper.readValue(expectedJson, Person.class);
System.out.println(person);
Assertions.assertEquals(person.getName(),"Ayang");
Assertions.assertEquals(person.getSkillList().toString(), "[java, c++]");
}
}
// 输出结果
// Person(name=Ayang, age=27, skillList=[java, c++])
4.3 JSON 转 List
上面演示 JSON 字符串都是单个对象的,如果 JSON 是一个对象列表那么使用 Jackson 该怎么处理呢? 从
PersonList.json
文件读取数据 转换成List<Person>
。
json
[
{
"name": "aLang",
"age": 27,
"skillList": [
"java",
"c++"
]
},
{
"name": "darcy",
"age": 26,
"skillList": [
"go",
"rust"
]
}
]
具体实现:
java
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class PersonTest02 {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void jsonStringToPojo() throws IOException {
File file = new File("src/PersonList.json");
List<Person> personList = objectMapper.readValue(file, new TypeReference<List<Person>>() {});
for (Person person : personList) {
System.out.println(person);
}
Assertions.assertEquals(personList.size(), 2);
Assertions.assertEquals(personList.get(0).getName(), "aLang");
Assertions.assertEquals(personList.get(1).getName(), "darcy");
}
}
// 输出结果
//Person(name=aLang, age=27, skillList=[java, c++])
//Person(name=darcy, age=26, skillList=[go, rust])
4.4 JSON 转 Map
使用 Jackson 把 JSON 文本转成 Map 对象。
java
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.Map;
public class PersonTest03 {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void jsonStringToPojo() throws IOException {
String expectedJson = "{\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";
Map<String, Object> employeeMap = objectMapper.readValue(expectedJson, new TypeReference<Map>() {});
System.out.println(employeeMap.getClass());
for (Map.Entry<String, Object> entry : employeeMap.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
Assertions.assertEquals(employeeMap.get("name"), "aLang");
}
}
// 输出结果
/*
class java.util.LinkedHashMap
name:aLang
age:27
skillList:[java, c++]
*/
4.5 Jackson 忽略字段
如果在进行 JSON 转 Java 对象时,JSON 中出现了 Java 类中不存在的属性,那么在转换时会遇到 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
异常。使用 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
可以忽略不存在的属性。
java
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class PersonTest04 {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void jsonStringToPojo() throws IOException {
String json = "{\"yyy\":\"xxx\",\"name\":\"aLang\",\"age\":27,\"skillList\":[\"java\",\"c++\"]}";
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Person person = objectMapper.readValue(json, Person.class);
System.out.printf(person.toString());
Assertions.assertEquals(person.getName(), "aLang");
Assertions.assertEquals(person.getSkillList().toString(), "[java, c++]");
}
}
//输出结果
// Person(name=aLang, age=27, skillList=[java, c++])
4.6 Jackson 日期格式化
在 Java 8 之前我们通常使用 java.util.Date
类来处理时间,但是在 Java 8 发布时引入了新的时间类 java.time.LocalDateTime
. 这两者在 Jackson 中的处理略有不同。
java
// 创建订单Order对象
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
private Integer id;
private Date createTime;
private LocalDateTime updateTime;
}
4.6.1 Date类型的序列化与反序列化
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Date;
public class DateTransform {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void dateInPojoToJson() throws JsonProcessingException {
Order order = new Order(1, new Date(), null);
// 序列化为json
String json = objectMapper.writeValueAsString(order);
System.out.println(json);
//反序列化为Order对象
order = objectMapper.readValue(json, Order.class);
System.out.println(order.toString());
Assertions.assertEquals(order.getId(), 1);
}
}
/* 输出结果:
{"id":1,"createTime":1712729115179,"updateTime":null}
Order(id=1, createTime=Wed Apr 10 14:05:15 CST 2024, updateTime=null)
*/
可以看到正常的进行了 JSON 的序列化与反序列化,但是 JSON 中的时间是一个时间戳格式,可能不是我们想要的。
4.6.2 LocalDateTime类型的序列化与反序列化
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Date;
public class OrderTest {
ObjectMapper objectMapper = new ObjectMapper();
@Test
public void testPojoToJson() throws JsonProcessingException {
Order order = new Order(1, new Date(), LocalDateTime.now());
String json = objectMapper.writeValueAsString(order);
System.out.println(json);
order = objectMapper.readValue(json, Order.class);
System.out.println(order.toString());
Assertions.assertEquals(order.getId(), 1);
}
}
运行报错:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type
java.time.LocalDateTime
not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.study.jackson.Order["updateTime"])
添加对应的依赖:
java
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.3</version>
</dependency>
添加依赖后的代码为:
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Date;
public class OrderTest {
// 通过findAndRegisterModules注册依赖
ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
@Test
public void testPojoToJson() throws JsonProcessingException {
Order order = new Order(1, new Date(), LocalDateTime.now());
String json = objectMapper.writeValueAsString(order);
System.out.println(json);
order = objectMapper.readValue(json, Order.class);
System.out.println(order.toString());
Assertions.assertEquals(order.getId(), 1);
}
}
/* 输出结果:
{"id":1,"createTime":1712729605162,"updateTime":[2024,4,10,14,13,25,166000000]}
Order(id=1, createTime=Wed Apr 10 14:13:25 CST 2024, updateTime=2024-04-10T14:13:25.166)
*/
4.6.3 时间格式化
通过在字段上使用注解 @JsonFormat
来自定义时间格式。
java
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order1 {
private Integer id;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private LocalDateTime updateTime;
}
java
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.util.Date;
public class OrderTest01 {
ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
@Test
public void testPojoToJson() throws JsonProcessingException {
Order1 order = new Order1(1, new Date(), LocalDateTime.now());
String json = objectMapper.writeValueAsString(order);
System.out.println(json);
order = objectMapper.readValue(json, Order1.class);
System.out.println(order);
Assertions.assertEquals(order.getId(),1);
}
}
/*
输出结果:
{"id":1,"createTime":"2024-04-10 14:22:17","updateTime":"2024-04-10 14:22:17"}
Order1(id=1, createTime=Wed Apr 10 14:22:17 CST 2024, updateTime=2024-04-10T14:22:17)
*/
5.总结
- Jackson 是 Java 中比较流量的 JSON 处理库之一,它是 Spring 的默认 JSON 工具。
- Jackson 主要有三个模块组成,Streaming API 、Annotations 和 Data Binding 。
- Jackson 中的 ObjectMapper 类十分强大,可以进行 JSON 相关处理,同时可以结合注释以及配置进行自定义转换逻辑。
- Jackson 扩展性很好,如 CSV、XML、YAML 格式处理都对 Jackson 有相应的适配等。