buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

Spring MVC中枚举序列化-如何处理VO中的枚举字段?

anyway,枚举是Java中表达有限状态的利器。使用枚举不仅可以增强类型安全,同时也会极大提升代码的可读性和可维护性。

在 Spring MVC 项目中,当使用 Jackson 序列化枚举类型时,默认行为是序列化枚举的 name() 值(即枚举常量的名称)。但在实际开发中,我们通常需要更灵活的控制。以下是详细分析和解决方案:


1. Jackson 默认枚举序列化行为

public enum EnterpriseAuthStatusEnum {
    PENDING,   // 序列化为 "PENDING"
    APPROVED,  // 序列化为 "APPROVED"
    REJECTED   // 序列化为 "REJECTED"
}

EnterpriseAuthVO { Long enterpriseId; EnterpriseAuthStatusEnum authStatus; } 对象被序列化为 JSON 时:

{
  "enterpriseId": 12345,
  "authStatus": "PENDING"  // 默认输出枚举的 name() 值
}

2. 常见需求与解决方案

需求 1:返回枚举的说明文字(而非常量名)

@JsonFormat(shape = JsonFormat.Shape.OBJECT) // 关键注解
public enum EnterpriseAuthStatusEnum {
    PENDING(0, "待审核"),
    APPROVED(1, "已通过"),
    REJECTED(2, "已拒绝");
    
    private final int code;
    private final String description;
    
    // 构造方法、getter(必须添加 getter 方法)
}

序列化结果:

{
  "enterpriseId": 12345,
  "authStatus": {
    "code": 0,
    "description": "待审核"
  }
}

需求 2:返回自定义值(如 code)

public enum EnterpriseAuthStatusEnum {
    PENDING(0),
    APPROVED(1),
    REJECTED(2);
    
    private final int code;
    
    EnterpriseAuthStatusEnum(int code) {
        this.code = code;
    }
    
    @JsonValue // 关键注解:标记作为序列化的值
    public int getCode() {
        return code;
    }
}

序列化结果:

{
  "enterpriseId": 12345,
  "authStatus": 0  // 直接输出 code 值
}

需求 3:根据不同场景返回不同值

public enum EnterpriseAuthStatusEnum {
    PENDING(0, "pending"),
    APPROVED(1, "approved"),
    REJECTED(2, "rejected");
    
    private final int code;
    private final String text;
    
    EnterpriseAuthStatusEnum(int code, String text) {
        this.code = code;
        this.text = text;
    }
    
    // 自定义序列化器
    @JsonComponent
    public static class Serializer extends StdSerializer<EnterpriseAuthStatusEnum> {
        public Serializer() {
            super(EnterpriseAuthStatusEnum.class);
        }
        
        @Override
        public void serialize(
            EnterpriseAuthStatusEnum value,
            JsonGenerator gen,
            SerializerProvider provider
        ) throws IOException {
            // 根据业务需求选择输出字段
            gen.writeString(value.text); 
            // 或 gen.writeNumber(value.code);
        }
    }
}

3. 全局配置方案(改变所有枚举行为)

application.yml 中配置:

spring:
  jackson:
    serialization:
      WRITE_ENUMS_USING_TO_STRING: true  # 使用 toString() 而非 name()

或通过 Java 配置:

@Configuration
public class JacksonConfig {
    
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer enumCustomizer() {
        return builder -> {
            // 方案1: 使用 toString()
            builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
            
            // 方案2: 使用索引值
            // builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
        };
    }
}

4. 最佳实践建议:返回带说明的完整对象(推荐)

@Data
public class EnumDTO {
    private String name;
    private int code;
    private String description;
}

// 在VO中转换
public class EnterpriseAuthVO {
    private Long enterpriseId;
    
    @JsonIgnore // 隐藏原始枚举
    private EnterpriseAuthStatusEnum authStatus;
    
    @ApiModelProperty("客户开户状态")
    public EnumDTO getauthStatusInfo() {
        if (authStatus == null) return null;
        return new EnumDTO(
            authStatus.name(),
            authStatus.getCode(),
            authStatus.getDescription()
        );
    }
}

序列化结果:

{
  "enterpriseId": 12345,
  "authStatusInfo": {
    "name": "PENDING",
    "code": 0,
    "description": "待审核"
  }
}

5. 完整示例代码

// 枚举定义
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum EnterpriseAuthStatusEnum {
    PENDING(0, "待审核"),
    APPROVED(1, "已通过"),
    REJECTED(2, "已拒绝");
    
    private final int code;
    private final String description;
    
    // 构造方法、getter
}

// VO对象
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class EnterpriseAuthVO implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @ApiModelProperty("企业id")
    private Long enterpriseId;
    
    @ApiModelProperty("客户开户状态")
    private EnterpriseAuthStatusEnum authStatus;
}

// 控制器
@RestController
public class EnterpriseController {
    
    @GetMapping("/auth/status")
    public EnterpriseAuthVO getAuthStatus() {
        return new EnterpriseAuthVO()
            .setEnterpriseId(10001L)
            .setAuthStatus(EnterpriseAuthStatusEnum.PENDING);
    }
}

请求响应:

{
  "enterpriseId": 10001,
  "authStatus": {
    "code": 0,
    "description": "待审核"
  }
}

关键总结

需求场景 实现方案 序列化结果示例
默认行为 无配置 "PENDING"
返回数字编码 @JsonValue + getCode() 0
返回完整对象 @JsonFormat(shape = OBJECT) {"code":0, "desc":"待审核"}
全局使用 toString() WRITE_ENUMS_USING_TO_STRING "待审核"
自定义序列化逻辑 实现 JsonSerializer 任意自定义格式

生产环境推荐:使用 @JsonFormat(shape = OBJECT) 返回完整枚举信息,前端可直接展示 description 字段,同时保留 code 用于逻辑判断。

posted on 2025-06-16 14:21  buguge  阅读(94)  评论(0)    收藏  举报