目录
通过configure单独设置序列化或者反序列化或者通用规则
DeserializationFeature(反序列化特性)
JackSon介绍
Jackson 是一个高效的 Java 库,用于处理 JSON 数据的序列化和反序列化。它主要用于将 Java 对象转换为 JSON 格式,以及将 JSON 数据转换为 Java 对象。Jackson 提供了强大的功能、灵活性和易用性,因此被广泛应用于 Java 应用程序中。
主要特点:
-
高性能:
- Jackson 被设计为高效且快速的 JSON 处理库,能够处理大量数据而不影响性能。
-
灵活性:
- 支持多种数据类型和嵌套结构,并能够处理复杂类型(如集合、Map、数组等)。
-
丰富的功能:
- 提供了强大的注解支持,允许开发者自定义序列化和反序列化的行为,如字段命名、忽略某些字段等。
-
支持多种格式:
- 除了 JSON,Jackson 还支持其他数据格式,如 XML、YAML 等,通过扩展模块实现。
-
流式处理:
- Jackson 支持流式处理,允许逐步读取和写入数据,适合处理大型数据集。
-
对象映射:
- Jackson 提供
ObjectMapper
类,简化了对象和 JSON 之间的转换,支持轻松地将 Java 对象序列化为 JSON 字符串或将 JSON 字符串反序列化为 Java 对象。
- Jackson 提供
pom依赖引入
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.18.3</version> <!-- 请根据需要选择最新版本 -->
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
//这个的作用是提供各个字段的geter和seter方法
在测试中会使用到的实体类
People实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {
private Integer id;//id号
private String name;//姓名
private Integer age;//年龄
private String sex;//性别
}
JackSon的使用(类和方法)
默认序列化方式(对象转Json字符串)
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 将一个对象转化为json字符串
*/
@Test
public void test1() throws JsonProcessingException {
People people = new People(20,"张三",19,"男");
ObjectMapper mapper = new ObjectMapper();
//创建ObjectMapper实例,这个实例用于将json字符串解析成java对象或者将java转换为json字符串
String s = mapper.writeValueAsString(people);
//writeValueAsString是将一个java实例对象转成一个json字符串,他是无格式化的
System.out.println(s);
String s1 = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
//writerWithDefaultPrettyPrinter创建一个带有默认美化格式的json编写器,然后再调用writeValueAsString来转化成一个有格式的json字符串
System.out.println(s1);
}
}
运行结果:
默认的Json字符串转java对象
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 将一个json字符串转化为一个java对象
*/
@Test
public void test2() throws JsonProcessingException {
//定义一个json字符串
String json = "{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}";
ObjectMapper mapper = new ObjectMapper();
//创建一个ObjectMapper对象用于解析json字符串
People people = mapper.readValue(json, People.class);
//作用是将一个json字符串解析为指定的java对象
//传入的参数第一个是要解析的json字符串,第二个是指定的是将json字符串解析出来封装在哪个类里面
System.out.println(people);
}
}
我们在记忆json字符串解析成java对象和java对象转换成Json字符串的时候我们可以这么记忆,wite是来写json字符串,read是来读json字符串。
运行结果:
将一个Json字符串转换成List<>集合
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 将一个List<people>集合类型的json字符串转化为java对象
*/
@Test
public void test3() throws JsonProcessingException {
//List<People>集合
String json = "[{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}," +
"{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}," +
"{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}," +
"{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}]";
ObjectMapper mapper = new ObjectMapper();
List<People> people = mapper.readValue(json, new TypeReference<List<People>>() {
});
System.out.println(people);
}
}
mapper.readValue(json, new TypeReference<List<People>>() {});这段代码跟People people = mapper.readValue(json, People.class)很像
他们其实在解析字符串的时候都是差不多的,这里为什么要new这么一个匿名内部类呢,其实我们传入这么一个内部类的实例进去,它在readValue这个存在方法里面会调用这个内部类里面的getType方法来获取我们指定类型的class对象即List<People>.class,为什么要用这么一个内部类了,因为java它的泛型擦除机制,即在编译的时候它会将泛型去掉,即后面值剩下List了,这无法指定我们List里面的类型是什么样的,就无法封装,而这个TypeReference就能保留泛型里面指定的类型,我们只需要将我们最后返回的类型指定在里面就好了,即List<People>
部分运行结果:
类中含有复杂数据类型的序列化和反序列化
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import com.part1.Entity.Student;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* student类型中含有List<People>类型的时候解析和转换
*/
@Test
public void test4() throws JsonProcessingException {
People people = new People(20,"张三",19,"男");
ArrayList<People> list = new ArrayList<>();
list.add(people);
Student student = new Student("四川",list);
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);
System.out.println(s);
Student value = mapper.readValue(s, Student.class);
System.out.println(value);
//事实证明当一个属性是复杂数据类型的时候默认的序列化器也能转成功
}
}
运行结果:
通过访问JsonNode节点数获取值
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 最暴力的方法
* 通过json节点数去访问对应的字符串数据
*/
@Test
public void test5() throws JsonProcessingException {
String s = "{\n" +
" \"id\" : 1,\n" +
" \"people\" : {\n" +
" \"name\" : \"李四\",\n" +
" \"age\" : 19,\n" +
" \"gender\" : \"男\"\n" +
" }\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(s);
//readTree接收一个json字符串并解析返回一个JsonNode对象,这个对象能提供一个树形结构的类,我们能访问访问到里面的数据
int id = jsonNode.get("id").asInt();
//获取id的值,并将其返回一个int类型的值
System.out.println(id);
String text = jsonNode.path("people").get("name").asText();
//这里使用path的原因是因为我们要获取的name是相当于一个类里面的一个属性这个属性的类型是另外一个类,我们就要通过path进入到指定的属性值里面
//我们进入后才能拿到这个name的值
System.out.println(text);
}
}
运行结果:
设置序列化和反序列化的时候的通用转换规则
我们在设置序列化或者反序列化的时候可能会出现我们不想转换原来的数据或者对象里面字段的值为空或者他的值为默认值的字段,即例如在序列化的时候不想将实例对象对应属性为null的属性给序列化,此时我们就可以使用对应的方法来设置解析器ObjectMapper
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* setDefaultPropertyInclusion设置序列化和反序列化的时候的规则
* JsonInclude.Include.ALWAYS:总是包含属性(默认行为)。
* JsonInclude.Include.NON_NULL:只包含非 null 的属性。
* JsonInclude.Include.NON_EMPTY:只包含非空的属性,适用于集合类型、字符串等。
* JsonInclude.Include.NON_DEFAULT:只包含与默认值不同的属性。
*/
@Test
public void test6() throws JsonProcessingException {
People people = new People();
people.setAge(0);
people.setId(null);
//对于一个封装类的数据类型它的默认值可以是0,可以是null意思就是这两个对于封装类数据来说都是默认值
//若是普通数据就默认是0
people.setName("张三");
ObjectMapper mapper = new ObjectMapper();
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
//设置序列化时属性的包含规则,setDefaultPropertyInclusion是来设置哪些属性会被包含,哪些属性会被排除
//setDefaultPropertyInclusion设置的规则在序列化和反序列化都能用的规则
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
System.out.println(s);
}
}
运行结果:
设置序列化时的规则
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
*setSerializationInclusion设置序列化时的规则
*/
@Test
public void test7() throws JsonProcessingException {
People people = new People();
people.setAge(0);
people.setId(null);
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
System.out.println(s);
}
}
运行结果:
这里的设置规则在上一个通用规则已经讲解就不再重复了
通过configure单独设置序列化或者反序列化或者通用规则
我们可以通过ObjectMapper解析器的configure方法来设置对应的规则,configure第一个参数时枚举的值,第二个是一个布尔值,这个布尔值用于判断这个规则是否生效
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
@Test
public void test8() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
//DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES是当json数据里面包含java指定解析类里面没有的属性的时候会抛出异常,此时我们关闭它,它就会忽略这个多余的字段
//第一个参数是一个枚举的常量,这个枚举可以是MapperFeature或者SerializationFeature或者DeserializationFeature
//第二个参数是一个布尔值,指定这个参数是否开启
//SerializationFeature:控制序列化过程中的行为,比如是否输出空值、日期格式等。
//DeserializationFeature:控制反序列化过程中的行为,比如是否允许未知字段、是否要求非空值等。
//MapperFeature:控制整个 ObjectMapper 的行为,比如是否开启属性命名策略等
String json = "{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\",\"home\":\"四川\"}";
//此时这个里面含有一个多余字段home字段,那么在解析封装的时候其实是会默认报错的
People people = mapper.readValue(json, People.class);
System.out.println(people);
}
}
configure第一个参数可以接收三种类型的枚举下面给出这三个枚举里面的值的意思
configure参数接收的三种枚举类型的值的含义
SerializationFeature
(序列化特性)
枚举值 | 默认值 | 说明 |
---|---|---|
|
| 美化输出,添加缩进和换行。 |
|
| 序列化时包含根类型名称(如 |
|
| 将日期序列化为时间戳(若关闭则按 |
|
| 将时间戳的秒部分序列化为纳秒(若关闭则用毫秒)。 |
|
| 将 |
|
| 序列化枚举时使用 |
|
| 将单元素数组展开为单个值(如 |
|
| 序列化 |
|
| 序列化空数组(若关闭则跳过)。 |
|
| 序列化无字段的 |
|
| 按 |
DeserializationFeature
(反序列化特性)
枚举值 | 默认值 | 说明 |
---|---|---|
FAIL_ON_UNKNOWN_PROPERTIES | true | 遇到 JSON 中存在但 Java 类无匹配字段时抛出异常(若关闭则忽略未知字段)。 |
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT | false | 将空字符串 "" 视为 null 。 |
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT | false | 将空数组 [] 视为 null 。 |
USE_JAVA_ARRAY_FOR_JSON_ARRAY | true | 将 JSON 数组反序列化为 Java 数组(若关闭则可能用 List )。 |
READ_ENUMS_USING_TO_STRING | false | 根据 toString() 反序列化枚举(默认用 name() )。 |
ADJUST_DATES_TO_CONTEXT_TIME_ZONE | true | 将日期调整为 ObjectMapper 的时区(若关闭则保留原始时区)。 |
UNWRAP_ROOT_VALUE | false | 支持根值解包(需配合 WRAP_ROOT_VALUE 使用)。 |
UNWRAP_SINGLE_VALUE_ARRAYS | false | 将单元素数组解包为单个值(如 [5] → 5 )。 |
FAIL_ON_NULL_FOR_PRIMITIVES | false | 当基本类型字段(如 int )的 JSON 值为 null 时抛出异常。 |
FAIL_ON_NUMBERS_FOR_ENUMS | false | 禁止用数字反序列化枚举(默认允许) |
MapperFeature
(映射器全局特性)
枚举值 | 默认值 | 说明 |
---|---|---|
AUTO_DETECT_FIELDS | true | 自动检测类的字段(包括非 public 字段)。 |
AUTO_DETECT_GETTERS | true | 自动检测 getter 方法。 |
AUTO_DETECT_IS_GETTERS | true | 自动检测 isXxx() 方法(用于 boolean 字段)。 |
AUTO_DETECT_SETTERS | true | 自动检测 setter 方法。 |
USE_ANNOTATIONS | true | 启用注解(如 @JsonProperty )的功能。 |
USE_GETTERS_AS_SETTERS | true | 允许通过 getter 推断 setter 逻辑(用于构造对象)。 |
SORT_PROPERTIES_ALPHABETICALLY | false | 按字母顺序序列化字段。 |
ACCEPT_CASE_INSENSITIVE_PROPERTIES | false | 反序列化时忽略字段名大小写(如 UserName 与 username 匹配)。 |
USE_STD_BEAN_NAMING | false | 使用标准 Bean 命名策略(如 getX() 对应字段 x ,而非 X )。 |
ALLOW_EXPLICIT_PROPERTY_RENAMING | false | 允许通过 @JsonProperty 显式重命名字段。 |
IGNORE_DUPLICATE_MODULE_REGISTRATIONS | false | 忽略重复注册的模块(若开启则不会抛出异常)。 |
可以通过里面的这些值来设置对应的规则
自定义序列化器
首先我们要写一个类继承StdSerializer(JsonSerializer也可以,StdSerializer是JsonSerializer的继承类),且StdSerializer后面要跟我们这个序列化器要操作的类(这里我们指定People类),然后重写serialize方法
这里我们定义这个序列化器将People实例里面的name属性的值添加前缀name_
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.part1.Entity.People;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* 自定义序列化器,继承StdSerializer<People>
* JsonSerializer是StdSerializer的原始类,StdSerializer有处理空值等逻辑,而继承JsonSerializer就是完全自定义序列化器
*/
public class PeopleSerializer extends StdSerializer<People> {
public PeopleSerializer() {
this(null);
}
public PeopleSerializer(Class<People> t) {
super(t);
}
/**
* @param people
* @param jsonGenerator
* @param serializerProvider
* @throws IOException
* 我们通过反射的方式将序列化的时候将name属性的值加前缀name
*/
@Override
public void serialize(People people, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
//JsonGenerator能够支持我们通过方法调用的方式来创建一个json字符串,它提供流式API逐步向输出流中写入json数据
jsonGenerator.writeStartObject();
//用于标记开始向输出流里面写入一个新的json对象,它能在json输出中插入一个左花括号,标记这json对象输出的开始
for (Field field:people.getClass().getDeclaredFields())
{
field.setAccessible(true);
//允许对该字段的访问,即使字段是是私有的
if (field.getName().equals("name")){
//判断是否是name字段
try {
jsonGenerator.writeStringField(field.getName(), "name_"+field.get(people));
//向json输出流里面写一个字符串字段,前面是字段名称,后面是字段的值这个传入的是一个字符串类型的值
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}else {
try {
jsonGenerator.writeObjectField(field.getName(),field.get(people));
//向json输出流写一个对象字段,后面参数是一个对象,注意field.get参数是我们要获取的这个field的值的对象
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
jsonGenerator.writeEndObject();
//标志着结束当前json字符串的写入
}
}
当我们写完这个序列化器还没有完,我们要将这个序列化器注册在解析器ObjectMapper里面,用于覆盖原本的序列化规则
通过创建SimpleMoudle模块将序列化器与People列绑定在一起,最后再将原本的默认序列化器覆盖掉(针对于People)
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.part1.Entity.People;
import com.part1.Utils.PeopleSerializer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 自定义一个序列化器,这个序列化器我们定义作用是在name字段值里面加name_前缀
*/
@Test
public void test9() throws JsonProcessingException {
People people = new People(10,"张三",20,"男");
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("PeopleSerializer",new Version(1, 0, 0, null));
//自定义模块,定义一个PeopleSerializer名字的模块用来管理自定义的序列化/反序列化规则
module.addSerializer(People.class, new PeopleSerializer());
//将people和PeopleSerializer绑定,将People原来的序列化的规则覆盖,变成PeopleSerializer里面的规则
mapper.registerModule(module);
//将这个模块注册到ObjectMapper,不注册的话在People被序列化的时候会按默认序列化规则实现
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
//格式化生成字符串
System.out.println(json);
}
}
运行结果
自定义反序列化器
这个的逻辑其实跟序列化器的很像,主要是我们在自定义反序列化器的时候里面的逻辑。我们主要是通过反射的机制来实现的
首先还是创建一个反序列化的类
注意此时他是继承的StdDeserializer这个类且后面要跟泛型
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.part1.Entity.People;
import java.io.IOException;
import java.lang.reflect.Field;
/**
* 自定义反序列化器,继承StdDeserializer<People>
* 在我们这个自定义的反序列化器里面我们将name字段解析的时候将它的值加一个name_前缀
*/
public class PeopleDeserialize extends StdDeserializer<People> {
public PeopleDeserialize() {
this(null);
}
public PeopleDeserialize(Class<People> t) {
super(t);
}
@Override
public People deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
//JsonParser是用于解析json数据的类,它是来读取json数据的
People people = new People();
ObjectCodec codec = jsonParser.getCodec();
//jsonParser.getCodec()是获取当前的编码器用于执行序列化或反序列化的操作
//ObjectCodec是核心编码器,通常是ObjectMapper负责json于java的转换
JsonNode node = codec.readTree(jsonParser);
//将其转化为JsonNode对象读取里面的数据
for (Field field : people.getClass().getDeclaredFields()){
field.setAccessible(true);
//设置允许访问所有的成员变量
if (field.getName().equals("name")){
String value = "name_"+node.get("name").asText();
try {
field.set(people,value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}else {
// String value = node.get(field.getName()).asText();
//直接使用这个是会报错的,因为People里面有Integer数值封装类,把一个字符串形式的赋值给它只会报错
JsonParser value = node.path(field.getName()).traverse(codec);
//从node.path(field.getName())节点里面获取目标字段的值,然后生成新的JsonParser
//traverse(codec)是将静态的数据节点转换成动态的解析流JsonParser,然后复用默认的反序列化规则codec里面的规则
try {
field.set(people,value.readValueAs(Object.class));
//value.readValueAs(Object.class)是转换成动态的java对象
//注意是readValueAs,提示词很容易打成readValuesAs,还要注意是设置到people里面
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
return people;
}
}
注意在value.readValueAs这个方法里面设置的是Object.class
他不是指定设置的值是Object类型,而是他的返回值即返回的通用类型,但是他的这个值具体类型还是有jackson来实现, //Object.class是设置这个java对象的返回类型,但是他数据具体的类型是由jackson自己决定的
{"name": "Alice"} → LinkedHashMap<String, String>
25 → Integer
"2023-10-05" → String
[1, 2, 3] → ArrayList<Integer>
true → Boolean
null → null
通过这种规则转换来的
再注册在解析器里面
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.part1.Entity.People;
import com.part1.Utils.PeopleDeserialize;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class test {
/**
* @throws JsonProcessingException
* 自定义一个反序列化器
*/
@Test
public void test10() throws JsonProcessingException {
String json = "{\"id\":20,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule("PeopleDeseriqlize",new Version(1, 0, 0, null));
//创建一个模板,管理这个反序列化器或者序列化器规则
module.addDeserializer(People.class,new PeopleDeserialize());
//添加这个反序列化器到模板里面
mapper.registerModule(module);
//注册
People people = mapper.readValue(json, People.class);
System.out.println(people);
}
}
运行结果:
JackSon相关注解使用
在jackson中有很多的相关注解能直接使用,能大大降低我们自己写代码的难度
以下是 Jackson 常用注解的整理表格,包含注解名称、用途及示例说明:
注解名称 | 用途 | 示例 |
---|---|---|
@JsonProperty | 指定 JSON 字段名与 Java 字段的映射关系。 | @JsonProperty("user_name") private String name; |
@JsonIgnore | 序列化和反序列化时忽略该字段。 | @JsonIgnore private String password; |
@JsonInclude | 控制字段何时被包含(如非空、非默认值)。 | @JsonInclude(Include.NON_NULL) private String remark; |
@JsonAlias | 反序列化时允许 JSON 字段有多个别名。 | @JsonAlias({"age", "userAge"}) private Integer age; |
@JsonFormat | 定义日期、时间或数字的序列化格式。 | @JsonFormat(pattern="yyyy-MM-dd") private Date birthDate; |
@JsonSerialize | 自定义序列化逻辑(需实现 JsonSerializer )。 | @JsonSerialize(using = CustomDateSerializer.class) Date date; |
@JsonDeserialize | 自定义反序列化逻辑(需实现 JsonDeserializer )。 | @JsonDeserialize(using = CustomDateDeserializer.class) Date date; |
@JsonTypeInfo | 定义多态类型的元数据(如类型标识字段)。 | @JsonTypeInfo(use=Id.NAME, property="type") |
@JsonSubTypes | 声明多态子类与类型标识的映射关系。 | @JsonSubTypes(@Type(value=Dog.class, name="dog")) |
@JsonTypeName | 为多态子类指定类型标识名。 | @JsonTypeName("cat") public class Cat extends Animal {} |
@JsonUnwrapped | 将嵌套对象的字段平铺到外层 JSON。 | @JsonUnwrapped private Address address; |
@JsonCreator | 指定反序列化时的构造方法或工厂方法。 | @JsonCreator public User(@JsonProperty("name") String name) { ... } |
@JsonIdentityInfo | 处理循环引用(如双向关联对象)。 | @JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) |
@JsonView | 定义字段在不同视图下的可见性。 | @JsonView(Views.Public.class) private String name; |
@JsonFilter | 动态过滤字段(需配合 FilterProvider 使用)。 | @JsonFilter("userFilter") public class User { ... } |
@JsonEnumDefaultValue | 反序列化时未匹配到枚举值时使用默认值。 | @JsonEnumDefaultValue UNKNOWN |
@JsonValue | 指定枚举序列化时使用的值(通常用于字段或方法)。 | @JsonValue public String getCode() { return code; } |
@JsonAnyGetter | 将 Map 字段的键值对展开为 JSON 对象的属性。 | @JsonAnyGetter public Map<String, Object> getProperties() { ... } |
@JsonAnySetter | 将 JSON 中未知字段收集到 Map 中。 | @JsonAnySetter public void setProperty(String key, Object value) |
@JsonRootName | 定义 JSON 根对象的名称。 | @JsonRootName("response") public class ApiResponse { ... } |
分类说明
-
字段控制
-
@JsonProperty
、@JsonIgnore
、@JsonInclude
、@JsonAlias
:用于字段名映射、可见性控制。
-
-
时间/格式处理
-
@JsonFormat
、@JsonSerialize
、@JsonDeserialize
:处理日期、时间、数字格式或自定义转换。
-
-
多态类型
-
@JsonTypeInfo
、@JsonSubTypes
、@JsonTypeName
:支持继承结构的序列化与反序列化。
-
-
集合与结构
-
@JsonUnwrapped
、@JsonCreator
、@JsonIdentityInfo
:处理嵌套对象、循环引用。
-
-
动态过滤
-
@JsonView
、@JsonFilter
:按需展示字段或动态过滤。
-
-
枚举处理
-
@JsonEnumDefaultValue
、@JsonValue
:定义枚举默认值和序列化行为。
-
-
高级操作
-
@JsonAnyGetter
、@JsonAnySetter
、@JsonRootName
:处理动态字段或定义根对象名称。
-
个别注解使用实例
由于他的注解实在是很多所以我这里只挑一些很主要的注解来讲解,由于是加在类和属性上面的,所以我重新定义了一个类来写例子
@JsonProperty序列化自定义字段名
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PeopleWithAnnotation {
@JsonProperty("people_id")//(在序列化的时候将属性名称换成指定的名称)
private Integer id;//id号
private String name;//姓名
private Integer age;//年龄
private String sex;//性别
}
测试代码:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.PeopleWithAnnotation;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test() throws JsonProcessingException {
PeopleWithAnnotation people = new PeopleWithAnnotation(20,"张三",19,"男");
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
System.out.println(s);
}
}
运行结果:
@JsonIgore序列化和反序列话的时候忽略该字段
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PeopleWithAnnotation {
@JsonIgnore
private Integer id;//id号
private String name;//姓名
private Integer age;//年龄
private String sex;//性别
}
测试代码:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.PeopleWithAnnotation;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test() throws JsonProcessingException {
PeopleWithAnnotation people = new PeopleWithAnnotation(20,"张三",19,"男");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
System.out.println(json);
String s = "{\"id\":999,\"name\":\"张三\",\"age\":19,\"sex\":\"男\"}";
PeopleWithAnnotation value = mapper.readValue(s, PeopleWithAnnotation.class);
System.out.println(value);
}
}
我们忽略了id字段事实证明他确实在序列化和反序列化的时候是忽略了
@JsonInclude 属性序列化时何时被包含
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PeopleWithAnnotation {
@JsonInclude(value = JsonInclude.Include.NON_NULL)//不为null的时候序列化
private Integer id;//id号
@JsonInclude(value = JsonInclude.Include.NON_DEFAULT)//不为默认值的时候序列化
private String name;//姓名
private Integer age;//年龄
@JsonInclude(value = JsonInclude.Include.NON_EMPTY)//不为空的时候序列化
private String sex;//性别
}
实例代码:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.part1.Entity.People;
import com.part1.Entity.PeopleWithAnnotation;
import com.part1.Utils.PeopleDeserialize;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test10() throws JsonProcessingException {
PeopleWithAnnotation people = new PeopleWithAnnotation(null,null,20,"");
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(people);
System.out.println(s);
}
}
输出结果
注意在封装类数据里面0和null都是默认值,而其他类型默认值也是null
@JsonFormat 时间格式化处理或者数字格式处理
核心属性
属性 | 类型 | 作用 | 示例 |
---|---|---|---|
shape | JsonFormat.Shape | 指定值的序列化形状(如字符串、数字)。 | shape = Shape.STRING |
pattern | String | 自定义格式(日期/时间遵循 SimpleDateFormat ,数字遵循 DecimalFormat )。 | pattern = "yyyy-MM-dd HH:mm:ss" |
timezone | String 或 TimeZone | 指定时区(避免时区偏移问题)。 | timezone = "Asia/Shanghai" |
locale | String 或 Locale | 指定地区(影响日期、数字的本地化格式)。 | locale = "zh_CN" |
with | JsonFormat.Feature[] | 启用特定格式化特性(如将空值序列化为特定占位符)。 | with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY |
without | JsonFormat.Feature[] | 禁用特定格式化特性。 |
在没有使用@JsonForamt注解之前我们目前常用的时间日期对象就是三个
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PeopleWithAnnotation {
private LocalDate localDate;
private Date date;
private LocalDateTime localDateTime;
}
我们现在实例化一个对象来看看他们原本是什么样子
事实证明他们的格式各不相同,现在我们通过JsonFormat来设置他们的格式
注意在处理LocalDate和LocalDateTime的时候他们都是java8通过的而JsonFormat是不支持java8时间序列化的,所以要下载额外的依赖
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.x.x</version> <!-- 确保使用与你的 Jackson 版本相匹配的版本 -->
</dependency>
然后再在ObjectMapper里面注册JavaTimeMoudle模块
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
就能使用了,我这里就不下了只做演示所以我只观察Date的代码
结果输出
@Serialize 自定义序列化
跟我们前面自定义序列化器的是差不多,也要通过一个自定义的序列化器但是这里就会很方便
但是注意我们这里定义的序列化器和反序列化器在继承Stdserializer类里面的泛型要跟我们在加@Serialize注解的类是一样的,意思是我们加注解的类的序列化器是属于他自己的不能公用
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.part1.Utils.PeopleSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonSerialize(using = PeopleSerializer.class)
public class People {
private Integer id;//id号
private String name;//姓名
private Integer age;//年龄
private String sex;//性别
}
这里我用上面设置好了People的序列化器来用
最后创建一个实例,输出结果
@JsonDeserialize 指定反序列化器
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.part1.Utils.PeopleDeserialize;
import com.part1.Utils.PeopleSerializer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonSerialize(using = PeopleSerializer.class)
@JsonDeserialize(using = PeopleDeserialize.class)
public class People {
private Integer id;//id号
private String name;//姓名
private Integer age;//年龄
private String sex;//性别
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.part1.Entity.People;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test() throws JsonProcessingException {
String json = "{\n" +
" \"id\" : 10,\n" +
" \"name\" : \"张三\",\n" +
" \"age\" : 20,\n" +
" \"sex\" : \"男\"\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
People people = mapper.readValue(json, People.class);
System.out.println(people);
}
}
话不多说直接上结果
事实证明成功了
日期对象序列化时自定义格式的两种方法
首先引入依赖
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.3</version>
</dependency>//这里使用跟jackson一样的版本
基于注解方式
我们在对应的对象里面可以指定我们的时间日期对象格式化的时候要格式化成什么样子的
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TimeEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm",timezone = "GMT+8")
private LocalDateTime localDateTime;
@JsonFormat(pattern = "HH:mm",timezone = "GMT+8")
private LocalTime localTime;
@JsonFormat(pattern = "yyyy/MM/dd HH:mm",timezone = "GMT+8")
private Date date;
}
这里pattern是指定序列化的时候我们要将器序列化的格式,注意LocalTime是当天的时间没有年月日,所以指定格式的时候只能使用时分秒,这里我还建议设置一下时效区,因为可以我们不设置这个时效区timezone的话能发现Date的时间跟我们的本地的时间是对不上的,所以要设置时效区
还有我们可以用shape参数指定在序列化的时候是将对应的日期数据序列化为字符串类型@JsonFormat(shape = JsonFormat.Shape.STRING)
测试代码
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.part1.Entity.TimeEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test2() throws JsonProcessingException {
TimeEntity timeEntity = new TimeEntity(LocalDateTime.now(), LocalTime.now(),new Date());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(timeEntity);
System.out.println(timeEntity);
System.out.println(s);
}
}
我们在对LocalDate和LocalDateTime序列化使用的时候要先注册一下JavaTimeModule在解析器里面。否则会报错
查看运行结果
基于模块注册方式
我们先将之前实例里面的注解删除掉,然后修改一下里面的字段
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TimeEntity {
private LocalDateTime localDateTime;
private LocalTime localTime;
private LocalDate localDate;
}
运行代码
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.part1.Entity.TimeEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test() throws JsonProcessingException {
TimeEntity timeEntity = new TimeEntity(LocalDateTime.now(), LocalTime.now(), LocalDate.now());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
SimpleModule module = new SimpleModule();
module.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")))
.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
mapper.registerModule(module);
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(timeEntity);
System.out.println(s);
}
}
LocalDateSerializer,LocalDateTimeSerializer等这些是jsr310包中提供的设置对应日期类型的序列化器,他里面传入的是一个DateTimeFormatter对象,且ofpattern里面指定的是序列化的时候我们要序列化成的格式
运行结果:
当我们删除掉最后一个序列化器的添加能发现结果变了,他变成了一个数组形式的,其他的也是一样,当我们把这个序列化器去掉后就变成了一个数组
那么当我们不想要这个时间戳格式或者数组格式的日期的时候,我们就可以通过一行代码设置
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
实例代码,此时我已经将序列化器的注册给注释掉了
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test() throws JsonProcessingException {
TimeEntity timeEntity = new TimeEntity(LocalDateTime.now(), LocalTime.now(), LocalDate.now());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
SimpleModule module = new SimpleModule();
// module.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
// .addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")))
// .addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModule(module);
String s = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(timeEntity);
System.out.println(s);
}
}
运行结果:
日期对象反序列化的方法
注解方式
@JsonFormat注解可以使用于序列化的时候,他是在pattern里面指定的序列化后日期的格式,那么在反序列化的时候这个格式就代表了我们反序列化的时候这个json字符串里面的这个日期的格式,他是逆用的这里就不给出对应的示例了
添加官方定义的反序列化器
跟上面序列化的时候是一样的
话不多说直接看例子
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.part1.Entity.TimeEntity;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
@SpringBootTest
public class JacksonTestWithAnnotation {
@Test
public void test2() throws JsonProcessingException {
String s = "{\n" +
" \"localDateTime\" : \"2025/04/30 13:20:10\",\n" +
" \"localTime\" : \"14/50/47\",\n" +
" \"localDate\" : \"2025/04/30\"\n" +
"}";
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
SimpleModule module = new SimpleModule();
module.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")));//这里指定的是json字符串里面的格式
module.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH/mm/ss")));
module.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy/MM/dd")));
mapper.registerModule(module);
TimeEntity timeEntity = mapper.readValue(s, TimeEntity.class);
System.out.println(timeEntity);
}
}
注意文明封装的时候给定的json字符串对于LocalDateTime这个对象来说,这个json字符串里面对应的数据是要有时分秒的即要有全部的格式,否则在封装的时候一样会跑异常,指出缺少时分秒的数据。
输出结果
以上就是部分注解的使用了,其他的大差不差,主要是jackson这部分的方法和类太多了,给我整麻木了,其他的注解看看意思网上找找例子就能用了
最后
本人的第九篇博客,以此来记录我的后端java学习。如文章中有什么问题请指出,非常感谢!!!