「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
引言
Hello 大家好,这里是Anyin。
继上一篇 优雅的实现前端回显字典枚举值 我们"优雅"的实现了前端字典枚举值的回显,当数据库查询返回值是一个code值的时候,我们能够在spring mvc进行序列化的时候根据注解信息,自动把code值转换为一个包含text和code值的对象返回给前端,让前端方便的取到对应的text值进行页面渲染。
这是在查询的场景下,那在编辑或者新增的场景下会怎样呢?
在编辑或者新增的场景下,前端会给后端一个code值,而后端一般使用String字符串接收该枚举值,然后在业务代码把它转为对应的枚举对象,最后在进行对应的业务操作。转换的逻辑一般如下:
public static SexEnum get(String code){
for(SexEnum item : values()) {
if(code.equals(item.getCode())){
return item;
}
}
// 返回默认值或者抛出异常
return SexEnum.MAN;
}
这样子会有2个问题:
- 所有前端传递的枚举对象都需要该转换方法
- 前端传递的枚举值无法校验正确与否
思路
以上2个问题其实会有不同的解法,例如问题1,可以通过继承父类,抽象get方法,让所有的枚举类都继承该父类即可;问题2可以通过注解+AOP的形式去校验枚举值是否正确。
当然,这里我们分享下另外一个解法。在上篇我们通过序列化解决,那这里我们当然通过反序列化解决。在JSON字符串进行反序列化的时候我们会拿到当前枚举字段的Class对象,然后配合值从而取到具体的枚举对象,最后进行返回。
实现
其实整体代码很简单,我们实现一个StringAsDictDeserializer类,它继承了JsonDeserializer,其泛型为BaseEnum,这是我们定义所有枚举对象的抽象接口。最后,还需要实现下ContextualDeserializer接口,通过该接口才可以拿到当前枚举字段的Class对象。
代码如下:
@Slf4j
@JacksonStdImpl
public class StringAsDictDeserializer extends JsonDeserializer<BaseEnum> implements ContextualDeserializer {
public StringAsDictDeserializer(){}
private Class<?> clazz;
public StringAsDictDeserializer(Class<?> clazz){
this.clazz = clazz;
}
@Override
public BaseEnum deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
// 前端传递的值
String value = p.getValueAsString();
// 通过遍历,获取对应的枚举对象
BaseEnum type = Arrays.stream(clazz.getEnumConstants()).map(t -> (BaseEnum) t)
.filter(t -> Objects.equals(t.getCode(), value))
.findAny().orElse(null);
if(type == null){
throw new DictSerializerException("value is error, not found enum type");
}
return type;
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
// 自定义反序列化器
Class<?> typeClazz = property.getType().getRawClass();
return new StringAsDictDeserializer(typeClazz);
}
}
以上,有一个很重要的注解@JacksonStdImpl, 只有添加了该注解,ContextualDeserializer接口的createContextual方法才会执行,也只有这样子,我们才可以拿到BeanProperty对象,从而拿到目标枚举类的Class对象。
测试
接下来,我们来测试下我们的代码。在controller接收实体类中,我们添加上对应的注解配置,如下:
@Data
public static class TestForm{
@JsonDeserialize(using = StringAsDictDeserializer.class)
private SexEnum sex;
@JsonDeserialize(using = StringAsDictDeserializer.class)
private StatusEnum status;
}
使用postman的请求参数:
控制台打印结果:
最后
以上,我们实现后端接收字典枚举值,你学会了吗?
相关源码地址 Anyin Cloud