8

If there is a way, how to do this, I'd like to know the most elegant one. Here is the question: - Let's assume you have an abstract class Z - You have two classes inherited from Z: named A and B.

You marshal any instance (A or B) like this:

JAXBContext context = JAXBContext.newInstance(Z.class);
Marshaller m = context.createMarshaller();
m.marshal(jaxbObject, ...an outputstream...);

In the resulting XML you see what kind of instance it was (A or B).

Now, how do you unmarshall like

JAXBContext jc = JAXBContext.newInstance(Z.class);
Unmarshaller u = jc.createUnmarshaller();
u.unmarshal(...an inputstream...)

I get an UnmarshalException saying

"Exception Description: A descriptor with default root element {<my namespace>}<the root tag, e.g. A or B> was not found in the project]

javax.xml.bind.UnmarshalException"

So how do you do unmarshalling so that you get an instance of Z and then you can test AFTER unmarshalling, what it is? e.g. z instanceof A then... z instanceof B then something else... etc.

Thanks for any ideas or solutions.

I am using JRE1.6 with MOXy as JAXB Impl.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
basZero
  • 4,129
  • 9
  • 51
  • 89

4 Answers4

4

There is a similar question here.

Is it possible, to just unmarshall by providing Person.class and the unmarshaller finds out itself, whether it has to unmarshall to ReceiverPerson.class or SenderPerson.class?

@XmlRootElement(name="person")
public class ReceiverPerson extends Person {
  // receiver specific code
}

@XmlRootElement(name="person")
public class SenderPerson extends Person {
  // sender specific code (if any)
}

// note: no @XmlRootElement here
public class Person {
  // data model + jaxb annotations here
}
Community
  • 1
  • 1
basZero
  • 4,129
  • 9
  • 51
  • 89
  • Obviously, you always have to provide a list of classes the unmarshaller can use as target class, but this breaks the whole OO approach, you should not need to mention all possible subclasses! – basZero Apr 01 '11 at 16:04
2

So how do you do unmarshalling so that you get an instance of Z and then you can test >AFTER unmarshalling, what it is? e.g. z instanceof A then... z instanceof B then >something else...etc.

This should work...

Unmarshaller u = jc.createUnmarshaller();
Object ooo = u.unmarshal( xmlStream );
if ( ooo instanceof A )
{
    A myAclass = (A)ooo;
}
else if ( ooo instanceof B )
{
    B myBclass = (B)ooo;
}

I have tested this myself and it works.

Alex
  • 21
  • 1
  • 2
    That's fine, but not good enough. Let's say, one day you introduce class C, and even later maybe D. Is there a solution where you don't have to update your code section above? It should work automatically with all subclasses... – basZero Aug 27 '11 at 09:28
0

Every XML Documents Must Have a Root Element and if you want to use the same UnMarshaller for both instances your only possibility is to have a common root element such as:

<root>
  <A></A>
</root>

and your xsd File would look like this

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:annotation>
        <xs:documentation>
        example for stackoverflow
    </xs:documentation>
    </xs:annotation>
    <xs:element name="root" type="rootType"/>
    <xs:complexType name="rootType">
            <xs:choice>
                <xs:element name="A" type="AType"/>
                <xs:element name="B" type="BType"/>
            </xs:choice>
    </xs:complexType>

    ... your AType and BType definitions here

</xs:schema>
  • I just tried it out, does not work. The unmarshaller can not unmarshall it. It says: "This class does not define a public default constructor, or the constructor raised an exception." Of course, the abstract class Z can not be instantiated. What I expect is that the unmarshall detects the instance type information in the XML and unmarshall to the correct object. – basZero Apr 01 '11 at 14:41
  • There is a similar question, and I am testing now this answer: [link](http://stackoverflow.com/questions/619761/jaxb-inheritance-unmarshal-to-subclass-of-marshaled-class/657497#657497) – basZero Apr 01 '11 at 14:45
  • This answer does not solve the issue. I am also not using XSD at all. Just plain POJO <> JAXB <> XML. – basZero Apr 01 '11 at 14:53
0

THERE IS NO SOLUTION TO MY QUESTION!

Under any circumstances you have to tell the unmarshaller exactly what object it should unmarshall to.

basZero
  • 4,129
  • 9
  • 51
  • 89
  • Your assertion that the unmarshaller requires knowledge of the target type is not correct. In the code example by Alex, as soon as the unmarshaller has assigned a value to ooo, the object referred to by ooo is already an instance of whatever the target class is. How YOU choose to cast that instance is up to you as the list of qualifying subclasses is potentially infinite, the selection of which is almost certainly subject to Business Defined logic. – Larry Hector Mar 05 '13 at 13:50