Quantcast
Channel: Active questions tagged rest - Stack Overflow
Viewing all articles
Browse latest Browse all 3667

RestClient: how to correctly unmarshall text/xml response

$
0
0

I need to consume an external REST API that returns not a json (application/json), but an xml (text/xml to be precise, not application/xml). I'm using the new RestClient introduced in Spring Boot 3.2.

This is an example of the data that the external api returns:

<Player><NAME>Someone</NAME></Player>

I can't change the response type based on the Accept header, the external api only returns stuff with the text/xml type. Of course, as it's an external api, I don't have a say in what is the return type.

I created this class:

@Data@AllArgsConstructor@NoArgsConstructor@XmlRootElement(name = "Player")@XmlAccessorType(XmlAccessType.FIELD)public class PlayerModel {    @XmlElement(name = "NAME")    private String name;}

And I tried this:

RestClient restClient = RestClient.create();PlayerModel result = restClient.get()            .uri(url)            .retrieve()            .body(PlayerModel.class);

But this crashed everything, as everytime I called the external api, it said that it didn't understand how to unmarshall something of type text/xml.

Then I read from spring docs about REST Clients about HTTP Message Conversion. There it said that there are many HttpMessageConverter, and that each one has a default media type that it supports. I guess that whatever RestClient is using by default doesn't support by default the type text/xml, so I decided to use one of the suggested. I was between MarshallingHttpMessageConverter and MappingJackson2XmlHttpMessageConverter.

With MarshallingHttpMessageConverter I had trouble, as I wanted to use an Unmarshaller made from JAXBContext (there will be code about this further down), which creates an Unmarshaller from jakarta.xml.bind.Unmarshaller. However, when creating a MarshallingHttpMessageConverter, if you try to pass this Unmarshaller to it (it accepts both an Unmarshaller and a Marshaller), it fails because it expects an Unmarshaller from org.springframework.oxm.Unmarshaller. I don't know about that, but maybe what I want is close to this.

As that didn't quite work, I used MappingJackson2XmlHttpMessageConverter, like so:

RestClient restClient = RestClient.builder()            .messageConverters(converters -> {                MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();                converter.setSupportedMediaTypes(List.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML));                converters.add(converter);            })            .build();

Now it doesn't explode, but when checking the variable PlayerModel result, the name is null, meaning it didn't catch it (should be Someone, but is null).

Lastly I resorted to catching it as a String, instead of a PlayerModel, and use a JAXB created Unmarshaller to unmarshall the xml content of the string into a playerModel:

RestClient restClient = RestClient.builder()            .messageConverters(converters -> {                MappingJackson2XmlHttpMessageConverter converter = new MappingJackson2XmlHttpMessageConverter();                converter.setSupportedMediaTypes(List.of(MediaType.APPLICATION_XML, MediaType.TEXT_XML));                converters.add(converter);            })            .build();String result = restClient.get()            .uri(url)            .retrieve()            .body(String.class);PlayerModel playerModel;try {    Unmarshaller unmarshaller = JAXBContext.newInstance(PlayerModel.class).createUnmarshaller();    playerModel = (PlayerModel) unmarshaller.unmarshal(new StringReader(result));} catch (JAXBException e) {    throw new RuntimeException(e);}return playerModel;

This works, but I want a better way to do it. I know there must be one, but I already used a lot of time for this. Maybe with some Bean configuration, or maybe with another HttpMessageConverter, or another Unmarshaller. The objective is this:

PlayerModel result = restClient.get()            .uri(url)            .retrieve()            .body(PlayerModel.class);

I'd appreciate any help I can get. Thanks :)))


Viewing all articles
Browse latest Browse all 3667

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>