views:

75

answers:

2

I have the following code:

private SetMultimap<String, Dynamic> dynamicFields = TreeMultimap.create(Ordering.natural(), new Comparator<Dynamic>() {
  @Override
  public int compare(Dynamic o1, Dynamic o2) {
   return o1.getTitle().compareTo(o2.getTitle());
  }
 });

which gives me the following exception.

IllegalAnnotationsException SetMultimap is an interface, and JAXB can't handle interfaces

My question is, how come this doesn't work but this does:

List<Dynamic> test = new ArrayList<Dynamic>();

And how can I fix the SetMultimap so that JAXB is happy?

+1  A: 

You need to add annotations to tell JAXB the possible runtime types of the reference, like this:

@XmlElements({
@XmlElement(type = TreeMultimap.class, name = "treeMultimap"),
@XmlElement(type = MultiHashMap.class, name = "hashMultimap")
})
private SetMultimap<String, Dynamic> dynamicFields = TreeMultimap.create(Ordering.natural(), new Comparator<Dynamic>() {
  @Override
  public int compare(Dynamic o1, Dynamic o2) {
    return o1.getTitle().compareTo(o2.getTitle());
  }
});

With the code above you can also replace the field with an instance of MultiHashMap if you would like to. You need to add a @XmlElement for each possible runtime type. If you won't ever use the hash multimap, one @XmlElement with TreeMultimap.class is enough:

@XmlElement(type = TreeMultimap.class, name = "treeMultimap")
private SetMultimap<String, Dynamic> dynamicFields = TreeMultimap.create(Ordering.natural(), new Comparator<Dynamic>() {
  @Override
  public int compare(Dynamic o1, Dynamic o2) {
    return o1.getTitle().compareTo(o2.getTitle());
  }
});
disown
This didn't work. I got exception saying:`com.google.common.collect.TreeMultimap does not have a no-arg default constructor.` TreeMultimap indeed does not have any no-arg default constructor. Perhaps it just doesn't work.
Shervin
JAXB is a bit bean-crazy in the sense that it expects every object to have a default constructor and mutable members. Not very nice IMO. There are several way to work around this problem, however. In your case I would go for a factory class. Use @XmlType(factoryClass=MyFactory.class...) to declare a factory for TreeMultimap, either on the package level (if possible, haven't tried), or subclass TreeMultimap.
disown
It would be great if we had a working version of this in SO. :) While I deeply respect Kohsuke Kawaguchi, a star, I always thought he was bored midways doing JAXB and left it rather unfinished.
Dimitris Andreou
Kohsuke's contribution to the specification (JSR-222) was invaluable, however JAXB is far more than a one man show. I have personally been involved in JAXB implemantations since 1.0, and on the spec side since 2.0. JAXB implementations such as Metro (the RI) and MOXy (I lead this one) continue to develop extensions that will eventually get rolled back into a future spec. If you have suggestions for enhancements please send them to the appropriate product forums.
Blaise Doughan
+1  A: 

The difference between List/ArrayList and SetMultimap is that one is a Java collection, and the other is a data structure outside the normal Java collection hierarchy. This means JAXB considers it a normal class.

The JAXB spec does not support interfaces. You are probably using the Metro JAXB implementation which also does not support interfaces. Some JAXB implementations such as MOXy can support interfaces, but some JAX-WS implementations depend upon a particular JAXB impl, and substituting JAXB implementations is not always possible:

Your best bet is to use a parameter level annotation to convert SetMultimap to a class that can be handled by JAXB:

Blaise Doughan