#java #generics #jackson #json-deserialization
Вопрос:
Ситуация следующая: мне удалось заставить Джексона десериализовать следующий общий ResponseWrapper<T>
код .
static final class ResponseWrapper<T> {
private ResponseData <T> response;
protected static final class ResponseData<T> {
private int status;
private T data;
}
}
Используя следующие ParameterizedTypeReference
public static <T> ParameterizedTypeReference <ResponseWrapper<T>> typeReferenceOf ( Class<T> tClass ) {
return ParameterizedTypeReference.forType( ParameterizedTypeImpl.make( ResponseWrapper.class, new Type[]{ tClass }, null ) );
}
Проблема: мне нужно справиться с ситуацией, когда десерализация T
завершится неудачей, потому что значение data
будет не объектом, а String
вместо него. Мне нужно зафиксировать исключение и присвоить это значение другому свойству ResponseData
, например String errorMessage
. Я взял это на себя, чтобы аннотировать response
свойство, @JsonDeserialize( using = ResponseDeserializer.class )
но я не знаю, как правильно реализовать JsonDeserializer
, чтобы оно делегировало десериализацию реализациям Джексона, и я просто фиксирую исключение, когда оно возникает.
Для большего контекста я использую WebClient
в качестве HTTP-клиента и обрабатываю ответы с помощью обмена Function<ClientResponse, Mono<ResponseWrapper<T>>
, в котором происходит десериализация.
Ответ №1:
Чтобы получить универсальный тип, мне пришлось ResponseDeserializer
реализовать ContextualDeserializer
@Override
public JsonDeserializer<?> createContextual ( DeserializationContext context, BeanProperty property ) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
return new ResponseDeserializer<>( valueType );
}
Это позволило мне создать конкретные экземпляры десериализатора с конкретными JavaType
. Ниже приведено полное решение:
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 );
}
}
}