How can I customize serialization of a list of JAXB objects to JSON?

Viewed 51898

I'm using Jersey to create a REST web service for a server component.

The JAXB-annotated object I want to serialize in a list looks like this:

@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
    "id", "name"
})
public class XMLDistribution {
    private String id;
    private String name;
    // no-args constructor, getters, setters, etc
}

I have a REST resource to retrieve one distribution which looks like this:

@Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
    @GET
    @Produces("application/json")
    public XMLDistribution retrieve(@PathParam("id") String id) {
        return retrieveDistribution(Long.parseLong(id));
    }
    // business logic (retrieveDistribution(long))
}

I also have a REST resource to retrieve a list of all distributions, which looks like this:

@Path("/distributions")
public class RESTDistributions {
    @GET
    @Produces("application/json")
    public List<XMLDistribution> retrieveAll() {
        return retrieveDistributions();
    }
    // business logic (retrieveDistributions())
}

I use a ContextResolver to customize JAXB serialization, which is currently configured like this:

@Provider
@Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    public JAXBJSONContextResolver() throws Exception {
        JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
        b.nonStrings("id");
        b.rootUnwrapping(true);
        b.arrays("distribution");
        context = new JSONJAXBContext(b.build(), XMLDistribution.class);
    }
    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return context;
    }
}

Both REST resources work, as well as the context resolver. This is an example of output for the first one:

// path: /distribution/1
{
  "id": 1,
  "name": "Example Distribution"
}

Which is exactly what I want. This is an example of output for the list:

// path: /distributions
{
  "distribution": [{
    "id": 1,
    "name": "Sample Distribution 1"
  }, {
    "id": 2,
    "name": "Sample Distribution 2"
  }]
}

Which is not quite what I want.

I don't understand why there is an enclosing distribution tag there. I wanted to remove it with .rootUnwrapping(true) in the context resolver, but apparently that only removes another enclosing tag. This is the output with .rootUnwrapping(false):

// path: /distribution/1
{
  "distribution": {
    "id": 1,
    "name": "Example Distribution"
  }
} // not ok
// path: /distributions
{
  "xMLDistributions": {
    "distribution": [{
      "id": 1,
      "name": "Sample Distribution 1"
    }, {
      "id": 2,
      "name": "Sample Distribution 2"
    }]
  }
}

I also had to configure .arrays("distribution") to always get a JSON array, even with only one element.

Ideally, I'd like to have this as an output:

// path: /distribution/1
{
  "id": 1,
  "name": "Example Distribution"
} // currently works
// path: /distributions
[{
  "id": 1,
  "name": "Sample Distribution 1"
}, {
  "id": 2,
  "name": "Sample Distribution 2"
}]

I tried to return a List<XMLDistribution>, a XMLDistributionList (wrapper around a list), a XMLDistribution[], but I couldn't find a way to get a simple JSON array of distributions in my required format.

I also tried the other notations returned by JSONConfiguration.natural(), JSONConfiguration.mappedJettison(), etc, and couldn't get anything resembling what I need.

Does anyone know if it is possible to configure JAXB to do this?

2 Answers
Related