I am trying to handle all types of Jackson exceptions that turn up during REST API requests in a Spring Boot application. If something cannot be serialized, JsonMappingException is thrown. I handle this exception, build the field path that cannot be serialized (using exception.getPath) and return this information.
Now, I have some classes that implement the same interface (polymorphism) and have to work with them during a request. This means I also expose them to the REST API and can be included in the request/response body. Here is the interface:
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "service",
defaultImpl = DefaultNotificatorPresentation.class,
visible = true
)
@JsonSubTypes({
@JsonSubTypes.Type(value = EmailNotificatorPresentation.class, name = "email")
})
public interface NotificatorPresentation {
String getService();
}
Basically, there are different types of notificators which all have different service (email, sms, etc). This property is used for the @JsonTypeInfo. Everything works as expected until I started testing if JsonMappingException is thrown correctly with the JSON subtypes.
JsonMappingException is thrown for all properties (eg. when malformed) and InvalidTypeIdException when service is not any of the available types (only email at the moment). I would like to tell the user the available options for the service property (when string is given but does not match the available types - email, sms, etc) and that it is malformed when no string is provided (object or array for example).
I came up with a solution that uses defaultImpl of @JsonTypeInfo and uses a custom class with custom validation annotation and ConstraintValidator that handles it.
public class DefaultNotificatorPresentation implements NotificatorPresentation {
// implementation of getService() and validation annotation
}
The annotation has a default message - available services are only email, sms. That way, every time the default implementation is created (always when an invalid service is given by the user) there will be a validation error. This approach works when the property service in the json request is of type string - "not found service" for example.
But when object ({ "example": true }) is set to the service property, the defaultImpl class is created twice. The first instance is given property service "{" (the first character of { "example": true }). The second one service is just null. This creates 2 validation exceptions but must throw JsonMappingException.
Do you have any ideas on how this can be solved? I can even use a totally different approach that handles Jackson polymorphism.