Saghir
Saghir

Reputation: 2445

Spring Boot Could not find acceptable representation for XML responses

I found multiple solutions for this exception and causes but none worked and it seems illogical.

I have REST service class as the following

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.MediaType;

import java.util.Map;
@RestController
public class UserRestService {

    @RequestMapping(value = "/userLogin", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody ResponseEntity<Map> login(@RequestBody Map<String, String> name) {
        ResponseEntity<Map> res = null;
        name.values().forEach(System.out::println);
        return new ResponseEntity<Map>(name, HttpStatus.OK);
    }
}

As you see, I try to create a POST request with request body as JSON and response as XML, printing the request in the process. It's just simple example but while the request is printed correctly which means it's been received and marshaled to Map. However the response status is 406 (not acceptable)

Exception in the log is

John
30
null
2018-08-16 14:05:48.440  WARN 22820 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
2018-08-16 14:05:48.481  WARN 22820 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation

the Request Header simply has

Accept:*/*
Content-Type:application/json

POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.cms</groupId>
    <artifactId>business</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <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.1</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Noting that adding the following dependency solves the problem

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Request Body { "name":"John", "age":30, "car":null }

Response if MediaType.APPLICATION_JSON_UTF8_VALUE is used for produces attribute of @RequestMapping is { "name":"John", "age":30, "car":null }

Response if MediaType.APPLICATION_XML_VALUE is used for produces attribute of @RequestMapping & jackson-dataformat-xml used is

<Map>
    <name>John</name>
    <age>30</age>
    <car/>
</Map>

Why using JAXB fails in this ?

Update 01

OK as @Simon comment, I created a sample object (class) to get as request and response with it. Same exact effect. Here is the updated code

public class MockupResponse {

    private String name;
    private Integer age;
    private String car;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getCar() {
        return car;
    }

    public void setCar(String car) {
        this.car = car;
    }

@Override
public String toString() {
        StringBuffer sb = new StringBuffer();
        if (null != name)
            sb.append("Name: " + name + "\n");
        if (null != age)
            sb.append("Age: " + age + "\n");
        if (null != car)
            sb.append("Car: " + car + "\n");

        return sb.toString();
}
}

Service

@RequestMapping(value = "/userLogin", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public @ResponseBody ResponseEntity<MockupResponse> login(@RequestBody MockupResponse sample) {
        System.out.println(sample);

        return new ResponseEntity<MockupResponse>(sample, HttpStatus.OK);
    }

Upvotes: 3

Views: 13472

Answers (1)

Simon Martinelli
Simon Martinelli

Reputation: 36223

Add @XmlRootElement to the class that must be serialized by JAXB like

@XmlRootElement
public class MockupResponse {

    private String name;
    private Integer age;
    private String car;
...
}

Then the XML serialization works.

Read more about that in the Spring Doc:

https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html

If you use Java 9 and later you must add JAXB and javax.activation to the dependencies:

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

Upvotes: 5

Related Questions