叟减 发表于 2025-6-16 13:07:41

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

枚举是Java中表达有限状态的利器。使用枚举不仅可以增强类型安全,同时也会极大提升代码的可读性和可维护性。
在 Spring MVC 项目中,当使用 Jackson 序列化枚举类型时,默认行为是序列化枚举的 name() 值(即枚举常量的名称)。但在实际开发中,我们通常需要更灵活的控制。以下是详细分析和解决方案:
1. Jackson 默认枚举序列化行为

public enum EnterpriseAuthStatusEnum {
    PENDING,   // 序列化为 "PENDING"
    APPROVED,// 序列化为 "APPROVED"
    REJECTED   // 序列化为 "REJECTED"
}当 EnterpriseAuthStatusVO 对象被序列化为 JSON 时:
{
"enterpriseId": 12345,
"kaiStatus": "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;
   
    EnterpriseAuthStatusEnum(int code, String description) {
      this.code = code;
      this.description = description;
    }
   
    // 必须添加 getter
    public int getCode() {
      return code;
    }
   
    public String getDescription() {
      return description;
    }
}序列化结果:
{
"enterpriseId": 12345,
"kaiStatus": {
    "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,
"kaiStatus": 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 EnterpriseAuthStatusVO {
    private Long enterpriseId;
   
    @JsonIgnore // 隐藏原始枚举
    private EnterpriseAuthStatusEnum kaiStatus;
   
    @ApiModelProperty("客户开户状态")
    public EnumDTO getKaiStatusInfo() {
      if (kaiStatus == null) return null;
      return new EnumDTO(
            kaiStatus.name(),
            kaiStatus.getCode(),
            kaiStatus.getDescription()
      );
    }
}序列化结果:
{
"enterpriseId": 12345,
"kaiStatusInfo": {
    "name": "PENDING",
    "code": 0,
    "description": "待审核"
}
}方案二:使用统一响应包装器

public class ApiResponse<T> {
    private int code;
    private String message;
    private T data;
   
    // 成功响应时附加枚举元数据
    public static <T> ApiResponse<T> success(T data, EnterpriseAuthStatusEnum status) {
      ApiResponse<T> response = new ApiResponse<>();
      response.code = 200;
      response.message = status.getDescription();
      response.data = data;
      return response;
    }
}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 EnterpriseAuthStatusVO implements Serializable {
    private static final long serialVersionUID = 1L;
   
    @ApiModelProperty("企业id")
    private Long enterpriseId;
   
    @ApiModelProperty("客户开户状态")
    private EnterpriseAuthStatusEnum kaiStatus;
}

// 控制器
@RestController
public class EnterpriseController {
   
    @GetMapping("/auth/status")
    public EnterpriseAuthStatusVO getAuthStatus() {
      return new EnterpriseAuthStatusVO()
            .setEnterpriseId(10001L)
            .setKaiStatus(EnterpriseAuthStatusEnum.PENDING);
    }
}请求响应:
{
"enterpriseId": 10001,
"kaiStatus": {
    "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 用于逻辑判断。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Spring MVC中枚举序列化-如何处理VO中的枚举字段?