Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB and abstract classes

I'm trying to use JAXB to unmarshall some XML, but I'm getting an "Unable to create an instance of..." exception. I understand why--it's trying to make an instance of an abstract class. What I want is to have it make an instance of a specific implementing class. My goal with this is to have class-specific checks on setter methods. Maybe "qux" is a valid baz value for BarImpl, but BarImpl2 wants to do something else.

I got part of the way there by not annotating Foo, but if I unannotate bar, things get ugly.

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import org.junit.Test;


public class JAXBTest {

    @Test
    public void test() throws javax.xml.bind.JAXBException {
        String xml = 
            "<foo>" +
            "  <bar>" +
            "    <baz>qux</baz>" +
            "  </bar>" +
            "</foo>";

        javax.xml.bind.JAXBContext context = javax.xml.bind.JAXBContext.newInstance(
                FooImpl.class,
                BarImpl.class
        );

        javax.xml.bind.Unmarshaller unmarshaller = context.createUnmarshaller();

        unmarshaller.unmarshal(new java.io.StringReader(xml));
    }

    @XmlRootElement(name="foo")
    public static abstract class Foo {
        @XmlElement(name="bar")
        Bar bar;
    }

    @XmlRootElement(name="bar")
    public static abstract class Bar {
        @XmlElement(name="baz")
        String baz;
    }

    public static class FooImpl extends Foo { }
    public static class BarImpl extends Bar { }
}
like image 846
David Ehrmann Avatar asked Mar 06 '12 08:03

David Ehrmann


1 Answers

You could do the following:

  • Annotation the impl classes with @XmlRootElement instead of the abstract classes.
  • Mark the abstract classes with @XmlTransient (see http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)
  • Use @XmlElement(type=BarImpl.class) on the bar property to specify the concrete type (see http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html).

JAXBTest

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

import org.junit.Test;


public class JAXBTest {

    @Test
    public void test() throws javax.xml.bind.JAXBException {
        String xml = 
            "<foo>" +
            "  <bar>" +
            "    <baz>qux</baz>" +
            "  </bar>" +
            "</foo>";

        javax.xml.bind.JAXBContext context = javax.xml.bind.JAXBContext.newInstance(
                FooImpl.class,
                BarImpl.class
        );

        javax.xml.bind.Unmarshaller unmarshaller = context.createUnmarshaller();

        unmarshaller.unmarshal(new java.io.StringReader(xml));
    }

    @XmlTransient
    public static abstract class Foo {
        @XmlElements({
            @XmlElement(name="bar",type=BarImpl.class),
            @XmlElement(name="bar",type=BarImpl2.class),
        })
        Bar bar;
    }

    @XmlTransient
    public static abstract class Bar {
        @XmlElement(name="baz")
        String baz;
    }

    @XmlRootElement(name="foo")
    public static class FooImpl extends Foo { }

    @XmlRootElement(name="bar")
    public static class BarImpl extends Bar { }

    public static class BarImpl2 extends Bar { }
}
like image 156
bdoughan Avatar answered Nov 03 '22 12:11

bdoughan