Java – JAXB “nor any of its super class is known to this context” avoid @XmlSeeAlso

javajava-ee-6jax-rsjaxb

Explanation & Workaround

Currently I am using JAX-RS and letting JAXB bindings automatically handle converting the data to XML and JSON for me in a JEE6 project. Everything is working absolutely fantastically until I try to create a generic response object to wrap all of my information in.

When I attempt to use my generic response class com.eln00b.Wrapper (which contains a private Object result attribute within it) I get:

javax.xml.bind.MarshalException – with linked exception: [com.sun.istack.SAXException2: class com.eln00b.CustomObject nor any of its super class is known to this context. javax.xml.bind.JAXBException: class com.eln00b.CustomObject nor any of its super class is known to this context.]

So I add to com.eln00b.Wrapper:

@XmlSeeAlso ({com.eln00b.CustomObject})
public class Wrapper {
}

Everything works fine.

The Problem

I want this to be extremely generic. I do not want t constantly add classes to the @XmlSeeAlso annotation on the com.eln00b.Wrapper class. How do I have the system automatically locate all of my classes for the JAXB context?

Even if it's a hack where I use something like Reflections to load the data, that's fine. I'm just not sure how to get the context to load all of that data without the @XmlSeeAlso annotation. With the large amount of annotations I will be creating it will just simply not work.

How It Worked Manually

It worked manually just by adding the data like so doing manual conversions. However, I do not want to use manual XML/JSON creation unless I absolutely need to (I don't want to deal with content negotiation or anything like that).

Sample:

JAXBContext.newInstance(new Class[] {Wrapper.class, CustomObject.class});

Best Solution

So here is what the essence of the custom resolver looks like:

@Provider
@Produces ({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class JaxbContextResolver implements ContextResolver<JAXBContext> {

  @Override
  public JAXBContext getContext(Class<?> type) {
    // load appropriate context data
    Class[] bindTypes = ...

    // create 
    try {
      return JAXBContext.newInstance(bindTypes);
    } catch (JAXBException e) {
      // todo:  this can be handled better but works for the example
      throw new RuntimeException(e);
    }
  }
}

Now, the processing for "load appropriate context data" is pretty simple. By basically mimicking @XmlSeeAlso using runtime data:

  1. Create a custom something (annotation, processing method, whatever) that marks a particular field/method as "contextual"
  2. Load the field/method data pulling the data types out
  3. Make sure you do not load duplicates and check for infinite recursion possibilities

Now, I used some caching to help make things more efficient for myself. I also created a slightly more complex setup for my root object where it actually kept track of the class data on its own and made it pretty speedy. I also created an alternative that marked classes as "contextual" that I used package inspection to load via annotations and just automatically add to the context but I have not checked efficiency on that yet. I have some ideas for a 3rd implementation, but I want to get more benchmarking completed.

Related Question