SpringBoot中Enum解析默認(rèn)使用的是EnumToStringConverter,默認(rèn)轉(zhuǎn)成枚舉的名稱。
響應(yīng)返回的JSON,Enum也默認(rèn)解析為name。
有時(shí)候不使用枚舉的name,而是value來進(jìn)行返回,參數(shù)解析。
- JSON序列化
可以通過在枚舉字段上加上@JsonValue來實(shí)現(xiàn),這樣生成的json就會(huì)是code值了。
@JsonCreator指定反序列化時(shí)使用的構(gòu)造方法,不指定會(huì)使用name解析。
例如:
public enum GenderEnum {
UNKNOWN("0", "未知"),
MALE("1", "男"),
FEMALE("2", "女");
@JsonValue
@Getter
private final String code;
@Getter
private final String description;
GenderEnum(String code, String description) {
this.code = code;
this.description = description;
}
private static final Map<String, GenderEnum> VALUES = new HashMap<>();
static {
for (final GenderEnum type : GenderEnum.values()) {
GenderEnum.VALUES.put(type.getCode(), type);
}
}
@JsonCreator
public static GenderEnum of(String code) {
return GenderEnum.VALUES.get(code);
}
}
- 反序列化
如果是PostJson提交的數(shù)據(jù),jackson會(huì)自動(dòng)調(diào)用@JsonCreator來進(jìn)行構(gòu)造,數(shù)據(jù)是可以正常提交的。
如果是Form表單提交,參數(shù)里只有一個(gè)Enum格式的參數(shù)。
@GetMappting("/listByGender")
public void listByGender(@RequestParam GenderEnum gender) {
}
這時(shí)候提交gender=1會(huì)解析失敗,可以通過配置MessageConvertFactory實(shí)現(xiàn)解析。
定義StringToEnumConverterFactory:
@Slf4j
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
private static final Map<Class, Converter> CONVERTER_MAP = new ConcurrentHashMap<>();
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
Converter converter = CONVERTER_MAP.get(targetType);
if (converter == null) {
converter = new StringToEnumConverter<>(targetType);
CONVERTER_MAP.put(targetType, converter);
}
return converter;
}
class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Map<String, T> enumMap = new HashMap<>();
private StringToEnumConverter(Class<T> enumType) {
Optional<Field> optional = AnnotationUtils.getAnnotationField(enumType, JsonValue.class);
//Enum有JsonValue注解時(shí)使用該值解析,否則使用默認(rèn)的name解析
if (optional.isPresent()) {
Field field = optional.get();
try {
field.setAccessible(true);
T[] enums = enumType.getEnumConstants();
for (T e : enums) {
enumMap.put(String.valueOf(field.get(e)), e);
}
} catch (IllegalAccessException e) {
log.error("enum map construct failed:", e);
}
} else {
T[] enums = enumType.getEnumConstants();
for (T e : enums) {
enumMap.put(e.name(), e);
}
}
}
@Override
public T convert(String source) {
return enumMap.get(source);
}
}
}
配置mvc
@Configuration
public class CustomWebMvcConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new StringToEnumConverterFactory());
}
}
- 使用FeignClient
當(dāng)使用FeignClient進(jìn)行服務(wù)訪問時(shí),F(xiàn)eign會(huì)自動(dòng)構(gòu)造請求參數(shù)。Feign的接口的定義為:
@GetMappting("/listByGender")
public void listByGender(@RequestParam GenderEnum gender);
這時(shí)候調(diào)用訪問,F(xiàn)eign會(huì)把gender轉(zhuǎn)成name來進(jìn)行訪問?gender=MALE,這個(gè)情況下服務(wù)端肯定報(bào)錯(cuò),因?yàn)槲覀兊腅num解析已經(jīng)不是name了。
所以要配置下FeignClient,支持Enum也轉(zhuǎn)成JsonValue注解的值進(jìn)行訪問。
定義Converter:
@Slf4j
public class EnumToStringConverter implements Converter<Enum<?>, String> {
@Override
public String convert(Enum<?> source) {
Class<?> enumType = source.getClass();
Optional<Field> optional = AnnotationUtils.getAnnotationField(enumType, JsonValue.class);
if (optional.isPresent()) {
Field field = optional.get();
field.setAccessible(true);
return String.valueOf(field.get(source));
} else {
return source.name();
}
}
}
配置FeignClient:
@Component
public class EnumFeignFormatterRegistrar implements FeignFormatterRegistrar {
@Override
public void registerFormatters(FormatterRegistry registry) {
registry.addConverter(new EnumToStringConverter());
}
}
通過以上三個(gè)步驟,就可以實(shí)現(xiàn)Enum參數(shù)的請求和響應(yīng)了。