REST. Jersey. How to programmatically choose what type to return: JSON or XML?

25,704

Solution 1

If your client wants to use a part of the URL to configure the response type, you can use a Servlet filter.

An easy way to implement overriding the representation (media type) could use a URL query parameter:

/resources/todo?format=json

The Servlet filter parses the URL query parameters, and if a format=json is present, replaces or adds the accept header "application/json".

Solution 2

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.


Can I create one class, annotate it with JAXB annotations(for XML support) and declare in web.xml for JSON (Jackson library) support?

You can always use an Application class to specify a MessageBodyReader/MessageBodyWriter for the JSON binding. I believe Jackson provides an implementation in its jar. Below is an example of an Application class that specifies MOXy as the JSON provider:

package org.example;

import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;

public class CustomerApplication  extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        HashSet<Class<?>> set = new HashSet<Class<?>>(2);
        set.add(MOXyJsonProvider.class);
        set.add(CustomerService.class);
        return set;
    }

}

Or I need to create separately two classes for JSON and XML?

EclipseLink JAXB (MOXy) offers native XML binding and is designed to enable you to use the same object model for both JSON and XML. You can integrate it into your JAX-RS application using the MOXyJsonProvider class:


How I can programmatically choose what type to return (JSON or XML)?

Server Side

You can specify that your service offers both XML and JSON messages using the @Produces annotation.

@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Path("{id}")
public Customer read(@PathParam("id") long id) {
    return entityManager.find(Customer.class, id);
}

For More Information

Client Side

You can use the MediaType to indicate the type of message. Below is an example using Jersey client APIs. Note how the URL is the same, just the requested media type is different.

Client client = Client.create();
WebResource resource = client.resource("http://localhost:8080/CustomerService/rest/customers");

// Get XML response as a Customer
Customer customer = resource.path("1")
    .accept(MediaType.APPLICATION_XML)
        .get(Customer.class);
System.out.println(customer.getLastName() + ", "+ customer.getFirstName());

// Get JSON response as a Customer
Customer customer = resource.path("1")
    .accept(MediaType.APPLICATION_JSON)
        .get(Customer.class);
System.out.println(customer.getLastName() + ", "+ customer.getFirstName());

For More Information

Solution 3

No need for seperate classes, what you need is seperate methods:

@GET
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Todo getXML() {
    Todo todo = new Todo();
    todo.setSummary("This is my first todo");
    todo.setDescription("This is my first todo");
    return todo;
}

Then in the client side, when you request for the service, you indicate in what format you want it:

// Get XML
System.out.println(service.path("rest").path("todo").accept(MediaType.TEXT_XML).get(String.class));
// Get XML for application
System.out.println(service.path("rest").path("todo").accept(MediaType.APPLICATION_XML).get(String.class));
// Get JSON for application
System.out.println(service.path("rest").path("todo").accept(MediaType.APPLICATION_JSON).get(String.class));
Share:
25,704
WelcomeTo
Author by

WelcomeTo

Updated on November 29, 2020

Comments

  • WelcomeTo
    WelcomeTo over 3 years

    I have 2 questions:

    1. Can I create one class, annotate it with JAXB annotations(for XML support) and declare in web.xml

    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    

    for JSON (Jackson library) support? Or I need to create separately two classes for JSON and XML?

    Or may be exist some more elegant way to cause REST service to return both JSON and XML?

    2. How I can programmatically choose what type to return (JSON or XML)?

    Thanks.

  • WelcomeTo
    WelcomeTo almost 12 years
    thanks, so I understand I need to sent accept header from client to get what type I need. But how I can return neccesary type from server?
  • Tomer
    Tomer almost 12 years
    Once you add the producess annotation, the framework will do it automatically for you based on the client request
  • WelcomeTo
    WelcomeTo almost 12 years
    ok ok:) I understand it) But what if I want to return neccesary type depending on some other facts (not on client's accept header)? Or in ither words, how to return xml if client's accept header is JSON?
  • Tomer
    Tomer almost 12 years
    I fail to see under what situation is that viable?! In all situation, the client is the one initiating the reequest and the client KNOWS in what format he would like to get the request, what you are suggesting will probably cause the client to hate you :)
  • WelcomeTo
    WelcomeTo almost 12 years
    for example I want to let client to choose type of response in URL: rest/get/cars/xml or rest/get/cars/json. Then depending on pathParam return right type of response.
  • Tomer
    Tomer almost 12 years
    Thats a different question, I'd suggest opening a new discussion
  • WelcomeTo
    WelcomeTo almost 12 years
    thanks, but can I configure response type unsing Response object when return from method?
  • mjn
    mjn almost 12 years
    Response can specify the content type: return Response.ok. (...) .type("application/json").build();
  • Ben
    Ben about 11 years
    If you're picky about URLs like I am..., you could use a file extension: /resources/todo.json or /resources/todo.xml
  • user2163960
    user2163960 over 5 years
    Jersey already supports selection by extension, if you enable it: stackoverflow.com/questions/28369396/… with the ServerProperties.MEDIA_TYPE_MAPPINGS (I know this question is older, just linking the answer you wanted, because google brought me here first for the answer of how to change types based on extension)