I have WebDeviceInfo
and IOSDeviceInfo
classes that are subclasses of DeviceInfo
. How can I create a single endpoint in a Spring @RestController
that will accept either IOSDeviceInfo
or WebDeviceInfo
?
Attempt #1
I tried to map the same RequestMapping
to two different methods, one that would get called if the RequestBody
could be mapped to a WebDeviceInfo
and the other that would get called if the RequestBody
could be mapped to a IOSDeviceInfo
.
@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerWebDevice(@RequestBody final WebDeviceInfo webDeviceInfo) {
//register web device
}
@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerIOSDevice(@RequestBody final IOSDeviceInfo iosDeviceInfo) {
//register ios device
}
But this does not work, the second RequestMapping
does not get registered and the application fails to start up because Spring sees that /register-device
with the same RequestMethod
and MediaType
is already mapped to another method.
Attempt #2
Next, I tried accepting the superclass as the RequestBody
and then casting it to the appropriate subclass.
@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
if (deviceInfo instanceof WebDeviceInfo) {
final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo;
//register web device
} else if (deviceInfo instanceof IOSDeviceInfo) {
final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo;
//register ios device
} else {
logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
}
}
This does not work either. I always get:
Could not cast deviceInfo to
WebDeviceInfo
orIOSDeviceInfo
Attempt #3
Finally, I tried just casting to the correct subclass inside a try
/catch
.
@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
try {
final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo);
//register web device
} catch (final ClassCastException ex) {
try {
final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo);
//register ios device
} catch (final ClassCastException ex2) {
logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
}
}
}
Again I get error:
Could not cast deviceInfo to
WebDeviceInfo
orIOSDeviceInfo
Is there any way to accomplish this, or am I going to have to create two separate methods with two different RequestMapping
s?
Attempts #2 and #3 should work when you annotate the base class DeviceInfo
with the correct Jackson annotations:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = IOSDeviceInfo.class, name = "ios"),
@JsonSubTypes.Type(value = WebDeviceInfo.class, name = "web")
})
public abstract class DeviceInfo {
[...]
}
class IOSDeviceInfo extends DeviceInfo {
[...]
}
class WebDeviceInfo extends DeviceInfo {
[...]
}
Then when you receive a request, the body will be deserialized into the correct subclass, either a IOSDeviceInfo or a WebDeviceInfo, depending on the 'type' parameter in the JSON body:
{
type : "ios",
[...]
}
Now you only need a single @RequestMapping method:
@RequestMapping(value = "/register-device", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public void registerDevice(@RequestBody final DeviceInfo deviceInfo) {
if (deviceInfo instanceof WebDeviceInfo) {
final WebDeviceInfo webDeviceInfo = (WebDeviceInfo) deviceInfo;
//register web device
} else if (deviceInfo instanceof IOSDeviceInfo) {
final IOSDeviceInfo iosDeviceInfo = (IOSDeviceInfo) deviceInfo;
//register ios device
} else {
logger.debug("Could not cast deviceInfo to WebDeviceInfo or IOSDeviceInfo");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With