12

I am trying to implement a REST service using Spring 4.

The REST method will return a list of customer objects in XML. The application is annotation-driven.

For XML, I have used JAXB annotations. As per my understanding, Spring will use "Jaxb2RootElementHttpMessageConverter" out-of-box when it finds JAXB annotations.

The Customer POJO:

@XmlRootElement(name = "customer")
public class Customer {
private int id;
private String name;
private List favBookList;
@XmlAttribute
public int getId() {
    return id;
}
public void setId(int id) {
    this.id = id;
}
@XmlElement
public String getName() {
    return name;
}
public void setName(String name) {
    this.name = name;
}

@XmlElementWrapper(name = "booklist")
@XmlElement(name="book")
public List getFavBookList() {
    return favBookList;
}
public void setFavBookList(List favBookList) {
    this.favBookList = favBookList;
}
} 

I have annotated the REST service class as @RestController (as per Spring 4)

The REST method to return a list of customers in XML :

@RequestMapping(value="/customer-list.xml",produces="application/xml")
public  List<Customer> getCustomerListInXML(){
    List<Customer> customerList = new ArrayList<Customer>();
    Customer customerObj1 = new Customer();
    customerObj1.setId(1);
    customerObj1.setName("Vijay");
    ArrayList<String> favBookList1 = new ArrayList<String>();
    favBookList1.add("Book1");
    favBookList1.add("Book2");
    customerObj1.setFavBookList(favBookList1);
    customerList.add(customerObj1);
    Customer customerObj2 = new Customer();
    customerObj2.setId(2);
    customerObj2.setName("Rajesh");
    ArrayList<String> favBookList2 = new ArrayList<String>();
    favBookList2.add("Book3");
    favBookList2.add("Book4");
    customerObj2.setFavBookList(favBookList2);
    customerList.add(customerObj2);
    return customerList;
}

The result I expected, when I hit the URL :

 <customers>
  <customer id="1">
   <booklist>
    <book xsi:type="xs:string">Book1</book>
    <book xsi:type="xs:string">Book2</book>
   </booklist>
   <name>Vijay</name>
  </customer>
  <customer id="2">
   <booklist>
    <book xsi:type="xs:string">Book3</book>
    <book xsi:type="xs:string">Book4</book>
   </booklist>
   <name>Rajesh</name>
  </customer>
 </customers>

What I get :

HTTP 406 : The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.

Note :

When I try to return a Customer object in XML, it works as expected. However, I am unable to return a list of Customer objects in XML.

The application is developed using java 7 and it runs on Tomcat 7.

Need help with this. Thanks.

2 Answers 2

12

I was able to generate an XML of the list of customers.

First create a generic wrapper class (The intent behind this is to use this generic class to pass a list of objects of any class).

The Generic Wrapper Class :

@XmlRootElement
@XmlSeeAlso({Customer.class})
public class EntityList<T> {

 private List<T> listOfEntityObjects;

    public EntityList() {
        listOfEntityObjects = new ArrayList<T>();
    }

    public EntityList(List<T> listOfEntityObjects) {
        this.listOfEntityObjects = listOfEntityObjects;
    }

    @XmlAnyElement
    public List<T> getItems() {
        return listOfEntityObjects;
    }

The modified REST method :

@RequestMapping(value="/customer-list.xml",produces="application/xml")
public  EntityList<Customer> getCustomerListInXML(){
    List<Customer> customerList = new ArrayList<Customer>();
    Customer customerObj1 = new Customer();
    customerObj1.setId(1);
    customerObj1.setName("Vijay");
    ArrayList<String> favBookList1 = new ArrayList<String>();
    favBookList1.add("Book1");
    favBookList1.add("Book2");
    customerObj1.setFavBookList(favBookList1);
    customerList.add(customerObj1);
    Customer customerObj2 = new Customer();
    customerObj2.setId(2);
    customerObj2.setName("Rajesh");
    ArrayList<String> favBookList2 = new ArrayList<String>();
    favBookList2.add("Book3");
    favBookList2.add("Book4");
    customerObj2.setFavBookList(favBookList2);
    customerList.add(customerObj2);
    EntityList<Customer> listOfCustomers =   
    new EntityList<Customer>  (customerList);

    return listOfCustomers;
}

The XML Response :

<entityList>
 <customer id="1">
  <booklist>
   <book xsi:type="xs:string">Book1</book>
   <book xsi:type="xs:string">Book2</book>
  </booklist>
  <name>Vijay</name>
 </customer>
 <customer id="2">
  <booklist>
   <book xsi:type="xs:string">Book3</book>
   <book xsi:type="xs:string">Book4</book>
  </booklist>
  <name>Rajesh</name>
 </customer>
</entityList>

Note :

The root element of the generated XML is entityList (which is taken from the name of the wrapper class)

However, I would like that the XML Root Element should be the plural of the entity name i.e customers in this case.

Any thoughts or suggestions?

Sign up to request clarification or add additional context in comments.

3 Comments

Maybe you could use @XmlTransient annotation. It basicly prevents the mapping of field/property. more here.
@XmlTransient annotation serves a different purpose. In my case, I would like to see customers as the root element instead of entityList Say in future, I have an entity called Car, I can reuse this generic wrapper and I should get cars as the root element in the xml.
Does the way of wrapper works for application/json too? No changes?
1

406 Not Acceptable The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.

Your back-end service is responding that the response type what it is returning is not provided/or it is different in the Accept-Type HTTP header in your request.

Find out the response content type returned by your server. Provide this content type in your request HTTP Accept header value. http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html HTTP Status codes

Other observations I think you should use parameterized type of List.

private List<String> favBookList; 
public List<String> getFavBookList() {
   return favBookList;
}
public void setFavBookList(List<String> favBookList) {
   this.favBookList = favBookList;
}

2 Comments

I used Firefox "Inspect Network Request" to inspect the request and response headers. The request header accept type is text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8 and the response header content type is text/html;charset=utf-8 (which I believe is due to the HTTP Status 406 html page I get). When I return an object of Customer in XML, the accept type in request header is the same as above and the content type in the response header is application/xml (In this case I am able to see the generated XML)
I used the parameterized type of List as per your suggestion. It removed the xsi:type="xs:string" from <book> tag just as I would have liked. Thanks for that suggestion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.