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

首先明确项目采用中采用的是什么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;
    }

}

### 数据库中的枚举类型使用方法 在数据库设计中,枚举类型提供了一种有效的方式来限定字段的取值范围。对于实体类中使用枚举类型的字段,在持久化至关系型数据库时通常会遇到两种主要场景:一是将枚举类型的数据存储到数据库;二是从数据库读取数据并将其映射回相应的枚举对象。 #### 存储枚举类型到数据库 当需要把Java或其他编程语言定义好的枚举实例存入数据库时,一般有两种策略可以选择: - **整数表示法**:通过自定义序列号或者其他唯一标识符来代表每一个枚举常量,并以此为基础完成转换操作。这种方式的优点在于性能较好,因为整数值可以直接用于索引创建等优化措施[^1]。 ```java public enum Status { ACTIVE(1), INACTIVE(0); private final int code; Status(int code) { this.code = code; } public static Status fromCode(Integer code){ for (Status status : values()){ if(status.getCode() == code){ return status; } } throw new IllegalArgumentException("Unknown value:" + code); } public Integer getCode(){ return code; } } ``` - **字符串表示法**:直接利用枚举的名字或描述性的文字串作为实际存储形式。这种方法更直观易懂,但在某些情况下可能会占用更多空间[^2]. #### 查询并将数字转为枚举类型前端接收到请求参数或者是执行SQL语句获取录之后,如果这些数据原本是以枚举的形式存在于程序逻辑里,则应该立即将其还原成对应的枚举对象以便后续处理。这可以通过上述提到过的`fromCode()`这样的静态工厂方法实现自动化的双向映射过程。 ```sql -- 假设有一个名为users的表其中status列用来保存用户的激活状态 SELECT id, name, email, `status`, FROM users WHERE id=? ``` ```java // Java代码片段展示如何解析来自数据库的结果集 while(resultSet.next()) { User user = new User(); ... String statusCode = resultSet.getString("status"); try{ user.setStatus(Status.valueOf(statusCode)); } catch(IllegalArgumentException e){ logger.error("Invalid status code found in database."); } } ``` ### 实体类与数据库表之间关联的例子 考虑一个具体的应用案例——隧道管理系统。这里涉及到`tunnel_info`这张表格以及两个重要的概念:“隧道ID”(`tunnel_id`)和“段位”(`duan`)。这两个字段共同作用于表达特定条件下同种类的对象及其属性类别。例如,“隧道A”的某一段可能具有独特的物理特征或是管理规定,而这些都是由具体的枚举成员所决定的。 ```sql CREATE TABLE tunnel_info ( tunnel_id INT NOT NULL, duan ENUM('入口', '中部', '出口') NOT NULL, length DECIMAL(8,2), -- 单位米 PRIMARY KEY(tunnel_id, duan) ); ``` ```java public class TunnelInfo { @Id @Column(name="tunnel_id") private Long tunnelId; @Enumerated(EnumType.STRING) @Column(nullable=false) private DuanEnum duan; // Getter and Setter omitted... } public enum DuanEnum { RUKOU("入口"), ZHONGBU("中部"), CHUKOU("出口"); private final String description; DuanEnum(String desc){this.description = desc;} public String getDescription(){return description;} public static DuanEnum fromDescription(String desc){ for(DuanEnum d:DuanEnum.values()){ if(d.getDescription().equals(desc)){ return d; } } throw new IllegalArgumentException("No matching enum constant for ["+desc+"]"); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值