Psycho Punch
Psycho Punch

Reputation: 6892

How do I use JAXB annotations with Spring RestTemplate?

I'm trying to automatically deserialize XML formatted response using Spring's RestTemplate. I'm using Jackson's jackson-dataformat-xml module, for which Spring Boot is set to auto-configure. I want to use JAXB annotations in the class I want to deserialize to, but it won't seem to work. Here's a sample of what I want the class to look like:

@XmlRootElement(name="Book")
public class Book {

    @XmlElement(name="Title")
    private String title;
    @XmlElement(name="Author")
    private String author;

}

This is based on the following XML sample:

<Book>
    <Title>My Book</Title>
    <Author>Me</Author>
</Book>

However, with class annotated like that above, the fields are always set null. I did some experiments and found out that the deserialization works if I use Jackson's @JsonProperty to annotate the child elements:

@XmlRootElement(name="Book")
public class Book {

    @JsonProperty("Title")
    private String title;
    @JsonProperty("Author")
    private String author;

}

It works, but somehow I feel like it's kind of awkward. Is there a way to get the JAXB annotations work like in my first example?

Jackson provides jackson-module-jaxb-annotations module for the XML databinding to work with JAXB annotations. However, I'm not sure how to setup the ObjectMapper being used by RestTemplate to use this module.

Upvotes: 10

Views: 10204

Answers (4)

Romain
Romain

Reputation: 321

If someone still have to deal with this kind of issue, Springboot provide an elegant solution :

Any beans of type com.fasterxml.jackson.databind.Module are automatically registered with the auto-configured Jackson2ObjectMapperBuilder and are applied to any ObjectMapper instances that it creates. This provides a global mechanism for contributing custom modules when you add new features to your application.

So adding this in one of your configuration class should be enough:

@Bean
public Module jaxbModule() {
    return new JaxbAnnotationModule();
}

Source: https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-customize-the-jackson-objectmapper

Upvotes: 5

xsalefter
xsalefter

Reputation: 640

If solution by @Psycho Punch still not working for you, This is another alternative:

  • add com.fasterxml.jackson.dataformat:jackson-dataformat-xml dependency.
  • use XmlMapper instead of ObjectMapper to your MappingJackson2XmlHttpMessageConverter. For example:

    XmlMapper xmlMapper = new XmlMapper();
    xmlMapper.registerModule(new JaxbAnnotationModule());
    
    forEach(converter -> {
        ((MappingJackson2XmlHttpMessageConverter) converter).setObjectMapper(xmlMapper)
    });
    

HTH

Upvotes: 2

Psycho Punch
Psycho Punch

Reputation: 6892

To address this issue, I needed to register an instance of JaxbAnnotationModule to every ObjectMapper used by converters added to Spring's RestTemplate. The class is included in Jackson's jackson-module-jaxb-annotations module, which I added to my build through Gradle.

With the dependency added to my project, what I did next was to configure the RestTemplate used by my application. The ObjectMapper instances are being used by MappingJackson2XmlHttpMessageConverters configured automatically by Spring. I had to register JaxbAnnotationModule instance to each ObjectMapper used in every converter, so the first task was to find all MappingJackson2XmlHttpMessageConverters using:

//create module
JaxbAnnotationModule jaxbAnnotationModule = new JaxbAnnotationModule();

restTemplate.getMessageConverters().stream().filter(converter -> {
    return converter instanceof MappingJackson2XmlHttpMessageConverter;
})

Once I had all the relevant converters, I then registered the module to each of their ObjectMappers:

forEach(converter -> {
    ((MappingJackson2XmlHttpMessageConverter) converter)
            .getObjectMapper()
            .register(jaxbAnnotationModule);
});

Upvotes: 5

Andremoniy
Andremoniy

Reputation: 34900

I suspect that in second case Spring simply ignores root level JAXB annotation, because by default Jackson will resolve name of the class properly.

In order to use JAXB annotations you have to use library jackson-xc

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-xc</artifactId>
</dependency>

These artilces may be also useful:

1) http://wiki.fasterxml.com/JacksonJAXBAnnotations

2) http://springinpractice.com/2011/12/06/jackson-json-jaxb2-xml-spring

3) How can we configure the internal Jackson mapper when using RestTemplate?

Upvotes: 0

Related Questions