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
<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 字符串。
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();@Testpublic 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类)
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();@Testpublic 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>
。
[{"name": "aLang","age": 27,"skillList": ["java","c++"]},{"name": "darcy","age": 26,"skillList": ["go","rust"]}
]
具体实现:
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();@Testpublic 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 对象。
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();@Testpublic 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)
可以忽略不存在的属性。
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();@Testpublic 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 中的处理略有不同。
// 创建订单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类型的序列化与反序列化
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();@Testpublic void dateInPojoToJson() throws JsonProcessingException {Order order = new Order(1, new Date(), null);// 序列化为jsonString 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类型的序列化与反序列化
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();@Testpublic 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”])
添加对应的依赖:
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.3</version>
</dependency>
添加依赖后的代码为:
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();@Testpublic 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
来自定义时间格式。
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;
}
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();@Testpublic 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 有相应的适配等。