记一次前端传integer类型枚举映射不正确处理问题

该博客讨论了如何在使用com.fasterxml.jackson库进行JSON序列化和反序列化时,针对枚举类型的特殊处理。文章指出,当前端传入数字时,Jackson默认根据枚举的下标进行映射,但为了匹配`@JsonValue`中的integer代码,需要重写枚举的反序列化过程。作者提供了一个基类`BaseEnum`和一个自定义反序列化类`JackSonIntegerToEnum`,确保数字类型能正确映射到对应的枚举实例。

首先明确项目采用中采用的是什么jackJson序列化和反序列化的,我们项目中采用的是com.fasterxml.jackson。

枚举实列:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum PersonsTypeEnum  implements BaseEnum<Integer> {
    UNREALCHINESE(1,"未实名的内地人"),
    REALCHINESE(2,"已实名的内地人"),
    FOREIGN(3,"外国人"),
    SPECIALREGION(4,"特别行政区"),
    UNKNOWN(5,"未知");
    @JsonValue
    private Integer code;

    private String msg;

    PersonsTypeEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }



    @Override
    public Integer getValue() {
        return code;
    }
}

由于JsonValue为code,其类型为integer类型。当前端传入1的时候,其映射枚举为REALCHINESE,因为枚举中可以看作一个数组,当为integer的时候,就会根据其下标去取值

com.fasterxml.jackson的枚举的反序列化的源码:解析类:EnumDeserializer

@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
    JsonToken curr = p.currentToken();
    
    // 为字符串类型,根据jsonValue注解中的字符串的hash值和key的hash值匹配
        //见方法lookup.find(name),当没找到,又会将字符串做一系列转换,如果是数字类字符串直        接取下标
    if (curr == JsonToken.VALUE_STRING || curr == JsonToken.FIELD_NAME) {
        CompactStringObjectMap lookup = ctxt.isEnabled(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
                ? _getToStringLookup(ctxt) : _lookupByName;
        final String name = p.getText();
        Object result = lookup.find(name);
        if (result == null) {
            return _deserializeAltString(p, ctxt, lookup, name);
        }
        return result;
    }
    // 当为数字类型时,默认取intValue,然后取下标值
        
    if (curr == JsonToken.VALUE_NUMBER_INT) {
        // ... unless told not to do that
        int index = p.getIntValue();
        if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)) {
            return ctxt.handleWeirdNumberValue(_enumClass(), index,
                    "not allowed to deserialize Enum value out of number: disable DeserializationConfig.DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS to allow"
                    );
        }
        if (index >= 0 && index < _enumsByIndex.length) {
            return _enumsByIndex[index];
        }
        if ((_enumDefaultValue != null)
                && ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE)) {
            return _enumDefaultValue;
        }
        if (!ctxt.isEnabled(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)) {
            return ctxt.handleWeirdNumberValue(_enumClass(), index,
                    "index value outside legal index range [0..%s]",
                    _enumsByIndex.length-1);
        }
        return null;
    }
    return _deserializeOther(p, ctxt);
}

综合上述,前端传入数字类型,只会根据其下标去取值,为了与jsonValue中的code映射上需要重写其反序列化,为了不影响其他枚举的映射,因此定义了一个枚举基类。

//指定反序列化类
//由于项目中使用了mybatisPlus,因此基类也继承了IEnum,根据实际情况去掉即可
@JsonDeserialize(using = JackSonIntegerToEnum.class)
public interface BaseEnum<T extends Serializable> extends IEnum {

}

//只有继承BaseEnum类的数字类型才做处理

public class JackSonIntegerToEnum extends JsonDeserializer<BaseEnum> {


    @Override
    public BaseEnum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String currentName = jp.currentName();
        Object currentValue = jp.getCurrentValue();
        JsonToken curr = jp.currentToken();
        Class findPropertyType = null;
        if (currentValue instanceof Collection) {

            JsonStreamContext parsingContext = jp.getParsingContext();

            JsonStreamContext parent = parsingContext.getParent();
            Object currentValue3 = parent.getCurrentValue();
            String currentName3 = parent.getCurrentName();
            try {
                Field listField = currentValue3.getClass().getDeclaredField(currentName3);
                ParameterizedType listGenericType = (ParameterizedType) listField.getGenericType();
                Type listActualTypeArguments = listGenericType.getActualTypeArguments()[0];
                findPropertyType = (Class) listActualTypeArguments;
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            findPropertyType = BeanUtils.findPropertyType(currentName, currentValue.getClass());
        }


        if (findPropertyType.isEnum()) {
            BaseEnum[] baseEnums = (BaseEnum[]) findPropertyType.getEnumConstants();
            for (BaseEnum baseEnum : baseEnums) {
                //兼容前端传字符串和数字,都映射成对应枚举
                if(curr == JsonToken.VALUE_NUMBER_INT && baseEnum.getValue().equals(jp.getIntValue())) {
                        return baseEnum;
                }else if(curr == JsonToken.VALUE_STRING && baseEnum.getValue().toString().equals(jp.getValueAsString())) {
                    return baseEnum;
                }
            }
        }
        return null;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值