Reputation: 1720
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?
Upvotes: 10
Views: 20596
Reputation: 11
What I did was the following:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Marshallable {}
(...)
public class A extends Marshallable {
(...)
public class GenericType<T extends Marshallable> {
A a = new A();
jaxbMarshaller.marshal(new GenericType(a), outputStream);
And now Jaxb will work with every type that extends Marshallable, even as part of a generic type.
Upvotes: 0
Reputation: 19453
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
Upvotes: 1
Reputation: 116512
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?
Upvotes: 0
Reputation: 19453
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
Upvotes: 5
Reputation: 147164
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.
Upvotes: -1
Reputation: 7189
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.
Upvotes: 0