Skip to content

Auto-configure Jersey with a JacksonJaxbXMLProvider when jackson-jaxrs-xml-provider is on the classpath #31924

@brandon1024

Description

@brandon1024

Problem

Spring Boot has fantastic support for Jackson. When using Jackson for JSON serialization/deserialization--especially with spring-mvc--it's easy to configure Jackson through the spring.jackson.* properties (thanks autoconfigure!).

Where this solution begins to fall short is when trying to use Jersey instead of Spring MVC, with Jackson for XML marshalling/unmarshalling. Ideally, the framework should expose a configured XmlMapper bean, or provide an easy way to build an XmlMapper that is configured, which we can then pass to the Jackson JAXB provider. Unfortunately I can't find a way to make that work cleanly.

Here's what I have for configuration:

@Configuration
@Slf4j
public class JerseyResourceSpringConfig {

    @Bean
    public ResourceConfig jerseyResourceConfig() {
        final var resourceConfig = new ResourceConfig();
        // ...
        resourceConfig.register(JacksonJaxbXMLProvider.class);
        return resourceConfig;
    }
}

It took me a while to figure out the spring.jackson.* properties weren't being respected, it wasn't immediately obvious (maybe some documentation enhancements would help). Internally Jersey will build an XmlMapper with its own defaults but they don't respect spring.jackson.* properties, which is obvious now given that Jersey doesn't really integrate with Spring in this way.

Alternatively it's possible to do something like this instead:

@Configuration
@Slf4j
public class JerseyResourceSpringConfig {

    @Bean
    public ResourceConfig jerseyResourceConfig() {
        final var resourceConfig = new ResourceConfig();
        // ...
        final var mapper = new XmlMapper();
        // configure mapper
        resourceConfig.register(new JacksonJaxbXMLProvider(mapper, JacksonJaxbXMLProvider.DEFAULT_ANNOTATIONS));
        return resourceConfig;
    }
}

The tricky part is configuring this XmlMapper correctly from those spring.jackson.* properties. From what I've seen there's no elegant way to do this in this specific configuration. Taking a look at JacksonAutoConfiguration, an ObjectMapper bean is created and some other goodies, and Jackson2ObjectMapperBuilder supports building XmlMapper, but manually exposing an XmlMapper bean doesn't seem ideal.

Here's what worked for me:

    @Bean
    public ResourceConfig jerseyResourceConfig(final JacksonJaxbXMLProvider provider) {
        final var resourceConfig = new ResourceConfig();
        // ...
        resourceConfig.register(provider);
        return resourceConfig;
    }

    @Bean
    public JacksonJaxbXMLProvider provider(final Jackson2ObjectMapperBuilder builder) {
        final var mapper = (XmlMapper) builder.createXmlMapper(true)
                .findModulesViaServiceLoader(true)
                .build();
        return new JacksonJaxbXMLProvider(mapper, JacksonJaxbXMLProvider.DEFAULT_ANNOTATIONS);
    }

It's not clear to me why I need to find jackson modules through the service loader.. Wouldn't the builder be configured to do this? Can someone explain why this might be needed here?

Proposed Solution

One option is to update the existing JacksonAutoConfiguration to register a configured XmlMapper bean, perhaps if XmlMapper is on the classpath. It could leverage all of the existing autoconfiguration!

        @Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
	static class JacksonObjectMapperConfiguration {

		@Bean
		@Primary
		@ConditionalOnMissingBean
		ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
			return builder.createXmlMapper(false).build();
		}

                @Bean
		@Primary
		@ConditionalOnMissingBean
		XmlMapper jacksonXmlMapper(Jackson2ObjectMapperBuilder builder) {
			return builder.createXmlMapper(true).build();
		}
	}

Metadata

Metadata

Assignees

No one assigned

    Labels

    status: blockedAn issue that's blocked on an external project changetype: enhancementA general enhancement

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions