Jackson 是一个 Java 的 JSON 处理库,广泛用于对象与 JSON 之间的转换(序列化和反序列化)。Spring Boot 默认集成了 Jackson,并将其作为 spring-boot-starter-web 依赖的一部分来处理 JSON 数据。
以下是序列化和反序列化的应用场景
| 应用场景 | 序列化 | 反序列化 |
|---|---|---|
| Spring Boot API 返回 JSON 响应 | Java 对象 → JSON | 前端请求 JSON → Java 对象 |
| 数据库存储 JSON | Java 对象 → JSON 存储 | 读取 JSON → Java 对象 |
| Redis 缓存 | Java 对象 → JSON 存入 Redis | 取出 JSON → Java 对象 |
| 消息队列(MQ) | Java 对象 → JSON 发送 | 监听 JSON → Java 对象 |
在 Spring Boot 中,定义 @Bean ObjectMapper 会 覆盖默认的 JSON 配置,影响 @RestController 返回值、@RequestBody 解析、以及 @Autowired ObjectMapper 注入。这样可以 统一全局 JSON 格式(如时间格式、属性命名)并 修改 Jackson 默认行为,确保应用中的 JSON 处理符合需求。如果不定义,Spring Boot 会使用默认 ObjectMapper,但无法定制其行为。
package local.ateng.java.serialize.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
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.ToStringSerializer;
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.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* Jackson 使用 ObjectMapper 序列化和反序列化配置
*
* @author 孔余
* @email 2385569970@qq.com
* @since 2025-03-05
*/
@Configuration
public class JacksonConfig {
// 日期与时间格式化
public static String DEFAULT_TIME_ZONE = "Asia/Shanghai";
public static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 自定义 Jackson 时间日期的序列化和反序列化规则
*
* @param objectMapper Jackson 的 ObjectMapper 实例
*/
public static void customizeJsonDateTime(ObjectMapper objectMapper, String timeZone,String dateFormat, String dateTimeFormat) {
// 设置全局时区,确保 Date 类型数据使用此时区
objectMapper.setTimeZone(TimeZone.getTimeZone(timeZone));
// 关闭默认时间戳序列化,改为标准格式
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 避免与 JavaTimeModule 冲突
objectMapper.setDateFormat(new SimpleDateFormat(dateTimeFormat));
// Java 8 时间模块
JavaTimeModule javaTimeModule = new JavaTimeModule();
// LocalDateTime 序列化 & 反序列化
javaTimeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
// LocalDate 序列化 & 反序列化
javaTimeModule.addSerializer(LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
javaTimeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
// 注册 JavaTimeModule
objectMapper.registerModule(javaTimeModule);
}
/**
* 自定义 Jackson 序列化规则
*
* @param objectMapper Jackson 的 ObjectMapper 实例
*/
public static void customizeJsonSerialization(ObjectMapper objectMapper) {
// 关闭 JSON 美化输出(生产环境建议关闭,提高性能)
objectMapper.disable(SerializationFeature.INDENT_OUTPUT);
// 避免 "No serializer found for class" 异常
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 过滤 null 值,减少 JSON 体积
//objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 过滤空集合、空字符串等无效数据,进一步精简 JSON
//objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
// 不过滤 null、空集合、空字符串等无效数据值,保持数据的原始状态
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
// 枚举类型:使用 `toString()` 方式序列化,而不是默认的 `name()`
objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
// BigDecimal 序列化时不使用科学计数法,确保数据精确
objectMapper.enable(SerializationFeature.WRITE_BIGDECIMAL_AS_PLAIN);
// 排序字段名,保证 JSON 输出的键顺序固定(有助于缓存和数据比对)
objectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
// 将 Long 和 BigInteger 序列化为字符串,防止 JavaScript 丢失精度
SimpleModule simpleModule = new SimpleModule();
ToStringSerializer stringSerializer = ToStringSerializer.instance;
simpleModule.addSerializer(BigDecimal.class, stringSerializer);
simpleModule.addSerializer(BigInteger.class, stringSerializer);
simpleModule.addSerializer(Long.class, stringSerializer);
simpleModule.addSerializer(Long.TYPE, stringSerializer);
objectMapper.registerModule(simpleModule);
}
/**
* 自定义 Jackson 反序列化规则
*
* @param objectMapper Jackson 的 ObjectMapper 实例
*/
public static void customizeJsonDeserialization(ObjectMapper objectMapper) {
// 允许单个值转数组(例如 1 -> [1])
objectMapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
// 忽略未知字段(避免因缺少字段报错,提升兼容性)
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 处理 原始类型(如 int, long, boolean 等)在反序列化时如果遇到 null 值将其替换为默认值,而不是抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
// 使用 BigDecimal 反序列化浮点数,避免精度丢失
objectMapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
// 使用枚举的 `toString()` 方法进行反序列化,而不是默认的 `name()`
objectMapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
// 允许特殊字符转义
objectMapper.enable(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS);
objectMapper.enable(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER);
// 错误时提供类型检查,增强反序列化稳定性
objectMapper.enable(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE);
}
/**
* 自定义 Jackson JSON 解析设置
*
* @param objectMapper Jackson 的 ObjectMapper 实例
*/
public static void customizeJsonParsing(ObjectMapper objectMapper) {
// 允许 JSON 中带注释,方便开发阶段使用
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
// 允许字段名不带引号(可处理某些特殊格式的 JSON)
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 允许单引号作为 JSON 字符串的定界符(适用于某些特殊格式)
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 允许控制字符的转义(例如,`\n` 或 `\t`)
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
// 允许反斜杠转义任何字符(如:`\\`)
objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
// 允许无效的 UTF-8 字符(如果 JSON 编码不完全符合标准)
objectMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
// 允许 JSON 中无序字段(通常是为了性能优化)
objectMapper.configure(JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS, true);
}
/**
* 配置并返回一个定制化的 ObjectMapper 实例。
* 该方法配置了日期时间格式、JSON 序列化、反序列化和解析等相关设置。
* 通过调用不同的定制方法,确保 ObjectMapper 在各种场景下都能正确处理数据。
*
* @return 配置完成的 ObjectMapper 实例
*/
@Bean
public ObjectMapper objectMapper() {
// 创建 ObjectMapper 实例
ObjectMapper objectMapper = new ObjectMapper();
// 配置日期和时间的序列化与反序列化
customizeJsonDateTime(objectMapper, DEFAULT_TIME_ZONE,DEFAULT_DATE_FORMAT,DEFAULT_DATE_TIME_FORMAT);
// 配置 JSON 序列化相关设置
customizeJsonSerialization(objectMapper);
// 配置 JSON 反序列化相关设置
customizeJsonDeserialization(objectMapper);
// 配置 JSON 解析相关设置
customizeJsonParsing(objectMapper);
// 返回配置完成的 ObjectMapper 实例
return objectMapper;
}
}package local.ateng.java.serialize.controller;
import local.ateng.java.serialize.entity.MyUser;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@RestController
@RequestMapping("/jackson")
public class JacksonController {
// 序列化
@GetMapping("/serialize")
public MyUser serialize() {
return MyUser.builder()
.id(1L)
.name("ateng")
.age(25)
.phoneNumber("1762306666")
.email("kongyu2385569970@gmail.com")
.score(new BigDecimal("88.911"))
.ratio(0.7147)
.birthday(LocalDate.parse("2000-01-01"))
.province("重庆市")
.city("重庆市")
.createTime(LocalDateTime.now())
.build();
}
// 反序列化
@PostMapping("/deserialize")
public String deserialize(@RequestBody MyUser myUser) {
System.out.println(myUser);
return "ok";
}
}访问序列化接口
curl -X GET http://localhost:12014/jackson/serialize
示例输出:
{"id":"1","name":"ateng","age":25,"phoneNumber":"1762306666","email":"kongyu2385569970@gmail.com","score":"88.911","ratio":0.7147,"birthday":"2000-01-01","province":"重庆市","city":"重庆市","createTime":"2025-03-06 08:28:19","createTime2":null,"createTime3":null,"num":0,"list":null}访问反序列化接口
curl -X POST http://192.168.100.2:12014/jackson/deserialize \
-H "Content-Type: application/json" \
-d '{
"id": 1,
"name": "ateng",
"age": 25,
"phoneNumber": "1762306666",
"email": "kongyu2385569970@gmail.com",
"score": 88.911,
"ratio": 0.7147,
"birthday": "2000-01-01",
"province": "Chongqing",
"city": "Chongqing",
"createTime": "2025-03-05 14:30:00"
}'
控制台打印
MyUser(id=1, name=ateng, age=25, phoneNumber=1762306666, email=kongyu2385569970@gmail.com, score=88.911, ratio=0.7147, birthday=2000-01-01, province=Chongqing, city=Chongqing, createTime=2025-03-05T14:30, createTime2=null, createTime3=null, num=0, list=null)
在 Spring Data Redis 中,Jackson 主要用于将 Java 对象序列化为 JSON 存入 Redis,并在读取时反序列化回 Java 对象。由于 Redis 只能存储字符串或二进制数据,因此 RedisTemplate 需要配置合适的序列化器,如 Jackson2JsonRedisSerializer,以确保对象能正确存储和恢复。
package local.ateng.java.serialize.config;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* RedisTemplate 配置类
* <p>
* 该类负责配置 RedisTemplate,允许对象进行序列化和反序列化。
* 在这里,我们使用了 StringRedisSerializer 来序列化和反序列化 Redis 键,
* 使用 Jackson2JsonRedisSerializer 来序列化和反序列化 Redis 值,确保 Redis 能够存储 Java 对象。
* 另外,ObjectMapper 的配置确保 JSON 的格式和解析行为符合预期。
* </p>
*/
@Configuration
public class RedisTemplateConfig {
// 日期与时间格式化
public static String DEFAULT_TIME_ZONE = "Asia/Shanghai";
public static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSSSS";
/**
* 自定义 ObjectMapper 配置以启用默认类型标记。
* 该方法的作用是在 JSON 序列化和反序列化时包含类类型信息,
* 以便在反序列化时能够正确地识别对象的具体类型。
*
* @param objectMapper 要配置的 ObjectMapper 实例
*/
public static void customizeJsonClassType(ObjectMapper objectMapper) {
// 启用默认类型标记,使 JSON 中包含对象的类信息
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance, // 允许所有子类型的验证器(最宽松)
ObjectMapper.DefaultTyping.NON_FINAL, // 仅对非 final 类启用类型信息
JsonTypeInfo.As.PROPERTY // 以 JSON 属性的形式存储类型信息
);
}
/**
* 配置 RedisTemplate
* <p>
* 创建 RedisTemplate,并指定如何序列化和反序列化 Redis 中的键值。
* 该配置支持使用 Jackson2JsonRedisSerializer 序列化值,并使用 StringRedisSerializer 序列化键。
* </p>
*
* @param redisConnectionFactory Redis 连接工厂
* @return 配置好的 RedisTemplate
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 创建 RedisTemplate 实例
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory); // 设置连接工厂
// 使用 StringRedisSerializer 来序列化和反序列化 Redis 键
// Redis 键将被序列化为字符串类型
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer); // 设置键的序列化器
template.setHashKeySerializer(stringRedisSerializer); // 设置哈希键的序列化器
// 创建 ObjectMapper 实例,用于配置 Jackson 的序列化和反序列化行为
ObjectMapper objectMapper = new ObjectMapper();
JacksonConfig.customizeJsonDateTime(objectMapper, DEFAULT_TIME_ZONE, DEFAULT_DATE_FORMAT, DEFAULT_DATE_TIME_FORMAT);
JacksonConfig.customizeJsonSerialization(objectMapper);
JacksonConfig.customizeJsonDeserialization(objectMapper);
JacksonConfig.customizeJsonParsing(objectMapper);
customizeJsonClassType(objectMapper);
// 创建 Jackson2JsonRedisSerializer,用于序列化和反序列化值
// 该序列化器使用配置好的 ObjectMapper
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
// 设置 RedisTemplate 的值的序列化器
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer); // 设置哈希值的序列化器
// 返回配置好的 RedisTemplate
template.afterPropertiesSet();
return template;
}
}package local.ateng.java.serialize.controller;
import local.ateng.java.serialize.entity.MyUser;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/redis")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RedisController {
private final RedisTemplate<String, Object> redisTemplate;
// 序列化
@GetMapping("/serialize")
public String serialize() {
MyUser myUser = MyUser.builder()
.id(1L)
.name("ateng")
.age(null)
.phoneNumber("1762306666")
.email("kongyu2385569970@gmail.com")
.score(new BigDecimal("88.911"))
.ratio(0.7147)
.birthday(LocalDate.parse("2000-01-01"))
.province(null)
.city("重庆市")
.createTime(LocalDateTime.now())
.createTime2(new Date())
.list(List.of())
.build();
redisTemplate.opsForValue().set("myUser", myUser);
return "ok";
}
// 反序列化
@GetMapping("/deserialize")
public String deserialize() {
MyUser myUser = (MyUser) redisTemplate.opsForValue().get("myUser");
System.out.println(myUser);
System.out.println(myUser.getCreateTime());
return "ok";
}
}序列化到Redis
{
"@class": "local.ateng.java.serialize.entity.MyUser",
"id": "1",
"name": "ateng",
"age": null,
"phoneNumber": "1762306666",
"email": "kongyu2385569970@gmail.com",
"score": [
"java.math.BigDecimal",
"88.911"
],
"ratio": 0.7147,
"birthday": "2000-01-01",
"province": null,
"city": "重庆市",
"createTime": "2025-03-06 08:46:55.760579",
"createTime2": [
"java.util.Date",
"2025-03-06 08:46:55.000760"
],
"createTime3": null,
"num": 0,
"list": [
"java.util.ImmutableCollections$ListN",
[]
]
}反序列化输出
MyUser(id=1, name=ateng, age=null, phoneNumber=1762306666, email=kongyu2385569970@gmail.com, score=88.911, ratio=0.7147, birthday=2000-01-01, province=null, city=重庆市, createTime=2025-03-06T08:46:55.760579, createTime2=Thu Mar 06 08:46:55 CST 2025, createTime3=null, num=0, list=[])
package local.ateng.java.serialize.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class DefaultValueStringSerializer extends JsonSerializer {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeString("/");
} else {
gen.writeString(value + "~");
}
}
}
使用 @JsonSerialize 的 using 和 nullsUsing 指定自定义的序列化器,最终序列化后就可以实现自定义
@JsonSerialize(using = DefaultValueStringSerializer.class, nullsUsing = DefaultValueStringSerializer.class)
private String province;