Currently deserializing with GSON and retrofit using retrofits GsonConverterFactory:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(new TypeToken<Map<Book, Collection<Author>>>(){}.getType(), new BooksDeserializer(context));
Gson gson = gsonBuilder.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
BookService service = retrofit.create(BookService.class);
Response<Map<Book, Collection<Author>>> response = service.getBooks().execute();
I would like to use the JacksonConverterFactory that is provided by retrofit? I would need to provide that a Jackson mapper. Is there a way to provide the type information to that mapper like I did with GSON?
SimpleModule simpleModule = new SimpleModule();
// TODO provide mapper with info needed to deserialize
// Map<Book, Collection<Author>>
mapper.registerModule(simpleModule);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(JacksonConverterFactory.create(mapper))
.build();
BookService service = retrofit.create(BookService.class);
Response<Map<Book, Collection<Author>>> response = service.getBooks().execute();
Looking specifically at the TODO can I tell the mapper to use this deserializer?
public class BooksDeserializer extends JsonDeserializer<Map<Book, Collection<Author>>> {
@Override
public Map<Book, Collection<Author>> deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
// deserialize here
}
}
According to the API , SimpleModule.addDeserializer(java.lang.Class, com.fasterxml.jackson.databind.JsonSerializer)
requires an instance of JsonSerializer<T>
whereby T is a supertype of the class you supply as an argument , ie the deserializer needs to be able to deserialize objects which are a subclass of the supplied class; TypeReference<Map<Book, Collection<Author>>>
is not a subtype of Map<Book, Collection<Author>>
.
However, writing a serializer for maps is not easy thanks to Java's type erasure. One way of doing this is to write a wrapper class for your map, such as
@XmlRootElement
public class SerializableBookMapWrapper {
private Map<Book, Collection<Author>> wrapped;
public SerializableBookMapWrapper(final Map<Book, Collection<Author>> wrapped) {
this.wrapped = wrapped;
}
public Map<Book, Collection<Author>> getWrapped() {
return wrapped;
}
public void setWrapped(final Map<Book, Collection<Author>> wrapped) {
this.wrapped = wrapped;
}
}
With this kind of wrapper class, you can implement a JsonDeserializer<SerializableBookMapWrapper>
and use that instead. However, if you haven't used Jackson annotations in the definition of Book
and Author
, you need to supply custom deserializers for them as well.
Alternatively, you can also try supplying this type information while actually using the ObjectMapper
instance for deserialization .
I think there is misunderstanding here: you do NOT implement JsonDeserializer
just because you want to bind into some type. This is what Jackson itself does. And if you need for some reason customized deserializer (or serializer), it will only handle specific part of nested type; deserializer for Book
or Author
or Map
, but for nested types Map
deserializer delegates to key and value handlers. It does NOT register itself for combination.
Anyway: I am fairly certain that custom deserializer is not the answer here, but a way to wrap basic usage of ObjectMapper
to read JSON as given type. But without knowing more about Retrofit I don't know what exactly.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.