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
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
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!
Most helpful comment
Using
Jaxb2XmlEncoderandJaxb2XmlDecoderfor now, please avoid cross-commenting.