7

It seems JAXB can't read what it writes. Consider the following code:

interface IFoo {
    void jump();
}

@XmlRootElement
class Bar implements IFoo {
    @XmlElement
    public String y;

    public Bar() {
        y = "";
    }

    public Bar(String y) {
        this.y = y;
    }

    @Override
    public void jump() {
        System.out.println(y);
    }
}

@XmlRootElement
class Baz implements IFoo {
    @XmlElement
    public int x;

    public Baz() {
        x = 0;
    }

    public Baz(int x) {
        this.x = x;
    }

    @Override
    public void jump() {
        System.out.println(x);
    }
}

@XmlRootElement
public class Holder {
    private List<IFoo> things;

    public Holder() {
        things = new ArrayList<>();
    }

    @XmlElementWrapper
    @XmlAnyElement
    public List<IFoo> getThings() {
        return things;
    }

    public void addThing(IFoo thing) {
        things.add(thing);
    }
}

// ...

try {
    JAXBContext context = JAXBContext.newInstance(Holder.class, Bar.class, Baz.class);

    Holder holder = new Holder();
    holder.addThing(new Bar("1"));
    holder.addThing(new Baz(2));
    holder.addThing(new Baz(3));

    for (IFoo thing : holder.getThings()) {
        thing.jump();
    }

    StringWriter s = new StringWriter();
    context.createMarshaller().marshal(holder, s);

    String data = s.toString();

    System.out.println(data);

    StringReader t = new StringReader(data);
    Holder holder2 = (Holder)context.createUnmarshaller().unmarshal(t);

    for (IFoo thing : holder2.getThings()) {
        thing.jump();
    }
}
catch (Exception e) {
    System.err.println(e.getMessage());
}

It's a simplified example, of course. The point is that I have to store two very differently implemented classes, Bar and Baz, in one collection. Well, I observed that they have pretty similar public interface, so I created an interface IFoo and made them two to implement it. Now, I want to have tools to save and load this collection to/from XML. Unfortunately, this code doesn't quite work: the collection is saved, but then it cannot be loaded! The intended output is

1
2
3
some xml
1
2
3

But unfortunately, the actual output is

1
2
3
some xml
com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to testapplication1.IFoo

Apparently, I need to use the annotations in a different way? Or to give up on JAXB and look for something else? I, well, can write "XMLNode toXML()" method for all classes I wan't to (de)marshal, but...

Joker_vD
  • 3,715
  • 1
  • 28
  • 42
  • Check the documentation for the usage of @XmlSeeAlso – Adam Arold Jun 20 '12 at 20:56
  • Possible duplicate of http://stackoverflow.com/questions/4144296/marshalling-a-list-of-objects-with-jaxb, which also asks about a list of an interface type, and has a different, working approach in the accepted answer. – Andy Thomas Oct 01 '15 at 16:13

1 Answers1

10

Try the following @XmlAnyElement(lax=true). The lax flag tells the JAXB (JSR-222) implementation to match elements to domain objects based their @XmlRootElement and @XmlElementDecl annotations. Without it the contents are treated as DOM nodes.

@XmlRootElement
public class Holder {
    private List<IFoo> things;

    public Holder() {
        things = new ArrayList<>();
    }

    @XmlElementWrapper
    @XmlAnyElement(lax=true)
    public List<IFoo> getThings() {
        return things;
    }

    public void addThing(IFoo thing) {
        things.add(thing);
    }
}

For More Information

bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 6
    It works. Thank you! But gosh, all this JAXB stuff is like black magic, I have yet to find a comrehensible guide on it. – Joker_vD Jun 20 '12 at 20:57
  • 1
    @Joker_vD - Checkout my Java XML & JSON Binding blog (http://blog.bdoughan.com/) and the EclipseLink JAXB (MOXy) User Guide (http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy) – bdoughan Jun 20 '12 at 21:00
  • What are the advantages and disadvantages of this approach compared to the approach you provided in your answer to the earlier question linked below? (E.g., the earlier answer seems to presume that the set of implementations is known to the class holding the list.) http://stackoverflow.com/a/4145801/202009 – Andy Thomas Oct 01 '15 at 16:16