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

如何用Event接口替代Object类型?Jackson反序列化异常问题

问题说明

我想把EventDto类的eventData字段类型从Object改成Event接口(该接口已有PrintEvent实现类),但替换后抛出了序列化异常。我不想用Object类型,因为会降低代码可读性。

错误信息:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.app.dto.Event]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.app.dto.Event (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 5, column: 26] (through reference chain: com.app.dto.SubmitEventsRequest["events"]->java.util.ArrayList[0]->com.app.dto.EventDto["eventData"])


问题原因

Jackson在反序列化时无法直接实例化抽象的Event接口,它不知道该把JSON数据转换成哪个具体的实现类(比如PrintEvent)。之前用Object类型时,Jackson会默认转成LinkedHashMap,所以不会报错,但换成接口类型后就需要明确指定类型映射规则。

解决方案

方案1:使用Jackson类型注解(推荐)

在Event接口上添加@JsonTypeInfo和@JsonSubTypes注解,告诉Jackson如何根据JSON中的字段匹配具体实现类。这里我们可以利用EventDto里的eventType字段作为类型标识:

修改后的Event接口

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.EXTERNAL_PROPERTY, // 从父对象EventDto获取类型标识
    property = "eventType" // 指定类型判断字段为eventType
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = PrintEvent.class, name = "PRINT_SUBMITTED"),
    @JsonSubTypes.Type(value = PrintEvent.class, name = "PRINT_FAILED")
    // 后续新增Event实现类时,在此添加对应映射
})
public interface Event {
}

说明

  • @JsonTypeInfo配置类型信息读取规则:EXTERNAL_PROPERTY表示从当前对象的父对象(EventDto)中读取eventType字段值,以此判断具体实现类。
  • @JsonSubTypes枚举所有事件类型与实现类的映射关系,比如PRINT_SUBMITTED和PRINT_FAILED都对应PrintEvent。

方案2:自定义反序列化器

如果不想依赖注解,可以自定义EventDto的反序列化器,根据eventType手动选择eventData的具体类型:

1. 实现自定义反序列化器

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class EventDtoDeserializer extends StdDeserializer<EventDto> {

    protected EventDtoDeserializer() {
        super(EventDto.class);
    }

    @Override
    public EventDto deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonNode node = p.getCodec().readTree(p);
        IncomingEventsTypeEnum eventType = IncomingEventsTypeEnum.valueOf(node.get("eventType").asText());
        
        EventDto.EventDtoBuilder builder = EventDto.builder();
        builder.eventType(eventType);
        
        // 根据eventType匹配对应的Event实现类
        JsonNode eventDataNode = node.get("eventData");
        switch (eventType) {
            case PRINT_SUBMITTED:
            case PRINT_FAILED:
                PrintEvent printEvent = p.getCodec().treeToValue(eventDataNode, PrintEvent.class);
                builder.eventData(printEvent);
                break;
            // 新增事件类型时在这里扩展
            default:
                throw new IllegalArgumentException("Unknown event type: " + eventType);
        }
        return builder.build();
    }
}

2. 注册反序列化器到ObjectMapper

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.addDeserializer(EventDto.class, new EventDtoDeserializer());
        mapper.registerModule(module);
        return mapper;
    }
}
验证

用原来的curl请求测试,现在eventData会被正确反序列化为PrintEvent对象,后续的EventHandler也能正常处理事件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:33:14