序列化 之 Jackson(三)springmvc特定类型处理

在 Spring MVC 中,若需对特定 Java 类型(如 LocalDateTime、自定义枚举、敏感字段等)实现自定义的 JSON 序列化(Java → JSON)和反序列化(JSON → Java)逻辑,可通过 Jackson 的 JsonSerializerJsonDeserializer 实现,并结合 Spring 的配置机制全局生效。

下面从原理、步骤到完整示例进行详细说明。


一、核心原理

Jackson 使用 模块化注册机制 来扩展其序列化/反序列化能力:

  • JsonSerializer<T>:自定义如何将类型 T 转为 JSON。
  • JsonDeserializer<T>:自定义如何从 JSON 构造类型 T
  • SimpleModule:将自定义的序列化器/反序列化器注册到 ObjectMapper
  • Spring 集成:通过配置 ObjectMapper Bean 或 WebMvcConfigurer,使自定义逻辑全局生效。

二、使用场景举例

  1. LocalDateTime 统一格式化为 "yyyy-MM-dd HH:mm:ss"
  2. 对手机号中间四位脱敏(如 138****1234)。
  3. 自定义枚举的 JSON 表示(不用 name(),而用 code 字段)。
  4. 处理前端传来的字符串时间戳转为 Date

三、完整示例:自定义 LocalDateTime 序列化/反序列化

3.1 定义序列化器(Java → JSON)

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value != null) {
            gen.writeString(formatter.format(value));
        } else {
            gen.writeNull();
        }
    }
}

3.2 定义反序列化器(JSON → Java)

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String value = p.getText();
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        return LocalDateTime.parse(value, formatter);
    }
}

四、注册到 ObjectMapper(Spring Boot 方式)

方法一:通过 @Bean 配置(推荐)

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // 注册 JavaTimeModule 以支持 JSR310(可选,但建议保留)
        mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());

        // 创建自定义模块
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());

        mapper.registerModule(module);

        // 可选:关闭时间戳格式(避免与自定义冲突)
        mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        return mapper;
    }
}

⚠️ 注意:@Primary 注解通常不需要,因为 Spring Boot 默认会使用你定义的 ObjectMapper Bean。


五、验证效果

Controller 示例

@RestController
public class TestController {

    @GetMapping("/time")
    public TimeResponse getTime() {
        return new TimeResponse(LocalDateTime.now());
    }

    @PostMapping("/time")
    public String setTime(@RequestBody TimeResponse request) {
        System.out.println("接收到时间: " + request.getCreateTime());
        return "OK";
    }
}

class TimeResponse {
    private LocalDateTime createTime;

    public TimeResponse(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    // getter/setter
    public LocalDateTime getCreateTime() { return createTime; }
    public void setCreateTime(LocalDateTime createTime) { this.createTime = createTime; }
}

请求/响应示例

GET /time

{
  "createTime": "2026-01-23 16:30:45"
}

POST /time

{
  "createTime": "2026-01-23 16:30:45"
}

→ 后端能正确解析为 LocalDateTime 对象。


六、其他类型示例:手机号脱敏序列化

6.1 自定义序列化器(仅序列化,不反序列化)

public class PhoneSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String phone, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (phone != null && phone.length() == 11) {
            String masked = phone.substring(0, 3) + "****" + phone.substring(7);
            gen.writeString(masked);
        } else {
            gen.writeString(phone);
        }
    }
}

6.2 在实体类上使用(局部生效)

public class User {
    private String name;

    @JsonSerialize(using = PhoneSerializer.class)
    private String phone;

    // getter/setter
}

若想全局生效所有 String 类型手机号字段,则需更复杂的逻辑(如字段命名规则 + 自定义 BeanSerializerModifier),一般建议局部注解。


七、高级:基于字段名或注解的通用脱敏(可选)

若需更灵活的脱敏策略(如 @Sensitive(type = MOBILE)),可结合:

  • 自定义注解 @Sensitive
  • 自定义 BeanSerializerModifier
  • PropertyFilterJsonSerializer 动态判断

八、注意事项

  1. 不要重复注册:避免既用 JavaTimeModule 又手动注册 LocalDateTime,可能导致冲突。
  2. 线程安全JsonSerializer/JsonDeserializer 必须是无状态的(不要持有可变成员变量)。
  3. 异常处理:在 deserialize() 中抛出 IOExceptionJsonProcessingException,Spring 会将其转为 400 错误。
  4. Spring Boot 自动配置:如果你只重写了 ObjectMapper Bean,Spring Boot 会自动将其用于 MappingJackson2HttpMessageConverter

九、总结

步骤 操作
1 编写 JsonSerializer<T> 和/或 JsonDeserializer<T>
2 通过 SimpleModule 注册到 ObjectMapper
3 在 Spring 配置中提供自定义 ObjectMapper Bean
4 (可选)在字段上使用 @JsonSerialize/@JsonDeserialize 局部覆盖
posted @ 2026-01-23 16:26  蓝迷梦  阅读(0)  评论(0)    收藏  举报