17

I try to return a list of Strings in Jersey as JSON and XML. I thought this would be trivial.

My first try was to write something like this

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/bar")
public List<String> get() {
    return dao.get();
}

and I expected an output similiar to this: ["string1", ..., "stringN] unfortunately I got this:

com.sun.jersey.api.MessageException: A message body writer for Java class java.util.LinkedList, and Java type java.util.List<java.lang.String>, and MIME media type application/json was not found 

Then I wrote a wrapper StringList for List

@XmlRootElement
public class StringList {

    private List<String> data;

    public StringList() {
    }

    public StringList(List<String> data) {
        this.data = data;
    }

    public List<String> getData() {
        return data;
    }

    public void setData(List<String> data) {
        this.data = data;
    }
}

and modified the facade to

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Path("/foo")
public StringList stringlist() {
    return new StringList(sl());
}

Which works great for Lists with more items than 1.

{"data":["foo","bar"]}

Unfortunately I get two unexepected results for one or zero elements

{"data": "just one"} // for one element i would expect {"data": ["just one"]}

null // for no elements i would expect {"data": []}

Am I doing something completly wrong? How can I fix this?

Fábio
  • 3,291
  • 5
  • 36
  • 49

6 Answers6

8

Okay, I could fix it by searching the samples

This does work, but it can only be used for JSON and not for XML

@GET
@Produces({MediaType.APPLICATION_JSON})
@Path("/get")
public JSONArray get() {;
    return new JSONArray(dao.getStringList());
}

Fixes problem, but is there also a generic approach?

4

You could use javax.ws.rs.core.GenericEntity:

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Path("/foo")
public GenericEntity<List<String>> stringlist()
{
  List<String> list = Arrays.asList("test", "as");

  return new GenericEntity<List<String>>(list) {};
}
bluish
  • 26,356
  • 27
  • 122
  • 180
sdorra
  • 2,382
  • 18
  • 16
  • 24
    Has anyone had any luck with this approach? I'm seeing the same exception with the code above... – John McCann Mar 16 '12 at 21:48
  • 1
    I see the same exception as John McCann mentioned with this approach – Will Jan 16 '13 at 16:56
  • 3
    looks like something is missed, the approach doesn't work. The " A message body reader for Java class javax.ws.rs.core.GenericEntity, and Java type class javax.ws.rs.core.GenericEntity, and MIME media type application/json was not found" message was throwed – Cyril Deba Apr 05 '13 at 15:04
  • Doesn't work with Jersey 2.0 n tomcat 7, Getting `description The server encountered an internal error that prevented it from fulfilling this request.` – sailor Nov 27 '14 at 05:30
  • Not sure why this is the accepted answer. It doesn't work. Tomcat 7, Jersey 2, Moxy – sigi Feb 02 '17 at 12:40
1

In order to convince Jersey to output lists the way you want to, you need to provide an own ContextResolver:

@Provider
public class JaxbContentResolver implements ContextResolver<JAXBContext> {

    private Log log = LogFactory.getLog(getClass());
    private JAXBContext context;

    public JaxbContentResolver() throws JAXBException {
        Class[] types = {StringList.class};
        context = new JSONJAXBContext(JSONConfiguration.mapped().rootUnwrapping(true).arrays("data").build(), types);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        log.trace("Entering Test-getContext for type: " + objectType.getSimpleName());
        return context;
    }
}

This way lists will appear the way you want them to.

NOTE: One downside of this approach is that you need to maintain one additional spot in your code; in case you want to add another (list wrapper) class to your REST interface, you need to remember to go to above code and add that class in your ContextResolver...

0

You need to use the alternative JSON configuration JSONConfiguration.natural().

Best you create your own ContextResolver using that alternative configuration as a provider and tell it which classes it is responsible for.

I am unaware of a method to use the alternative configuration globally in another way.

t0mm13b
  • 34,087
  • 8
  • 78
  • 110
0

In addition to the provided answers, if you still get MessageBodyWriter not found, try adding a dependency such as:

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-moxy</artifactId>
    </dependency>
Skylar Saveland
  • 11,116
  • 9
  • 75
  • 91
0

My solution to this was a wrapper class (found it somewhere). It works now. I don't understand the thoughts behind not supporting javas List class as a root element. Perhaps it has to do with some json specification / best practice i'm not aware of.

But for now im using this:

public class Houses {
    private List<String> houses;

    // Needed for jersey
    public Houses() { }

    public Houses(List<String> houses) {
        this.houses = houses;
    }

    public void setHouses(List<String> houses) {
        this.houses = houses;
    }

    public List<String> getHouses() {
        return this.houses;
    }
}
sigi
  • 99
  • 11