Spring-framework: Support XML Marshalling with jackson-dataformat-xml in WebFlux [SPR-15697]

Created on 23 Jun 2017  路  7Comments  路  Source: spring-projects/spring-framework

Jarno Walgemoed opened SPR-15697 and commented

Hi,

Jackson-xml marshalling seems to be broken in the 2.0.0 M2 milestone release for Spring boot which contains Spring 5.0 RC2 (with web-flux). I tried both with functional web routers and a 'classic' (simple) annotation based controller (in Kotlin):

data class Hello(val one: String, val two: String)

@Bean
fun apiRouter() = router {
    GET("/hello") { req ->
        ok().contentType(MediaType.APPLICATION_XML)
                .body(BodyInserters.fromObject(Hello("Hello", "World")))
    }
}

And

data class Hello(val one: String, val two: String)

@RestController
class HelloController {
    @GetMapping("/hello") fun sayHello() = Hello("Hello", "World")
}

The jackson XML marshaller doesn't seem to be registered automatically anymore.

Steps to reproduce

  • Create a Spring Boot project (start.spring.io) -> 2.0.0 M2 with the reactive web dependency;
  • Add a restcontroller that returns a simple entity (see above);
  • Add jackson-dataformat-xml dependency.

Call the endpoint with the application/xml accept header. The sever will respond with a 406 - not acceptable response. In the logging this shows up:

2017-06-23 12:39:42.629 ERROR 26064 --- [ctor-http-nio-2] o.s.w.s.h.ResponseStatusExceptionHandler : Response status 406 with reason "Could not find acceptable representation"

Verification

I've tried a similar approach with the latest stable Spring version and there it works as expected, depending on the provided accept header (application/xml or application/json) the application responds with the response in the requested format.


Affects: 5.0 RC2

3 votes, 6 watchers

web blocked enhancement

Most helpful comment

Using Jaxb2XmlEncoder and Jaxb2XmlDecoder for now, please avoid cross-commenting.

All 7 comments

Jarno Walgemoed commented

Issue originally posted in Spring Boot issue tracker: https://github.com/spring-projects/spring-boot/issues/9581

As per Brian Clozel's request I re-created it here.

S茅bastien Deleuze commented

We already supports XML in WebFlux but Jackson-based support could allow to provide Jackson-based XML + JSON webservices support which is quite useful when leveraging features like JSON Views.

To support that, we need async XML parsing support in jackson-dataformat-xml, I created the related issue on their GitHub issue tracker.

is there a work-around?

Using Jaxb2XmlEncoder and Jaxb2XmlDecoder for now, please avoid cross-commenting.

ok. if annotate my object woth jaxb-annotation (@XmlRootElement()) it works ok for the post-body (.body(Mono.just(requestClientData), RequestClientData.class)), but:
.bodyToMono(MyClass.class) - response returns alway null object. Have to use .bodyToMono(String.class). Is there a workaround for this? Thanks!

It feels like this is a question that would be better suited to Stack Overflow. As mentioned in the guidelines for contributing, we prefer to use the issue tracker only for bugs and enhancements.

I have managed to find a solution to this:

Firstly, I created a bean to encode and decode the XML:

@Configuration
public class WebClientConfig {
    @Bean
    public WebClient webClient() {
        return WebClient
        .builder()
        .baseUrl("Your base URL here")
        .exchangeStrategies(ExchangeStrategies.builder().codecs((configurer) -> {
            configurer.defaultCodecs().jaxb2Encoder(new Jaxb2XmlEncoder());
            configurer.defaultCodecs().jaxb2Decoder(new Jaxb2XmlDecoder());
        }).build())
    .build();
    }
}

This enables you to encode and decode the xml request and responses.

In the request object that I want to convert to XML before sending it, I added the @XmlRootElement(name="MyElement")

@XmlRootElement(name="MyElementRequestObject")
public class MyElementRequestObject {
}

By default, if you do not add in the name field on the XMLRootElement, it will make your xml into lowercase ( see https://stackoverflow.com/questions/9879433/jaxb-java-generating-xml-why-lowercase)

In my response object, I did something similar:

@NoArgsConstructor
@Getter
@ToString
@XmlRootElement(name = "MyElementResponseObject")
public class MyElementResponseObject {

    @XmlElement(name = "data")
    private List<String> listOfData;
}

Finally, putting it altogether, I can now make my post() request as needed:

 MyElementResponseObject block = webClient.post()
                .uri("your endpoint name here")
                .syncBody(new MyElementRequestObject())
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
                .accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML)
                .retrieve()
                .bodyToMono(MyElementResponseObject.class)
                .block();

        System.out.println("string returned: " + block.toString()); //just to test it worked

This enabled me to not have to return the result as an object, rather than a string.

Hope that helps!

Was this page helpful?
0 / 5 - 0 ratings