1、问题描述
有以下一条Map结构的数据,sex字段的值是0
{
name:"test",
sex:"0"
}
对应的sex枚举
public enum SexEnum{
MAN("0"),
WOMAN("1");
private String code;
public SexEnum getByCode(String code) {
for (SexEnumem : values()) {
if (em.getCode().equals(code)) {
return em;
}
}
return null;
}
}
调用BeanUtil.toBean,map转bean,报Can not convert 0 to class SexEnum
2、排查过程
debug toBean方法,最终定位到cn.hutool.core.convert.impl包下的EnumConverter枚举转换类的tryConvertEnum方法,如下
/**
* 尝试转换,转换规则为:
* <ul>
* <li>如果实现{@link EnumItem}接口,则调用fromInt或fromStr转换</li>
* <li>找到类似转换的静态方法调用实现转换且优先使用</li>
* <li>约定枚举类应该提供 valueOf(String) 和 valueOf(Integer)用于转换</li>
* <li>oriInt /name 转换托底</li>
* </ul>
*
* @param value 被转换的值
* @param enumClass enum类
* @return 对应的枚举值
*/
protected static Enum tryConvertEnum(Object value, Class enumClass) {
if (value == null) {
return null;
}
// EnumItem实现转换
if (EnumItem.class.isAssignableFrom(enumClass)) {
final EnumItem first = (EnumItem) EnumUtil.getEnumAt(enumClass, 0);
if (null != first) {
if (value instanceof Integer) {
return (Enum) first.fromInt((Integer) value);
} else if (value instanceof String) {
return (Enum) first.fromStr(value.toString());
}
}
}
// 用户自定义方法
// 查找枚举中所有返回值为目标枚举对象的方法,如果发现方法参数匹配,就执行之
try {
final Map<Class<?>, Method> methodMap = getMethodMap(enumClass);
if (MapUtil.isNotEmpty(methodMap)) {
final Class<?> valueClass = value.getClass();
for (Map.Entry<Class<?>, Method> entry : methodMap.entrySet()) {
if (ClassUtil.isAssignable(entry.getKey(), valueClass)) {
return ReflectUtil.invokeStatic(entry.getValue(), value);
}
}
}
} catch (Exception ignore) {
//ignore
}
//oriInt 应该滞后使用 以 GB/T 2261.1-2003 性别编码为例,对应整数并非连续数字会导致数字转枚举时失败
//0 - 未知的性别
//1 - 男性
//2 - 女性
//5 - 女性改(变)为男性
//6 - 男性改(变)为女性
//9 - 未说明的性别
Enum enumResult = null;
if (value instanceof Integer) {
enumResult = EnumUtil.getEnumAt(enumClass, (Integer) value);
} else if (value instanceof String) {
try {
enumResult = Enum.valueOf(enumClass, (String) value);
} catch (IllegalArgumentException e) {
//ignore
}
}
return enumResult;
}
方法总结:属性值转枚举时,会优先获取返回值是该枚举类的静态方法,将调用此方法将属性值进行转换,如果枚举没提供以上方法,则调用枚举的valueOf方法,根据枚举名称转换。至此解决此问题的关键在于:1、枚举类中提供一个可行的静态方法可以转换属性值到枚举。2、属性值与枚举名称保持一致。
3、解决方法
结合上面的代码,可以发现提供的getByCode方法不是static方法,导致会调用valueOf方法,但是属性值(0)与枚举的名称(MAN)不一致导致报错;
因此,将方法改为static即可;