springmvc解析的列表中的对象含有枚举

1.问题

通过http调用请求,传输的是一个对象列表,其中列表中的对象含有枚举,
发现一直无法解析成功,并抛出以下错误,很明显是枚举无法解析成功;

POST https://xxx/audit/api/v1/asyncSaveAuditLog
Content-Type: application/json;charset=utf-8

[
  {"sysName":"SEEWO_USERCENTER"}
]
{
  "timestamp": 1627466213248,
  "status": 400,
  "error": "Bad Request",
  "exception": "org.springframework.http.converter.HttpMessageNotReadableException",
  "message": "JSON parse error: Can not construct instance of java.lang.Enum: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.lang.Enum: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information\n at [Source: java.io.PushbackInputStream@4ceb6da5; line: 2, column: 14] (through reference chain: java.util.ArrayList[0]->com.seewo.audit.api.dto.SaveAuditLogQuery[\"sysName\"])",
  "path": "/audit/api/v1/asyncSaveAuditLog"
}

2.思路

1.0.暴力破解

使用@RequestBody String body来接收对象,然后再使用JSON.parseArray()解析成JSONObject,在对特殊的枚举特殊处理;但太弱智了;

1.1.Converter

后来发现Converter仅用于点对点的转换,不适用于一整个列表对象的转换,因为这样就需要你来完成json解析的工作了,因此这种方式不行;

1.2. MessageConverter

Springboot提供的一个扩展工具接口,用于解析http中@RequestBody的消息,但后来发现,就是因为原有的MessageConverter处理不了才抛出这个异常的,要想解决这个问题只能添加新的MessageConverter,手写吧,工作量较大,而且还需要测试;
后来想到可以引用FastJsonHttpMessageConverter,但发现它解析不了列表,因为我传输的是一个列表;因此这种方式不行;

1.3.@JSONField

后来了解到fastjson提供了该注解用于自定义解析规则,因此使用该注解,问题解决;

@JSONField(deserializeUsing = EnumTypeDeserializer.class)
    private Enum<AuditServiceEnum> sysName;
package com.seewo.audit.api.utils;

import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

public class EnumTypeDeserializer implements ObjectDeserializer {
    private static final Logger logger = LoggerFactory.getLogger(EnumTypeDeserializer.class);

    private static final String prefixEnum = "java.lang.Enum<";

    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
        //[{"sysName":"SEEWO_USERCENTER"}]
        //其中sysName为fielName,SEEWO_USERCENTER为value
        Object value = parser.parse();
        String typeName = type.getTypeName();
        //如果是 java.lang.Enum< 开头,说明是 java.lang.Enum<com.seewo.audit.api.enums.AuditServiceEnum> 这种形式,因此需要去除前缀
        if (typeName.startsWith(prefixEnum)) {
            typeName = typeName.substring(prefixEnum.length(), typeName.length() - 1);
        }
        //直接反射
        try {
            Class<?> clazz = Class.forName(typeName);
            Method method = clazz.getMethod("valueOf", String.class);
            Object ret = method.invoke(null, value);
            return (T) ret;
        } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            logger.error("反序列化枚举失败:{}, {}", fieldName, e.getMessage());
        }
        return null;
    }

    @Override
    public int getFastMatchToken() {
        return 0;
    }
}

3.反思

解决这个问题花了将近4小时,哎,我还是太菜了,仍需继续努力;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值