简体   繁体   中英

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?

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.

I have discovered what was wrong with my code. Nicholas was right, my JSON was something like:

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

and the field Loja wasn't being recognized when sent to Android application.

Here's what I did: I created another class called Lojas which has nothing more than a Loja List.

public class Lojas {

    private List<Loja> lojas;

    public Lojas() {
    }

    public List<Loja> getLojas() {
        return lojas;
    }

    public void setLojas(List<Loja> lojas) {
        this.lojas = lojas;
    }
}

in my getLoja() method, I'm returning this brand new method created:

public Lojas 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, Lojas.class);
    }

this way, I can request an List of Loja object at once. but to do it I had to change server side to always return an List of Loja object:

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

        List<Loja> list = new ArrayList<Loja>();
        list.add(new Loja("teste", "20", "30", "1"));
        list.add(new Loja("teste2", "20", "30", "1"));
        ModelAndView mav = new ModelAndView();
        mav.addObject("lojas", list);
        return mav;
    }

I solved this problem making a JSON Post using RestTemplate .

HTTP_HEADERS.setContentType(MediaType.APPLICATION_JSON);

final ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
final String body = ow.writeValueAsString(convertedFireCsvBeans);
final HttpEntity<String> entity = new HttpEntity<>(body, HTTP_HEADERS);   
final ParameterizedTypeReference<String> responseType = new ParameterizedTypeReference<String>(){};
final ResponseEntity<String> responseEntity = REST_TEMPLATE.exchange(url, HttpMethod.POST, entity, responseType);
if(responseEntity.getStatusCode() != null){
            System.out.println("HTTP STATUS CODE: " + responseEntity.getStatusCode() +
                       "\n"+ responseEntity.getStatusCode().getReasonPhrase());
                    httpStatus = responseEntity.getStatusCode();                    
                }

The main thing is that my HttpEntity I had to set to String .

I hope it can help someone.

明确定义一个空的构造函数对我有帮助,否则我使用 restTemplate 的测试将失败。

在父对象中引入 no args 构造函数将起作用。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM