Conversion xml avec Jackson

Posted on jeu. 06 décembre 2018 in Spring

Prise en charge json et xml dans un même controleur REST

Les bases

Supposons que nous voulions récupérer une liste d'items par l'intermédiaire de la requête REST suivante :

@GetMapping("/")
public List<Item> getItems() {
    return itemService.getAll();
}

En l'absence de Jackson, une exception sera levée : java.lang.IllegalArgumentException: No converter found for return value of type: class java.util.Arrays$ArrayList

Ajoutons-le dans les dépendances :

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

A partir du moment où jackson est déclaré dans les dépendances, JSON est le format par défaut. Il est donc inutile d'initialiser un Bean à travers une classe de configuration, comme on pourrait le faire dans l'exemple suivant :

@Configuration
public class MessageConverterConfig {

    @Bean
    public MappingJackson2HttpMessageConverter
        mappingJacksonHttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter
        = new MappingJackson2HttpMessageConverter();
        converter.setPrettyPrint(true);
        converter.setDefaultCharset(StandardCharsets.UTF_8);

        return converter;
    }
...    

Voilà comme se présente notre controlleur REST :

@RestController
@RequestMapping(value = "/items")
public class ItemController {
    @Autowired
    private ItemService itemService;

    @GetMapping("/")
    public List<Item> getItems() {
        return itemService.getAll();
    }
...

L'annotation @RestController (disponible à partir de Spring 4), au niveau de la classe, est un raccourci spécifiant que toutes les méthodes du controlleur retourneront un objet directement dans le corps de la réponse HTTP. Cela évite ainsi d'annoter chacune d'elles avec @ResponseBody.
Cette inclusion fait ici appel à une implémentation de l'interface HttpMessageConverter, en l'occurrence MappingJackson2HttpMessageConverter, dans le package org.springframework.http.converter.json.

XML vs JSON

Maintenant, on voudrait également retourner cette liste d'items au format xml. Les solutions qu'on trouve ici ou là sont pour la plupart inutilement complexes.
Déclarons tout d'abord une nouvelle dépendance (jackson-dataformat-xml) dans le fichier pom.xml :

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Spécifions ensuite le format de sortie par défaut, au niveau du controlleur. C'est le rôle de l'attribut produces :

@RestController
@RequestMapping(
    value = "/items",
    produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
...

Précisons enfin le format de sortie dans la méthode de notre controlleur :

@GetMapping(value = "/")
public List<Item> getItems() {
   return itemService.getAll();
}

@GetMapping(
    value ="/xml", 
    produces = MediaType.APPLICATION_XML_VALUE)
public List<Item> getItemsXml() {
    return itemService.getAll();
}

On dispose donc de deux méthodes se différenciant par leur nom, le segment final de leur point d'accès et la valeur de l'attribut produces, précisé au niveau du controlleur dans le premier cas :
MediaType.APPLICATION_JSON_UTF8_VALUE
au niveau de la méthode dans le second :
MediaType.APPLICATION_XML_VALUE.

La requête ne sera prise en compte que si la valeur de l'en-tête HTTP Accept correspond à la valeur de cet attribut.

MappingJackson2HttpMessageConverter sera utilisé dans le premier cas, MappingJackson2XmlHttpMessageConverter, dans l'autre.

Conclusion

La prise en compte de différents formats de retour est finalement très simple à mettre en oeuvre et n'est d'ailleurs pas limitée aux seuls formats json ou xml. D'autres formats plus atypiques sont également envisageables : texte, RSS ou PDF.