Możesz zaimplementować niestandardowy JsonDeserializer
dla swojego rodzaju ogólnego, który również implementuje ContextualDeserializer
.
Na przykład, załóżmy, że mamy następujący typ prosty wrapper, który zawiera wartość ogólna:
public static class Wrapper<T> {
public T value;
}
teraz chcemy deserializować JSON, który wygląda tak:
{
"name": "Alice",
"age": 37
}
do instancji Klasa wyglądająca następująco:
public static class Person {
public Wrapper<String> name;
public Wrapper<Integer> age;
}
Implementacja ContextualDeserializer
pozwala nam utworzyć określony deserializator dla każdego pola w klasie Person
, w oparciu o ogólne parametry typu pola. Dzięki temu możemy deserializować nazwę jako ciąg, a wiek jako liczbę całkowitą.
Kompletny Deserializator wygląda następująco:
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;
}
}
Jest najlepiej spojrzeć na createContextual
tu pierwszy, jak to będzie pierwszy powołany przez Jacksona. Odczytujemy typ pola z BeanProperty
(np. Wrapper<String>
), a następnie wyodrębniamy pierwszy typowy parametr (np. String
). Następnie tworzymy nowy deserializer i przechowujemy wewnętrzny typ jako valueType
.
Po wywołaniu deserialize
na nowo utworzonym deserializatorze, możemy po prostu poprosić Jacksona o deserializację wartości jako typu wewnętrznego, a nie jako całego typu opakowania, i zwrócić nowy Wrapper
zawierający deserializowaną wartość.
W celu zarejestrowania tego niestandardowego Deserializator, my wtedy trzeba utworzyć moduł, który go zawiera, i zarejestrować ten moduł:
SimpleModule module = new SimpleModule()
.addDeserializer(Wrapper.class, new WrapperDeserializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
Gdybyśmy następnie spróbuj deserializowania przykładowy JSON z góry, widzimy że działa zgodnie z oczekiwaniami:
Person person = objectMapper.readValue(json, Person.class);
System.out.println(person.name.value); // prints Alice
System.out.println(person.age.value); // prints 37
Istnieje kilka szczegółów na temat jak kontekstowe deserializers pracować w Jackson documentation.
Można użyć refleksji, aby dowiedzieć się, że zadeklarowane * * typ 'foo' to' Foo '. O to chodzi. Nie wiem, czy Jackson powiedział, czy to jest środek twojego celu tutaj. ['Field # getGenericType'] (http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Field.html#getGenericType--) –
Radiodef