Question

I am trying to use JAXB's introspection to marshall and unmashall some existing domain objects marked up with JAXB annotations. Most things work as expected, but I am having quite a bit of trouble getting a fairly simple class to serialize. This class is used as an @XmlElement on a number of beans and looks something like:

public class Range<E extends Comparable<E>> implements Serializable {
    protected boolean startInclusive, endInclusive;
    protected E       start, end;

    public Range(){
            startInclusive = endInclusive = true;
    }

    public boolean contains(E value){...}

    public E getEnd() {
            return end;
    }

    public void setEnd(E end) {
            this.end = end;
    }

    public boolean isEndInclusive() {
            return endInclusive;
    }

    public void setEndInclusive(boolean endInclusive) {
            this.endInclusive = endInclusive;
    }

    public E getStart() {
            return start;
    }

    public void setStart(E start) {
            this.start = start;
    }

    public boolean isStartInclusive() {
            return startInclusive;
    }

    public void setStartInclusive(boolean startInclusive) {
            this.startInclusive = startInclusive;
    }
}

I have tried to do the following, with no success, JAXB is still angry with the interface Comparable.

public class DoubleRange extends Range<Double> {}

Using both Range and DoubleRange as return types for the bean getter's yields an exception like:

java.lang.Comparable is an interface, and JAXB can't handle interfaces.
    this problem is related to the following location:
        at java.lang.Comparable
        at protected java.lang.Comparable com.controlpath.util.Range.start
        at example.util.Range
        at example.util.DoubleRange
        at public example.util.DoubleRange example.domain.SomeBean.getRange()
        at example.domain.SomeBean

I realize that in most cases List<T> and Map<T, U> only work because the JAXB specification has special provisions for those types when they are encountered on beans, but is there any way to convey what I want to the JAXB introspection engine without having to reimplement range with non-generic fields?

Was it helpful?

Solution

You could write a custom adapter (not using JAXB's XmlAdapter) by doing the following:

1) declare a class which accepts all kinds of elements and has JAXB annotations and handles them as you wish (in my example I convert everything to String)

@YourJAXBAnnotationsGoHere
public class MyAdapter{

  @XmlElement // or @XmlAttribute if you wish
  private String content;

  public MyAdapter(Object input){
    if(input instanceof String){
      content = (String)input;
    }else if(input instanceof YourFavoriteClass){
      content = ((YourFavoriteClass)input).convertSomehowToString();
    }else if(input instanceof .....){
      content = ((.....)input).convertSomehowToString();
    // and so on
    }else{
      content = input.toString();
    }
  }
}

// I would suggest to use a Map<Class<?>,IMyObjToStringConverter> ...
// to avoid nasty if-else-instanceof things

2) use this class instead of E in your to-be-marshalled class

NOTES

  • Of course this would not work for complex (nested) data structures.
  • You have to think how to unmarshall this back again, could be more tricky. If it's too tricky, wait for a better proposal than mine ;)

OTHER TIPS

How about

public class Range<**E extends Number**> implements Serializable { ...
  • Number is a class

  • I bet JAXB knows default marshalling/unmarshalling rules for Number

For unmarshalling to specific type, you need XmlAdapter as I described here: JAXB inheritance, unmarshal to subclass of marshaled class

Try something like Simple XML Serialization it comes with support for generic types in XML elements with a number of annotations such as @Element and @ElementList. The programming model is very similar, but simpler than JAXB.

Actually, it is not quite clear to me why this would not work. It seems like JAXB should be able to resolve specific subtype correctly: if (and only if!) this type is NOT the root type (which it is not as per your description). I mean, it is just a Bean; so if bean with T replaced with direct type works, so should generic version iff using sub-classing to bind types (as is done in example).

So perhaps it could be a bug in implementation?

So it looks like the problem is the erasure of E on start and end is Comparable. If it can't handle interfaces you could try Object, but I would hope it complains at that too (either now or later). Possibly you could make Range abstract and specialise it for each specific E. I should know more about JAXB.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top