JAXB 2.x: How to unmarshal an XML without knowing the target class?
Asked Answered
W

4

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.

Wesla answered 1/4, 2011 at 12:17 Comment(0)
W
0

THERE IS NO SOLUTION TO MY QUESTION!

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

Wesla answered 2/5, 2011 at 8:22 Comment(1)
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.Queenie
W
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
}
Wesla answered 1/4, 2011 at 14:51 Comment(1)
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!Wesla
A
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.

Acrocarpous answered 25/8, 2011 at 20:0 Comment(1)
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...Wesla
H
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>
Haworth answered 1/4, 2011 at 12:42 Comment(3)
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.Wesla
There is a similar question, and I am testing now this answer: linkWesla
This answer does not solve the issue. I am also not using XSD at all. Just plain POJO <> JAXB <> XML.Wesla
W
0

THERE IS NO SOLUTION TO MY QUESTION!

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

Wesla answered 2/5, 2011 at 8:22 Comment(1)
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.Queenie

© 2022 - 2024 — McMap. All rights reserved.