I have this method in my controller:
@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody InterfaceClass interfaceClass) {
// Do something
}
The error I am getting is pretty straightforward and self-explanatory:
Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.
Basically, I need to tell Spring that I have a concrete implementation of InterfaceClass, ClassImpl.
I tried:
@JsonRootName("InterfaceClass")
public class ClassImpl implements InterfaceClass {
}
to no extent. I cannot use @JsonTypeInfo as the parent interface class InterfaceClass should not be aware of ClassImpl and they are in different modules. What I have also tried:
Implement InterfaceClass with abstract AbstractClass and put:
@JsonDeserialize(as = AbstractClass.class)
on top of InterfaceClass. Then extend AbstractClass with ClassImpl. The error simply becomes:
Can not construct instance of InterfaceClass: abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information.
Further tried:
public class ControllerClass<E extends InterfaceClass> {
@RequestMapping(method = RequestMethod.POST)
InterfaceClass insert(@RequestBody E interfaceClass) {
InterfaceClass object = (InterfaceClass) interfaceClass;
}
}
and this results in:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to InterfaceClass
as expected.
I was really expecting Spring Boot to handle component discovery since there is only one concrete implementation of InterfaceClass or AbstractClass, which is ClassImpl in my classpath. Maybe I am doing something wrong? How can I overcome this, without explicitly hinting the InterfaceClass on where its implementation is (e.g. no @JsonDeserialize, etc.)?
Solution 1 - Dynamically register subtypes
You can define the subtypes dynamically.
1. On the interface, define a JSON field (@type) to be used as identifier:
2. Add "@type" field to your JSON payload
2. Register the inteface's subtypes dynamically:
4. Specify the "@type" name on your concrete class (optional):
5. Use can now use the interface with @RequestBody:
Solution 2 - Dynamically register a custom Deserializer
If adding a
@typefield isn't possible (or not wanted), you may also define a Custom Deserializer for your interface, which would in fact create aClassImpl:1. Define a custom deserializer:
2. Dynamically set the custom deserializer:
3. Remove @JsonTypeInfo from the interface: