Felipe Mosso Felipe Mosso - 3 months ago 33
Android Question

HttpMessageNotReadableException: Could not read JSON: Unrecognized field using Spring for Android

I'm developing and Android application which communicates with my Server. This communication is done thru Spring framework and Jackson. I'm sending an request for my server succesfully but I'm not getting the response. Here's what I have done:

Android app:

public Loja getLoja() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

String url = BASE_URL + "/android/played.json";
return restTemplate.getForObject(url, Loja.class);
}


Loja class (I have two versions of it. One in Android app and other in Server. They are completely the same):

public class Loja {

private String nome;
private String xValue;
private String yValue;
private String andar;

public Loja(String nome, String xValue, String yValue, String andar) {
this.nome = nome;
this.xValue = xValue;
this.yValue = yValue;
this.andar = andar;
}

public Loja() {
}

public String getAndar() {
return andar;
}

public void setAndar(String andar) {
this.andar = andar;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}

public String getxValue() {
return xValue;
}

public void setxValue(String xValue) {
this.xValue = xValue;
}

public String getyValue() {
return yValue;
}

public void setyValue(String yValue) {
this.yValue = yValue;
}

}


And here's my controller:

@RequestMapping("/android/played")
public ModelAndView getLoja() {
System.out.println("Android Resquest.");

Loja loja = new Loja("teste", "20", "30", "1");

ModelAndView mav = new ModelAndView();
mav.addObject("Loja", loja);
return mav;
}


With all of this I'm getting the followin exception in Android app:

03-21 22:13:06.197: E/AndroidRuntime(25342): java.lang.RuntimeException: An error occured while executing doInBackground()
03-21 22:13:06.197: E/AndroidRuntime(25342): at android.os.AsyncTask$3.done(AsyncTask.java:299)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
03-21 22:13:06.197: E/AndroidRuntime(25342): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.lang.Thread.run(Thread.java:856)
03-21 22:13:06.197: E/AndroidRuntime(25342): Caused by: org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Unrecognized field "Loja" (Class com.example.androidspring.Loja), not marked as ignorable
03-21 22:13:06.197: E/AndroidRuntime(25342): at [Source: org.apache.http.conn.EofSensorInputStream@422e3c58; line: 1, column: 10] (through reference chain: com.example.androidspring.Loja["Loja"]); nested exception is org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "Loja" (Class com.example.androidspring.Loja), not marked as ignorable
03-21 22:13:06.197: E/AndroidRuntime(25342): at [Source: org.apache.http.conn.EofSensorInputStream@422e3c58; line: 1, column: 10] (through reference chain: com.example.androidspring.Loja["Loja"])
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:125)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:147)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:76)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:484)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:439)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:237)
03-21 22:13:06.197: E/AndroidRuntime(25342): at com.example.androidspring.MainActivity.getLoja(MainActivity.java:59)
03-21 22:13:06.197: E/AndroidRuntime(25342): at com.example.androidspring.MainActivity$DownloadFilesTask.doInBackground(MainActivity.java:72)
03-21 22:13:06.197: E/AndroidRuntime(25342): at com.example.androidspring.MainActivity$DownloadFilesTask.doInBackground(MainActivity.java:1)
03-21 22:13:06.197: E/AndroidRuntime(25342): at android.os.AsyncTask$2.call(AsyncTask.java:287)
03-21 22:13:06.197: E/AndroidRuntime(25342): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
03-21 22:13:06.197: E/AndroidRuntime(25342): ... 5 more
03-21 22:13:06.197: E/AndroidRuntime(25342): Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "Loja" (Class com.example.androidspring.Loja), not marked as ignorable
03-21 22:13:06.197: E/AndroidRuntime(25342): at [Source: org.apache.http.conn.EofSensorInputStream@422e3c58; line: 1, column: 10] (through reference chain: com.example.androidspring.Loja["Loja"])
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.std.StdDeserializer.reportUnknownProperty(StdDeserializer.java:649)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:635)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.BeanDeserializer.handleUnknownProperty(BeanDeserializer.java:1355)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:717)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:580)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2723)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1914)
03-21 22:13:06.197: E/AndroidRuntime(25342): at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:122)
03-21 22:13:06.197: E/AndroidRuntime(25342): ... 15 more


By what I undestood, it is not recognizing my Loja class. But they are exactly the same! What else could it be?

Answer

The exception seems to suggest that while the Jackson mapper is trying to create a Loja from the returned json, it encountered a json field with a key of Loja, which it didn't know how to handle (because there are no fields in the Loja java object named "Loja"), so it failed. This seems logical, because it appears that the server is effectively returning this json structure:

{"Loja":{"nome":"teste","xValue":"20","yValue":"30","andar":"1"}}

Because of this, you have two primary options.

  1. Change the object that is passed to getForObject() or
  2. Change the json returned from the server.

For the first one, you can have your client do a getForObject() passing in an Object that contains a Loja.

Something like this class:

class MyObj {
    private Loja Loja;

    public void setLoja(Loja loja) {
        this.Loja = loja;
    }

    public Loja getLoja() {
        return this.Loja;
    }
}

being used with this call:

restTemplate.getForObject(url, MyObj.class);

Alternatively, you could have your server return the Loja objects fields as a part of the model, or better yet, return the instance of the Loja object that you created. Spring is smart enough to utilize Jackson to turn your POJO's into the proper json model you are expecting.

@RequestMapping("/android/played")
public Loja getLoja() {
    System.out.println("Android Resquest.");

    return new Loja("teste", "20", "30", "1");
}

Which would produce this json:

{"nome":"teste","xValue":"20","yValue":"30","andar":"1"}

Which would be readable by your getForObject() method on the client side just the way it is currently.

Comments