Je développe et application Android qui communique avec mon serveur. Cette communication se fait à travers Spring framework et Jackson. J'envoie une demande pour mon serveur avec succès mais je ne reçois pas la réponse. Voici ce que j'ai fait:
Application Android:
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);
}
Classe Loja (j'en ai deux versions. Une dans l'application Android et une autre dans Server. Elles sont complètement identiques):
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;
}
}
Et voici mon contrôleur:
@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;
}
Avec tout cela, je reçois l'exception suivante dans l'application Android:
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
D'après ce que j'ai compris, cela ne correspond pas à ma classe de Loja. Mais ils sont exactement les mêmes! Quoi d'autre cela pourrait-il être?
L’exception semble indiquer que, bien que le mappeur de Jackson essaie de créer une variable Loja
à partir du json renvoyé, il a rencontré un champ json avec une clé de Loja qu’il ne savait pas comment gérer (car il n’existait aucun champ dans la variable Loja
Objet Java nommé "Loja"), il a donc échoué. Cela semble logique, car il semble que le serveur renvoie effectivement cette structure json:
{"Loja":{"nome":"teste","xValue":"20","yValue":"30","andar":"1"}}
Pour cette raison, vous avez deux options principales.
getForObject()
ou Pour le premier, vous pouvez demander à votre client d'effectuer une getForObject()
en passant une Object
contenant une Loja
.
Quelque chose comme cette classe:
class MyObj {
private Loja Loja;
public void setLoja(Loja loja) {
this.Loja = loja;
}
public Loja getLoja() {
return this.Loja;
}
}
utilisé avec cet appel:
restTemplate.getForObject(url, MyObj.class);
Vous pouvez également demander à votre serveur de renvoyer les champs des objets Loja
dans le modèle ou, mieux encore, de renvoyer l'instance de l'objet Loja
que vous avez créée. Spring est assez intelligent pour utiliser Jackson pour transformer vos POJO en modèle JSON approprié que vous attendez.
@RequestMapping("/Android/played")
public Loja getLoja() {
System.out.println("Android Resquest.");
return new Loja("teste", "20", "30", "1");
}
Qui produirait ce json:
{"nome":"teste","xValue":"20","yValue":"30","andar":"1"}
Ce qui serait lisible par votre méthode getForObject()
du côté client, telle qu’elle est actuellement.
J'ai résolu ce problème en créant un JSON Post
en utilisant 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();
}
La chose principale est que ma HttpEntity
je devais mettre à String
.
J'espère que ça peut aider quelqu'un.
J'ai découvert ce qui n'allait pas avec mon code. Nicholas avait raison, mon JSON était quelque chose comme:
{"Loja":{"nome":"teste","xValue":"20","yValue":"30","andar":"1"}}
et le champ Loja n'était pas reconnu lorsqu'il était envoyé à une application Android.
Voici ce que j'ai fait: j'ai créé une autre classe appelée Lojas qui n'a rien de plus qu'une liste Loja.
public class Lojas {
private List<Loja> lojas;
public Lojas() {
}
public List<Loja> getLojas() {
return lojas;
}
public void setLojas(List<Loja> lojas) {
this.lojas = lojas;
}
}
dans ma méthode getLoja (), je retourne cette toute nouvelle méthode créée:
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);
}
de cette façon, je peux demander une liste d'objets Loja à la fois. mais pour le faire, je devais changer de serveur afin de toujours renvoyer un objet List of Loja:
@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;
}
définir explicitement un constructeur vide m'a aidé dans mon cas, sinon mon test utilisant restTemplate échouerait.