How to force Spring boot's @RequestBody to accept XML

Viewed 796

I have a spring boot application with groovy (jdk 11), I want to accept a xml request body. This is my controller:

@RestController
class UserController {
    @PostMapping(path = "/herd", produces = APPLICATION_XML_VALUE, consumes = APPLICATION_XML_VALUE)
    void saveHerd(@RequestBody Users request) {
        println user.name
    }
}

My xml request body:

<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user name="foo" age="4"/>
    <user name="Jack" age="8"/>
</users>

The users pojos:

class Users {
    List<UserDTO> user
}

class UserDTO {
    String name
    String age
}

The problem is that my request is returning a Content type 'application/xml;charset=UTF-8' not supported]

If I debug my application, I see that spring web is still expecting a json for some reason even if I have specified consumes to be XML.

Why is Spring-boot still expecting a json instead of an XML?

3 Answers

Try adding the following annotation on class level on your Users.class

      @XmlRootElement

Also check in your maven if jaxb library is included, since you don't mention which JDK you use. You need this library for marschalling unmarschalling xml but was removed from JDK 11 and onwards.

Edit: Since you use JDK 11 and jaxb is removed from this version add the following dependencies

<dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
  <version>2.3.0</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-core</artifactId>
  <version>2.3.0</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-impl</artifactId>
  <version>2.3.0</version>
</dependency>

I'm not sure if this is working for you but it was definitely working for me.

I was using Java 17 and added this library:

implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml'

And in the model, I used @JacksonXmlRootElement on the class and JacksonXmlProperty on its properties, if you need to accept value in CDATA section, use @JacksonXmlCData.

@JacksonXmlRootElement(localName = "xml")
public class CommonMessageReq {
    @JacksonXmlProperty(localName = "ToUserName")
    @JacksonXmlCData
    private String toUserName;
    @JacksonXmlProperty(localName = "FromUserName")
    @JacksonXmlCData
    private String fromUserName;
    @JacksonXmlProperty(localName = "CreateTime")
    private Long createTime;
    @JacksonXmlProperty(localName = "MsgType")
    @JacksonXmlCData
    private String msgType;
    
    // ...omit getter/setter methods...
}

@PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE})
public ResponseEntity<String> handle(@RequestBody CommonMessageReq request) {
    // ...omit details
}

Anyway, it's an alternative library of JAXB.

Add below dependency in pom.xml

<dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
</dependency>

Annotate the pojo with @XmlRootElement and the pojo has to implement Serializable for marshalling & unmarshalling the XML

@XmlRootElement
public class Device implements Serializable{
}

The Controller accepts both xml and json, unless restricted to XML using consumes = MediaType.APPLICATION_XML_VALUE

@PostMapping(path="/xmlTest")
    public String testXml(@RequestBody Device device){
        return device.getMake() + device.getModel();
    }
Related