🌟 优雅的枚举处理方案:代码转换与验证
🎯 核心功能
这套工具提供两大核心能力:
- 枚举代码转名称:将存储的枚举代码转换为友好的展示名称
- 枚举值验证:确保传入的值在枚举定义范围内
💎 核心组件
1. 枚举转换注解 @EnumCodeToName
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = EnumCodeToNameSerializer.class)
public @interface EnumCodeToName {
Class<? extends Enum<?>> enumType(); // 枚举类型
String codeField() default "code"; // 代码字段名
String nameField() default "chineseName"; // 名称字段名
boolean useDefaultIfNotMatched() default true; // 是否使用默认值
}
2. 枚举验证注解 @EnumValid
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValidator.class)
public @interface EnumValid {
String message() default "枚举值非法";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
Class<? extends Enum<?>> enumType(); // 枚举类型
String codeField() default "code"; // 代码字段名
boolean nullable() default false; // 是否允许null
}
3. 枚举序列化器 EnumCodeToNameSerializer
public class EnumCodeToNameSerializer extends JsonSerializer<String> {
@Override
public void serialize(String code, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (code == null) {
gen.writeNull();
return;
}
try {
Object currentObj = gen.getCurrentValue();
String fieldName = gen.getOutputContext().getCurrentName();
Field field = currentObj.getClass().getDeclaredField(fieldName);
EnumCodeToName annotation = field.getAnnotation(EnumCodeToName.class);
if (annotation == null) {
gen.writeString(code);
return;
}
Object result = getValueNameByCode(
annotation.enumType(),
code.trim(),
annotation.codeField(),
annotation.nameField(),
annotation.useDefaultIfNotMatched()
);
gen.writeString(result != null ? result.toString() : code);
} catch (NoSuchFieldException e) {
throw new IOException("序列化失败: 字段不存在", e);
} catch (Exception e) {
throw new IOException("枚举序列化失败: " + e.getMessage(), e);
}
}
private Object getValueNameByCode(Class<? extends Enum<?>> enumClass, String code,
String codeField, String nameField, boolean useDefault) {
try {
// 查找匹配的枚举值
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
Field codeF = enumClass.getDeclaredField(codeField);
codeF.setAccessible(true);
if (code.equals(String.valueOf(codeF.get(enumConstant)))) {
Field nameF = enumClass.getDeclaredField(nameField);
nameF.setAccessible(true);
return nameF.get(enumConstant);
}
}
// 使用默认值
if (useDefault) {
Method getDefault = enumClass.getMethod("getDefault");
Enum<?> defaultEnum = (Enum<?>) getDefault.invoke(null);
Field nameF = enumClass.getDeclaredField(nameField);
nameF.setAccessible(true);
return nameF.get(defaultEnum);
}
} catch (Exception ignored) {}
return null;
}
}
4. 枚举验证器 EnumValidator
public class EnumValidator implements ConstraintValidator<EnumValid, String> {
private Class<? extends Enum<?>> enumType;
private String codeField;
private boolean nullable;
@Override
public void initialize(EnumValid constraintAnnotation) {
this.enumType = constraintAnnotation.enumType();
this.codeField = constraintAnnotation.codeField();
this.nullable = constraintAnnotation.nullable();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) return nullable;
try {
String trimmed = value.trim();
for (Enum<?> enumConstant : enumType.getEnumConstants()) {
Field field = enumType.getDeclaredField(codeField);
field.setAccessible(true);
if (trimmed.equals(String.valueOf(field.get(enumConstant)))) {
return true;
}
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
"无效的枚举值: " + value + ",可选值: " + getValidValues()
).addConstraintViolation();
} catch (Exception e) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
"验证枚举值时发生系统错误"
).addConstraintViolation();
}
return false;
}
private String getValidValues() {
try {
return Arrays.stream(enumType.getEnumConstants())
.map(e -> {
try {
Field f = enumType.getDeclaredField(codeField);
f.setAccessible(true);
return String.valueOf(f.get(e));
} catch (Exception ex) {
return "";
}
})
.filter(s -> !s.isEmpty())
.collect(Collectors.joining(", "));
} catch (Exception e) {
return "无法获取有效值列表";
}
}
}
🚀 使用示例
1. 定义枚举
public enum OrderStatus {
CREATED("C", "已创建"),
PAID("P", "已支付"),
SHIPPED("S", "已发货"),
COMPLETED("F", "已完成");
private final String code;
private final String desc;
OrderStatus(String code, String desc) {
this.code = code;
this.desc = desc;
}
public static OrderStatus getDefault() {
return CREATED;
}
}
2. 在DTO中使用
public class OrderDTO {
@EnumValid(
enumType = OrderStatus.class,
message = "无效的订单状态"
)
private String statusCode;
@EnumCodeToName(
enumType = OrderStatus.class,
codeField = "code",
nameField = "desc"
)
private String statusDisplay;
// getters/setters
}
3. 控制器示例
@RestController
@RequestMapping("/orders")
public class OrderController {
@PostMapping
public ResponseEntity<?> createOrder(@Valid @RequestBody OrderDTO orderDTO) {
// 业务逻辑
return ResponseEntity.ok(orderDTO);
}
}
4. 效果展示
请求:
{
"statusCode": "P",
"statusDisplay": "P"
}
响应:
{
"statusCode": "P",
"statusDisplay": "已支付"
}
错误示例:
{
"statusCode": "X",
"statusDisplay": "X"
}
错误响应:
{
"timestamp": "2023-08-20T10:00:00",
"status": 400,
"error": "Bad Request",
"message": "无效的订单状态: X,可选值: C, P, S, F",
"path": "/orders"
}
🌈 特性总结
- 自动转换:存储代码,展示友好名称
- 严格验证:确保数据合法性
- 灵活配置:可自定义字段名
- 容错处理:找不到匹配时使用默认值
- 友好提示:验证失败时显示有效值列表
这套工具让枚举处理变得简单而强大!🎉
评论区