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

使用Jackson注解@JsonTypeInfo与@JsonSubTypes时遇解码异常求助

问题:Jackson反序列化时无法解析类型ID '4'

错误信息

org.springframework.core.codec.DecodingException: JSON decoding error: Could not resolve type id '4' as a subtype of com.myproject.onetoone.viewmodels.request.TopicGenerationRequest: known type ids = [GoalsGenerationRequest, TodoGenerationRequest, TopicGenerationRequest]; nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve type id '4' as a subtype of com.myproject.onetoone.viewmodels.request.TopicGenerationRequest: known type ids = [GoalsGenerationRequest, TodoGenerationRequest, TopicGenerationRequest] at [Source: (io.netty.buffer.ByteBufInputStream); line: 2, column: 13]

相关代码

父类 TopicGenerationRequest

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @Type(value = GoalsGenerationRequest.class, name="GoalsGenerationRequest"),
    @Type(value = TodoGenerationRequest.class, name="TodoGenerationRequest")
})
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TopicGenerationRequest {
      private int type;
}

子类 GoalsGenerationRequest

import com.myproject.onetoone.viewmodels.request.TopicGenerationRequest;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@JsonTypeName("GoalsGenerationRequest")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GoalsGenerationRequest extends TopicGenerationRequest {
    private List<Goal> goals;
}

子类 TodoGenerationRequest

@JsonTypeName("TodoGenerationRequest")
@Getter
@Setter
public class TodoGenerationRequest extends TopicGenerationRequest {

    @NotNull(message = "A valid UUID must be passed")
    private UUID ownerId;

    @NotBlank(message = "valid title should be added")
    private String title;

    private ZonedDateTime deadline;
}

Goal 类

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Goal {
    private int type;
    private int id;
}

请求内容(Postman发送)

{
    "type": 4,
    "goals": [
        {
            "type": 3,
            "id": 456
        },
        {
            "type": 3,
            "id": 454
        }
    ]
}

端点映射逻辑

Mono<ProfileIdentity> profileIdentityMono = Utility.getUserPrincipal().map(UserPrincipal::getProfileID);
Mono<TopicGenerationRequest>  generationRequest = serverRequest.bodyToMono(TopicGenerationRequest.class)
    .onErrorResume(castError -> Mono
        .error(new ClientErrorException(ErrorCodes.INVALID_REQUEST, "please provide valid json body")));

解决方案

核心问题

当前配置存在两个冲突:

  1. @JsonTypeInfo 指定用字符串名称作为类型ID,但请求中传递的是数字4,Jackson无法将数字匹配到GoalsGenerationRequest这类字符串名称
  2. 父类的type字段与Jackson用于类型识别的type属性重名,导致Jackson把数字4当成类型名称解析,而非普通字段

具体修改方案

方案一:用数字作为类型ID匹配子类

如果希望继续使用数字type区分子类,调整注解配置:

  1. 修改父类的@JsonSubTypes,将name设置为对应数字:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @Type(value = GoalsGenerationRequest.class, name="4"), // 对应请求的type=4
    @Type(value = TodoGenerationRequest.class, name="5") // 按需设置Todo对应的数字
})
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TopicGenerationRequest {
      private int type;
}
  1. 子类的@JsonTypeName同步为对应数字(或直接删除,依赖父类配置):
@JsonTypeName("4")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class GoalsGenerationRequest extends TopicGenerationRequest {
    private List<Goal> goals;
}

方案二:用字符串作为类型ID(规范做法)

遵循Jackson默认的名称匹配逻辑,调整请求和配置:

  1. 修改Postman请求的type值为字符串:
{
    "type": "GoalsGenerationRequest",
    "goals": [
        {
            "type": 3,
            "id": 456
        },
        {
            "type": 3,
            "id": 454
        }
    ]
}
  1. 可选:修改父类的type字段名称,避免与Jackson类型识别属性混淆:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
    @Type(value = GoalsGenerationRequest.class, name="GoalsGenerationRequest"),
    @Type(value = TodoGenerationRequest.class, name="TodoGenerationRequest")
})
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TopicGenerationRequest {
      private int requestType; // 替换原type字段名,消除冲突
}

注意事项

  • 无论哪种方案,请求中的type值必须与Jackson配置的类型ID完全匹配
  • 若保留父类type字段与Jackson类型属性同名,Jackson会优先将其解析为类型ID,而非普通业务字段

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:37:43