JAX-RS vs Collections vs JSON Arrays

The Problem

Looks like I’ve managed to get myself into a pickle. I have a (Jersey) JAX-RS app, which automagically publishes a WADL. If I then generate a Client (using wadl2java), I end up with generated code that doesn’t work.

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<Foo> get() {
		return myList;
	}

Foo is a simple POJO with an @XmlRootElement annotation. The resulting JSON is perfectly fine: I get a JSON Array with a bunch of Foo objects. Perfect!

The generated WADL is a different matter:

    <method id="get" name="GET">
        <response>
            <!-- foo is a reference to an XSD Complex Type ... but where's my list? -->
            <ns2:representation element="foo" mediaType="application/json"/>
        </response>
    </method>

For some reason, the WADL generator is smart enough to realise that we’re dealing with Foo instances. But it’s too stupid to realise that we’re dealing with more than one.

If you then generate a Client using wadl2java, you’ll end up with something like this:

    public Foo getAsFoo() {
        return response.readEntity(Foo.class);
    }

Well that’s not going to work, is it? Trying to read a single Foo when you’ve got an array of them. And indeed … you get a wonderful exception.

Internal Exception: java.lang.ClassCastException: Foo cannot be cast to java.util.Collection

This seems to be a fundamental XML vs JSON problem. After all there is no such thing as an “XML Array”. Either you have one element, or you have multiple elements nested under a common root.

I could solve this by not relying on a plain JSON Array and encapsulating the result.

// Not this:
[
   {
      "foo":"bar"
   },
   {
      "bar":"baz"
   }
]
 
// But this:
{
   "listOfFoos":[
      {
         "foo":"bar"
      },
      {
         "bar":"baz"
      }
   ]
}

But then that’s ugly. And instead of using a native Java collection I’ll have to create a useless intermediary object. And it would break compatibility with current API clients.

The Solution

Whelp … I haven’t found one yet. To be continued, I hope. But I did manage to find a workaround, thanks to Adam Bien’s blog.

The incorrectly generated getAsFoo() method doesn’t work. But we can use getAsJson() instead — which doesn’t necessarily have to return a JSON string.

    List<Foo> foos = client.getFoo().getAsJson(
        new GenericType<List<Foo>>() {
        }
    );

GenericType is a dirty JAX-RS hack in my opinion, but it works. It’s a shame that I have to rely on getAsJson(), though. It would’ve been much cleaner to use the getAsFoo() method directly.

On donating to open source

Some time ago, Gabriel Weinberg, the guy behind the excellent Duck Duck Go search engine, started a bit of a discussion about open source donations. He set up a website where companies can make a pledge to donate x% of their profits to worthy projects. I donated 15% of my company’s estimated profit this year. Half went towards an individual hacker who will remain nameless and genderless. The other half went to the OpenBSD project, because face it, where would you be without OpenSSH?

FOSS Tithe.org