I started working with the Grails framework recently when I started working for my new company. We were writing an application that provided a RESTful service, and my team had opted to use JAX-RS to implement that service.
One of the frustrations I had while learning Grails had to do with some of the convenience Groovy provides. In particular, I ran into an issue where JAX-RS did not know how to handle maps, yet it handled lists (even with nested maps!) just fine. That’s certainly a workaround if I had to return a map, but it does not make sense to have an API call return a list of a map when only a single map makes sense.
The Problem
@GET
@Path("/api/test")
@Produces(["application/xml", "application/json"])
Response getTestCall()
{
ok [name: "Bud", foo: "bar"]
}
JAX-RS uses annotations to set up resources. The GET annotation tells the application that the function response to GET requests. The Path annotation defines the url associated with the call. Produces tells the application that the call will return either XML or JSON, depending on what the client requests.
The only line of code in the function is a great example of the grooviness that Groovy provides. ok is a static method that is imported above this code that simply returns a Response object with the 200 OK HTTP status. The function takes generic input (more on this soon) to use as the body for the response. Groovy does not require you to use parenthesis for functions in some cases, as you can see in this line. The notation after ok is what is used to create a new LinkedHashMap object. Really convenient right? So what happens when we hit this API call?
A message body writer for Java class java.util.LinkedHashMap, and Java type class java.util.LinkedHashMap, and MIME media type application/xml was not found
Out of the box, this code does not work. Neither JAX-RS nor the Grails plugin include a message body writer that will convert a LinkedHashMap into XML or JSON. If we were to add a set of brackets to the map, we have turned our map into a list containing a single map, and this works since a body writer that will convert a list object is included. While this could be considered a viable workaround, a list implies multiple (possible) results, while for the specific call it does not make sense to return multiple items.
Click through to see the solution after the break.