在不提示父级的情况下克服"Can not construct instance of InterfaceClass"

Overcome "Can not construct instance of InterfaceClass" without hinting the parent

我的控制器中有这个方法:

@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody InterfaceClass interfaceClass) {

    // Do something
}

我得到的错误非常简单明了:

Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.

基本上,我需要告诉Spring我有InterfaceClassClassImpl.

的具体实现

我试过了:

@JsonRootName("InterfaceClass")
public class ClassImpl implements InterfaceClass {
}

完全没有。我不能使用 @JsonTypeInfo 作为父接口 class InterfaceClass 不应该知道 ClassImpl 并且它们在不同的模块中 。我也尝试过:

用摘要 AbstractClass 实现 InterfaceClass 并输入:

@JsonDeserialize(as = AbstractClass.class)

InterfaceClass 之上。然后用 ClassImpl 扩展 AbstractClass。错误简单地变成:

Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.

进一步尝试:

public class ControllerClass<E extends InterfaceClass> {

    @RequestMapping(method = RequestMethod.POST)
    InterfaceClass insert(@RequestBody E interfaceClass) {
        InterfaceClass object = (InterfaceClass) interfaceClass;
    }
}

这导致:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to InterfaceClass

符合预期。

我真的很期待 Spring Boot 处理组件发现,因为 InterfaceClassAbstractClass 只有一个具体实现,即 ClassImpl 在我的 class路径。也许我做错了什么?如果不明确提示 InterfaceClass 它的实现位置(例如没有 @JsonDeserialize 等),我该如何克服这个问题?

解决方案 1 - 动态注册子类型

您可以动态定义子类型。

1。在界面上,定义一个JSON字段(@type)作为标识符:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, 
              include = JsonTypeInfo.As.PROPERTY, property = "@type")
public interface InterfaceClass {
}

2。将“@type”字段添加到您的 JSON 有效载荷

{
    ...
    "@type": "someName"
}

2。动态注册接口的子类型:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() {
        public void configure(ObjectMapper objectMapper) {
            objectMapper.registerSubtypes(ClassImpl.class);
            super.configure(objectMapper);
        };
    };
    return builder;
}

4。在具体的 class(可选)上指定“@type”名称:

//Optional, otherwise uses the Simple class name (ie: 'ClassImpl')
@JsonTypeName("someName") 
public class ClassImpl implements InterfaceClass {
}

5.现在可以使用带有@RequestBody 的接口:

@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody InterfaceClass interfaceClass) {
}

解决方案 2 - 动态注册自定义解串器

如果无法(或不需要)添加 @type 字段,您还可以为您的界面定义一个自定义反序列化器,这实际上会创建一个 ClassImpl:

1。定义自定义反序列化器:

class ClassImplJsonDeserializer extends JsonDeserializer<ClassImpl> {
    @Override
    public ClassImpl deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return jp.readValuesAs(ClassImpl.class).next();
    }
}

2。动态设置自定义解串器:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.deserializerByType(InterfaceClass.class, new ClassImplJsonDeserializer());
    return builder;
}

3。从接口中移除@JsonTypeInfo:

public interface InterfaceClass {
}