You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中枚举列表自定义反序列化报错排查

问题解决:Month枚举多格式反序列化报错处理

问题根源

你当前的CustomEnumDeserializer默认把输入当成JSON对象处理,强行读取lowerName和code字段,但实际请求里的period元素是数字或字符串(比如1、"january"),此时JsonNode不是对象,调用node.get("lowerName")会返回null,再调用asText()就触发空指针,导致解析错误。

修复后的反序列化器

修改CustomEnumDeserializer,让它支持数字、字符串、对象三种输入格式,同时对无效参数抛出明确错误:

public class CustomEnumDeserializer extends StdDeserializer<Month> {

    public CustomEnumDeserializer() {
        super(Month.class);
    }

    @Override
    public Month deserialize(JsonParser jsonParser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        Month matchedMonth = null;

        // 处理数字格式(如1、2)
        if (node.isInt()) {
            int code = node.asInt();
            matchedMonth = findByCode(code);
        }
        // 处理字符串格式(如"january"、"FEBRUARY")
        else if (node.isTextual()) {
            String name = node.asText();
            matchedMonth = findByName(name);
        }
        // 处理对象格式(如{"code":1,"lowerName":"January"})
        else if (node.isObject()) {
            String lowerName = node.has("lowerName") ? node.get("lowerName").asText() : null;
            int code = node.has("code") ? node.get("code").asInt() : -1;
            
            if (lowerName != null) {
                matchedMonth = findByName(lowerName);
            }
            if (matchedMonth == null && code != -1) {
                matchedMonth = findByCode(code);
            }
        }

        // 无匹配时抛出错误,Spring会自动返回400请求错误
        if (matchedMonth == null) {
            throw new InvalidFormatException(jsonParser, 
                "无效的月份参数:" + node.asText(), node.asText(), Month.class);
        }

        return matchedMonth;
    }

    // 根据code查找枚举
    private Month findByCode(int code) {
        for (Month month : Month.values()) {
            if (month.getCode() == code) {
                return month;
            }
        }
        return null;
    }

    // 根据名称(忽略大小写)查找枚举
    private Month findByName(String name) {
        for (Month month : Month.values()) {
            if (month.getLowerName().equalsIgnoreCase(name)) {
                return month;
            }
        }
        return null;
    }
}

关键改动说明

  • 类型判断:先检查JsonNode的类型,分别处理数字、字符串、对象三种输入场景,避免无效的字段读取
  • 空安全处理:读取对象字段前先判断node.has("lowerName"),防止空指针
  • 错误抛出:找不到匹配时抛出InvalidFormatException,替代原来的返回null,Spring会自动生成标准化的错误响应,包含无效参数提示
  • 代码拆分:把查找逻辑抽成单独方法,提高可读性

测试验证

现在三种输入格式都能正常解析:

  1. 数字列表:
{
  "period": [1, 2, 3]
}
  1. 字符串列表(大小写任意):
{
  "period": ["january", "FEBRUARY", "March"]
}
  1. 混合格式:
{
  "period": ["january", 2, "March"]
}

输入无效参数(如13、"invalidMonth")时,会返回标准化的400错误响应,包含具体的无效参数提示。

内容的提问来源于stack exchange,提问作者Gianni Spear

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:12:34