È possibile implementare un numero personalizzato JsonDeserializer
per il tipo generico che implementa anche ContextualDeserializer
.
Per esempio, supponiamo di avere il seguente tipo di involucro semplice che contiene un valore generico:
public static class Wrapper<T> {
public T value;
}
Ora vogliamo deserializzare JSON che assomiglia a questo:
{
"name": "Alice",
"age": 37
}
in un'istanza di una classe che assomiglia a questo:
public static class Person {
public Wrapper<String> name;
public Wrapper<Integer> age;
}
Implementazione ContextualDeserializer
ci consente di creare un deserializzatore specifico per ogni campo nella classe Person
, in base ai parametri di tipo generico del campo. Questo ci permette di deserializzare il nome come stringa e l'età come numero intero.
Il deserializzatore completo si presenta così:
public static class WrapperDeserializer extends JsonDeserializer<Wrapper<?>> implements ContextualDeserializer {
private JavaType valueType;
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
JavaType wrapperType = property.getType();
JavaType valueType = wrapperType.containedType(0);
WrapperDeserializer deserializer = new WrapperDeserializer();
deserializer.valueType = valueType;
return deserializer;
}
@Override
public Wrapper<?> deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException {
Wrapper<?> wrapper = new Wrapper<>();
wrapper.value = ctxt.readValue(parser, valueType);
return wrapper;
}
}
E 'meglio guardare createContextual
prima qui, in quanto questo sarà chiamato prima da Jackson. Leggiamo il tipo di campo fuori dallo BeanProperty
(ad esempio Wrapper<String>
) e quindi estraiamo il primo parametro di tipo generico (ad esempio String
). Creiamo quindi un nuovo deserializzatore e memorizziamo il tipo interno come valueType
.
volta deserialize
è chiamato in questa deserializzatore appena creato, possiamo semplicemente chiedere a Jackson per deserializzare il valore come il tipo interiore piuttosto che come l'intero tipo di involucro, e restituire un nuovo Wrapper
contenente il valore deserializzato.
Per registrare questo deserializzatore personalizzato, abbiamo quindi bisogno di creare un modulo che lo contiene, e registriamo quel modulo:
SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class, new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
Se poi tenta di deserializzare l'esempio JSON dall'alto, possiamo vedere che funziona come previsto:
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value); // prints Alice
System.out.println(person.age.value); // prints 37
ci sono alcuni ulteriori dettagli su come contestuale deserializzatore lavorano nel Jackson documentation.
È possibile utilizzare la reflection per scoprire che il * dichiarato * il tipo di 'foo' è' Foo '. Questo è tutto. Non so di Jackson per dire se questo è un mezzo per la tua fine qui. ['Field # getGenericType'] (http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html#getGenericType--) –
Radiodef