spring-projects / spring-projects/spring-boot
Auto-configure Jersey with a JacksonJaxbXMLProvider when jackson-jaxrs-xml-provider is on the classpath
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 81.5k
- Forks
- 42.7k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 65
Description
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();
}
}
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading JacksonAutoConfiguration and Jackson2ObjectMapperBuilder, focusing on how the existing ObjectMapper bean is created and how createXmlMapper(true) is used. Check the JacksonJaxbXMLProvider integration described in the issue. Done means a configured XML mapper or equivalent Jersey provider is automatically available when the relevant classpath dependency is present, with existing configuration preserved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring-boot
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100