ajax data source with objects and Java, Jersey, JAXB

ajax data source with objects and Java, Jersey, JAXB

theg33ktheg33k Posts: 6Questions: 0Answers: 0
edited May 2011 in DataTables 1.8
This problem seems to be specific to Java developers using Jersey (the most popular Java RESTful web service implementation). Anyways, I'm having trouble providing the data table JSON in the format it wants. By default JAXB/Jersey converts arrays with 1 item to a JSON object rather than an array of length 1. I got that figured out (solution to be provided below for others) but I'm still having trouble with zero length arrays.

With 1 object in my array, the JSON created (after the fix below) looks like this (and works)
[code]
{"aaData":[{"clientId":"test","credit":false,"debit":false,"displayName":"test","giftCard":false,"id":"4344A4E2-E0E0-4C03-9A8A-8BD550E6BA0E","password":"test","quasiCash":false,"userId":"test"}],"iTotalDisplayRecords":1,"iTotalRecords":1}
[/code]

When I have 0 items in the list, the JSON comes out with no aaData object, like this:
[code]
{"iTotalDisplayRecords":0,"iTotalRecords":0}
[/code]

This causes the data table to get stuck displaying "Loading data from server."

I tried adding a null object to my list, but then the result is this:
[code]
{"aaData":[{"nil":"true"}],"iTotalDisplayRecords":0,"iTotalRecords":0}
[/code]

Which is equally bad, it causes there to be a row filled with the word undefined.

Next I tried to hard-code a response string that looks like this:
[code]
{"iTotalDisplayRecords":0,"iTotalRecords":0,"aaData":[]}
[/code]

I thought that would work but it doesn't. It was a *little* bit better but still wound up with the data table displaying "Loading..."

So two questions

First, what is the appropriate format of "aaData" for an empty list?

Second, would it be possible to add a check in the constructor of the table such that if iTotalRecords=0 you just ignore aaData altogether and treat it as empty? This would be a little hack-ish but would save Java developers from having to write custom JSON serialization.

Here's a related discussion about JAXB/JSON and serialization for the yui data table with someone having the same issue.
http://jersey.576304.n2.nabble.com/array-handling-in-JAXB-to-JSON-conversion-td2417653.html




Okay, so for Java developers, here's how you get lists of 1 to serialize properly. First you have to know that JAXB uses the mapped builder by default. I don't particularly know what is special about that name other than it is different from "natural" serialization, which is what you want. So first let's create a new ContextResolver which uses the natural serialization method.

[code]
package sample.hello.config;

import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;
import javax.ws.rs.ext.ContextResolver;
import javax.xml.bind.JAXBContext;

import sample.hello.bean.Address;
import sample.hello.bean.Contact;

@Provider
public class JAXBContextResolver implements ContextResolver {

private JAXBContext context;
private Class[] types = {Contact.class, Address.class};

public JAXBContextResolver() throws Exception {
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
}

public JAXBContext getContext(Class<?> objectType) {
for (Class type : types) {
if (type == objectType) {
return context;
}
}
return null;
}
}
[/code]

That's pretty simple, the only thing of note is really to make sure whatever classes you want serialized in the "natural" way are listed in that types array.

The sticky part that doesn't seem to be documented anywhere that has examples for this is how to actually register your ContextResolver. It's actually really easy. You should already have something similar to this in your web.xml

[code]

Jersey REST Service
com.sun.jersey.spi.container.servlet.ServletContainer


com.sun.jersey.config.property.packages
sample.hello


com.sun.jersey.spi.container.ContainerRequestFilters
com.sun.jersey.api.container.filter.LoggingFilter


com.sun.jersey.spi.container.ContainerResponseFilters
com.sun.jersey.api.container.filter.LoggingFilter

1

[/code]

Just make sure your contextresolver has that @Provider annotation and that it lives inside a child package of the value configured for com.sun.jersey.config.property.packages.

Cheers,
G33k

Replies

  • allanallan Posts: 63,516Questions: 1Answers: 10,473 Site admin
    I would have thought that

    [code]
    {"iTotalDisplayRecords":0,"iTotalRecords":0,"aaData":[]}
    [/code]
    that would work. The only thing that looks 'odd' to me is that you've not got an sEcho parameter in there. I'd suggest adding that in, and I believe that will work - if not then it's a bug...

    It is possible to add in a check to see if aaData is defined or not - but I'm included to say that it is a mandatory parameter and if the result set is zero then it should just be an empty array. Is that a nightmare to add into the server-side code?

    Allan
  • logeloge Posts: 15Questions: 1Answers: 0
    His problem is definitely the missing sEcho token in the reply. And normally its no problem to return back an empty List which will be serialized to [] by most json marshallers in java. Thats what my experiences are....

    But of course its worth a thought if it would help people if iTotalRecords = 0 always results in empty table. No matter what aaData supplies. But thats a question of taste...

    Marc
  • jimjonahjimjonah Posts: 1Questions: 0Answers: 0
    Here is my JSON. This leaves the table stuck at "Processing.
    {"sEcho":0,"iTotalRecords":"0","iTotalDisplayRecords":"0","aaData":[]}.

    If I modify it to this:
    {"sEcho":0,"iTotalRecords":"0","iTotalDisplayRecords":"0","aaData":[{}]}

    The table columns are defined using mDataProp: fieldName, so the table is expecting objects, so it will try to parse the empty list of objects, complains that it couldn't find one of the named columns, then adds an empty row.
This discussion has been closed.