2

Here is my Request class which holds a generics request:

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author lorddoskias
 */
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Request {

    private List<StringStringMapEntry> parameters = new ArrayList<StringStringMapEntry>();
    private List<StringStringMapEntry> headers = new ArrayList<StringStringMapEntry>();
    @XmlAnyElement(lax=true)
    private Object requestBody = null;  // this has to reference a JAXB-enabled object
    private String resourcePath;


    public Request() {
    }

    public Object getRequestBody() {
        return requestBody;
    }

    public void setRequestBody(Object requestBody) {
        this.requestBody = requestBody;
    }

Some parts are omitted for brevity but the essence is there. Then I have the following Sequence class:

import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author lorddoskias
 */
@XmlRootElement
public class Sequence {

    private String sequenceName;
    private long numReads;
    private int sequenceLength;

    public Sequence() {
        /* Required by JAXB */
    }

    public Sequence(String name, long numReads, int length) {
        sequenceName = name;
        this.numReads = numReads;
        sequenceLength = length;
    }

    public long getNumReads() {
        return numReads;
    }

    public void setNumReads(long numReads) {
        this.numReads = numReads;
    }

    public int getSequenceLength() {
        return sequenceLength;
    }

    public void setSequenceLength(int sequenceLength) {
        this.sequenceLength = sequenceLength;
    }

    public String getSequenceName() {
        return sequenceName;
    }

    public void setSequenceName(String sequenceName) {
        this.sequenceName = sequenceName;
    }
}

And I do something like that:

List<Sequence> seq = new ArrayList<Sequence>();
        seq.add(seq1);
        seq.add(seq2);

        client.insertIndividualStatistics(PostRequest.assemblyIndividualStats(66, seq));

Here is the insertIndividualStatistics method:

public static Request assemblyIndividualStats(int stepId, List<Sequence> l) {

    Request req = new Request();
    req.setResourcePath("/stats/assembly/individual");

    req.addParameter("id", String.valueOf(stepId));
    req.setRequestBody(l);

    return req;
}

And when I try to WebResource res = client.resource(bla).path("my-path").post(req); I get :

Exception in thread "main" com.sun.jersey.api.client.ClientHandlerException: javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
 - with linked exception:
[javax.xml.bind.JAXBException: class uk.org.infectogenomics.model.rest.MySequenceListWrapper nor any of its super class is known to this context.]

I thought JAXB supported simple Lists if they weren't a root object? I even tried something different - wrapping the list in a simple JAXB annotated class:

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author lorddoskias
 */
@XmlRootElement
public class MySequenceListWrapper {

    @XmlElement(name = "List")
    private List<Sequence> list;

    public MySequenceListWrapper() {/*JAXB requires it */

    }

    public MySequenceListWrapper(List<Sequence> list) {
        this.list = list;
    }

    public List<Sequence> getList() {
        return list;
    }
}

And setting that as the requestBody but then I get a similar exception but instead of it saying that ArrayList is unrecognized it says that MySequenceListWrapper is unrecognized. I've read the article from Blaise Doughan blog and I suspect that at least in the latter case it should work since I have the appropriate XmlAnyElement annotation?

bdoughan
  • 147,609
  • 23
  • 300
  • 400
LordDoskias
  • 3,121
  • 3
  • 30
  • 44

1 Answers1

1

You could use a JAX-RS ContextResolver to make a JAXBContext that is aware of the MySequenceListWrapper class. Or you could leverage the @XmlSeeAlso annotation as follows:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlSeeAlso({MySequenceListWrapper.class})
public class Request {
    ...
}
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • The XmlSeeAlso is compulsory when using XmlAnyElement or not? Also can I have a list of XmlSeeAlso for a finite set of objects I suspect might show up in the Request class? – LordDoskias Apr 26 '12 at 16:09
  • 1
    `@XmlSeeAlso` is not compulsory when using `@XmlAnyElement`. It is just a mechanism for ensuring that a class is included when creating a `JAXBContext`. You are using the Jersey client APIs, since you have not registered a `ContextResolver`, a JAXBContext will be created on the return type and classes will be pulled in as the class model is traversed. Classes not visited in this traversal will not become mapped classes. The `@XmlSeeAlso` when placed on a mapped class will cause the referenced classes to be mapped as well. `@XmlSeeAlso` takes an array of classes so you can reference many. – bdoughan Apr 26 '12 at 17:42
  • And is there a way to avoid using the wrapper object? As I have stated in the question - I can recall reading somewhere that JAXB can manage marshalling/unmarshalling Lists without needing special intervention? – LordDoskias Apr 26 '12 at 18:07
  • Just so I have your use case straight, an instance of `MySequenceWrapper` gets set on `Request.requestBody` and you are asking if you can set a `List` instead? – bdoughan Apr 26 '12 at 19:03
  • Precisely, and the only reason I'm using a wrapper is so that I can facilitate marshalling/unmarshalling without having to write custom boiler-plate code – LordDoskias Apr 26 '12 at 19:05