The situation is the following: I have managed to get Jackson to deserialize the following generic ResponseWrapper<T>
.
static final class ResponseWrapper<T> {
private ResponseData <T> response;
protected static final class ResponseData<T> {
private int status;
private T data;
}
}
Using the following ParameterizedTypeReference
public static <T> ParameterizedTypeReference <ResponseWrapper<T>> typeReferenceOf ( Class<T> tClass ) {
return ParameterizedTypeReference.forType( ParameterizedTypeImpl.make( ResponseWrapper.class, new Type[]{ tClass }, null ) );
}
The problem: I need to handle a situation where the deseralization of T
will fail because the value of data
will not be an object but a String
instead. I need to capture the exception and assign this value to another property in ResponseData
, for example String errorMessage
. My take on it has been to annotate the response
property with @JsonDeserialize( using = ResponseDeserializer.class )
but I do not know how to implement the JsonDeserializer
properly so that it delegates the deserialization to Jackson's implementations and I simply capture the exception when it occurs.
For more context, I am using WebClient
as the HTTP client and I deal with responses using an exchange Function<ClientResponse, Mono<ResponseWrapper<T>>
where the deserialization takes place.
In order to get the generic type, I had ResponseDeserializer
implement ContextualDeserializer
@Override
public JsonDeserializer<?> createContextual ( DeserializationContext context, BeanProperty property ) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
return new ResponseDeserializer<>( valueType );
}
That allowed me to create concrete instances of the deserializer with the specific JavaType
. The following is the complete solution:
private static final class ResponseDeserializer<T> extends JsonObjectDeserializer<ResponseWrapper.ResponseData<T>> implements ContextualDeserializer {
private final JavaType javaType;
public ResponseDeserializer ( ) {
this.javaType = null;
}
public ResponseDeserializer ( JavaType javaType ) {
this.javaType = javaType;
}
@Override
public JsonDeserializer<?> createContextual ( DeserializationContext context, BeanProperty property ) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
return new ResponseDeserializer<>( valueType );
}
@Override
protected ResponseWrapper.ResponseData<T> deserializeObject ( JsonParser jsonParser, DeserializationContext context, ObjectCodec codec, JsonNode tree ) throws IOException {
int status = tree.get( "status" ).asInt();
JsonNode dataNode = tree.get( "data" );
try {
final T data; {
if ( dataNode == null ) {
data = null;
} else {
JsonParser dataParser = dataNode.traverse();
JsonToken token = dataParser.nextToken(); // handle?
data = context.readValue( dataParser, javaType );
}
};
return new ResponseWrapper.ResponseData<T>( status, data );
} catch ( InvalidDefinitionException e ) {
String errorMessage = dataNode.asText();
return new ResponseWrapper.ResponseData<T>( status, errorMessage );
}
}
}