简体   繁体   中英

Determine subclass when unmarshalling with JAXB

I will try to provide a SSCCE of my problem.

I have this hierarchy of classes:

Class Base:

@XmlRootElement
@XmlSeeAlso({ A.class, B.class })
@XmlAccessorType(XmlAccessType.FIELD)
public class Base {
    @XmlAttribute
    private String baseData;

    public String getBaseData() {
        return baseData;
    }

    public void setBaseData(String baseData) {
        this.baseData = baseData;
    }
}

Class A extends Base

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="A", propOrder = { "dataA1", "dataA2" })
@XmlRootElement(name = "Base", namespace = "")
public class A extends Base {
    private String dataA1;
    private String dataA2;

    public String getDataA1() {
        return dataA1;
    }

    public void setDataA1(String dataA1) {
        this.dataA1 = dataA1;
    }

    public String getDataA2() {
        return dataA2;
    }

    public void setDataA2(String dataA2) {
        this.dataA2 = dataA2;
    }
}

Class B extends Base

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="B", propOrder = { "dataB1", "dataB2" })
@XmlRootElement(name = "Base", namespace = "")
public class B extends Base {
    private String dataB1;
    private String dataB2;

    public String getDataB1() {
        return dataB1;
    }

    public void setDataB1(String dataB1) {
        this.dataB1 = dataB1;
    }

    public String getDataB2() {
        return dataB2;
    }

    public void setDataB2(String dataB2) {
        this.dataB2 = dataB2;
    }
}

But when I try to test it:

public class Test {

    public static void main(String[] args) throws Exception {
        Base base = new A();
        base.setBaseData("BaseDataA");
        ((A) base).setDataA1("DataA1");
        ((A) base).setDataA2("DataA2");

        JAXBContext jc = JAXBContext.newInstance(Base.class, A.class, B.class);

            // Marshal the object
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            StringWriter sw = new StringWriter();
            marshaller.marshal(base, sw);

            // Unmarshal the object
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            String generatedXml = sw.toString();
            System.out.println(generatedXml);
            Base objectFromUnmarshall = (Base) unmarshaller.unmarshal(new StringReader(generatedXml));
            System.out.println(objectFromUnmarshall);

            // Re-marshal the object
            System.out.println("");
            sw = new StringWriter();
            marshaller.marshal(objectFromUnmarshall, sw);
            System.out.println(sw.toString());
    }
}

This is the output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Base baseData="BaseDataA">
    <dataA1>DataA1</dataA1>
    <dataA2>DataA2</dataA2>
</Base>

jaxb.B@5c0369c4

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Base baseData="BaseDataA"/>

I marchal an object A, but when unmarshalling it creates an instance of B!

Why? How can I tell him to unmarshalh to the good instance?

NOTE: I am using Sun JAXB implementation and I cannot change it.

Well, the only way I have found to handle it is to use an XMLAdapter (based in this great article ).

public class BaseAdapter  extends XmlAdapter<BaseAdapter.BaseHandler,Base>{

    @Override
    public Base unmarshal(BaseHandler baseHandler) throws Exception {
        if (null == baseHandler) {
            return null;
        }
        if (null != baseHandler.dataA1) {
            A a = new A();
            a.setDataA1(baseHandler.dataA1);
            a.setDataA2(baseHandler.dataA2);            
            return a;
        } else {
            B b = new B();
            b.setDataB1(baseHandler.dataB1);
            b.setDataB2(baseHandler.dataB2);
            return b;
        }
    }

    @Override
    public BaseHandler marshal(Base base) throws Exception {
         if (null == base) {
            return null;
        }
        BaseHandler baseHandler = new BaseHandler();
        if (base instanceof A) {
            A a = (A) base;
            baseHandler.dataA1 = a.getDataA1();
            baseHandler.dataA2 = a.getDataA2();
        } else {
            B b = (B) base;
            baseHandler.dataB1 = b.getDataB1();
            baseHandler.dataB2 = b.getDataB2();
        }
        return baseHandler;
    }

    public static class BaseHandler {
        public String dataA1;
        public String dataA2;
        public String dataB1;
        public String dataB2;
    }
}

Here you are the complete SSCCE code if somebody is interested.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM