Retrofit uses Google’s gson libraries to deserialize JSON representation to Java object representation. Although, this deserialization process works for most of the cases, sometimes you would have to override the deserialization process to parse a part of the response or because you don’t have any clear object representation of the JSON data.
In this post, I will share an example of a custom deserializer to parse the response from Wiktionary’s word definition API. First, let us take a look at the request and response.
The request URL is mentioned below:-
The response is mentioned below, It has been shortened for brevity.
{"batchcomplete":"","query":{"pages":{"200363":{"pageid":200363,"ns":0,"title":"sublime","extract":"<p></p>\n<h2><span id=\"English\">English</span></h2>\n<h3><span id=\"Pronunciation\">Pronunciation</span></h3>\n<ul><li>\n</li>\n<li>Rhymes: <span lang=\"\">-a\u026am</span></li>\n</ul><h3><span id=\"Etymology_1\">Etymology 1</span></h3>\n<p>From <span>Middle English</span> <i class=\"Latn mention\" .......for brevity }}}}
As, you can see the data we would be interested in is extract and probably the pageid. Now, as there is no straightforward object representation of this entire response in Java, so we would implement our own custom deserializer to parse this JSON response.
The code for the deserializer is mentioned below.
public class DictionaryResponseDeserializer implements JsonDeserializer<WicktionarySearchResponse> {
@Override
public WicktionarySearchResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Gson gson=new Gson();
JsonElement value = null;
value = json.getAsJsonObject().get("query").getAsJsonObject().get("pages");
WicktionarySearchResponse response = new WicktionarySearchResponse();
if(value!=null) {
Iterable<Map.Entry<String, JsonElement>> entries = value.getAsJsonObject().entrySet();
Query query = new Query();
ArrayList<ResultPage> resultPages = new ArrayList<ResultPage>();
for (Map.Entry<String, JsonElement> entry : entries) {
resultPages.add(new Gson().fromJson(entry.getValue(), ResultPage.class));
}
query.setPages(resultPages);
response.setQuery(query);
}
return response;
}
}
Pay special attention to the highlighted lines. On the first highlighted line, we are assigning the JsonElement with the value of the object that contains all the pages from the JSON response, as we are interested in only that data. Next, we iterate the assigned value and as we are interested in the actual values and not the keys (as the key pageid is already present in the individual pageid objects), so we just use entry.getValue to obtain that and then transform it to a Java POJO instance using the GSON object instance.
Below, I have mentioned the service interface and an util class to invoke the word search API.
public interface DictionaryService {
@GET("/w/api.php")
public void getMeaningOfWord(@QueryMap Map<String, String> map, Callback<WicktionarySearchResponse> response);
@GET("/w/api.php")
public WicktionarySearchResponse getMeaningOfWord(@QueryMap Map<String, String> map);
}
/**
* Created by Ramandeep on 07-01-2015.
*/
public class DictionaryUtil {
private static final String tag="DictionaryUtil";
private static Gson gson= initGson();
private static Gson initGson() {
if(gson==null){
gson= new GsonBuilder().registerTypeAdapter(WicktionarySearchResponse.class,new DictionaryResponseDeserializer()).create();
}
return gson;
}
public static WicktionarySearchResponse searchDefinition(String word){
WicktionarySearchResponse searchResponse=null;
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://wiktionary.org").setConverter(new GsonConverter(gson))
.build();
DictionaryService serviceImpl= restAdapter.create(DictionaryService.class);
Map queryMap=new HashMap();
queryMap.put("action","query");
queryMap.put("prop","extracts");
queryMap.put("redirects",null);
queryMap.put("format","json");
queryMap.put("continue",null);
queryMap.put("titles",word);
try {
searchResponse= serviceImpl.getMeaningOfWord(queryMap);
}catch (Exception e){
if(e==null&&e.getMessage()!=null) {
Log.e(tag, e.getMessage());
}
}
return searchResponse;
}
}
Below, I have mentioned the POJO classes. In order of hierarchy.
public class WicktionarySearchResponse {
private Query query=null;
public Query getQuery() {
return query;
}
public void setQuery(Query query) {
this.query = query;
}
}
public class Query {
public List<ResultPage> getPages() {
return pages;
}
public void setPages(List<ResultPage> pages) {
this.pages = pages;
}
private List<ResultPage> pages=null;
}
public class ResultPage {
private long pageId;
private String title;
private int index;
private String extract;
public ResultPage() {
}
public long getPageId() {
return pageId;
}
public void setPageId(long pageId) {
this.pageId = pageId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getExtract() {
return extract;
}
public void setExtract(String extract) {
this.extract = extract;
}
}