Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to refactor XSD so that unmarshalling does not return JAXBElement

I have the following schema:

<xsd:schema xmlns:bar="http://www.foo.org/bar"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:annox="http://annox.dev.java.net"
        xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
        targetNamespace="http://www.foo.org/bar"
        jaxb:extensionBindingPrefixes="annox" jaxb:version="2.1" elementFormDefault="qualified">

    <xsd:element name="unit" type="bar:unit" />

    <xsd:complexType name="unit">
        <xsd:annotation>
            <xsd:appinfo>
                <annox:annotate>@javax.xml.bind.annotation.XmlRootElement(name="unit")</annox:annotate>
            </xsd:appinfo>
        </xsd:annotation>
            <xsd:sequence>
            <xsd:any processContents="skip" />
        </xsd:sequence>
    </xsd:complexType>

</xsd:schema>

When I unmarshall this XML

<unit xmlns="http://www.foo.org/bar">
    <text>Name</text>
</unit>

the returned object is javax.xml.bind.JAXBElement<Unit>, however I would like to get org.foo.bar.Unit back. I need this because unmarshalling in my case happens implicitly by JAX-RS provider or SpringWeb.

Observations:

  • If I remove/replace <xsd:any processContents="skip" /> declaration, JAXB starts to return org.foo.bar.Unit.
  • If I remove <xsd:element name="unit" type="bar:unit" /> declaration, JAXB starts to return org.foo.bar.Unit (although one need to disable validation during unmarshalling).

Thus I would say that given XSD is the smallest XSD that demonstrates the problem.

Questions: Why JAXB wraps org.foo.bar.Unit into JAXBElement for above combination? From what I see, there is no way the XSD type unit can have tag name different from unit, so why JAXB needs this factory method?

@XmlElementDecl(namespace = "http://www.foo.org/bar", name = "unit")
public JAXBElement<Unit> createUnit(Unit value) { ... }

The project demonstrating the problem for JAXB 2.2.7 is here. When run it outputs the following:

Running org.foo.bar.UnitTest
>>> Class is: javax.xml.bind.JAXBElement
>>> XML is: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><unit xmlns="http://www.foo.org/bar"><text>Name</text></unit>
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.318 sec <<< FAILURE!
like image 331
dma_k Avatar asked Sep 16 '16 09:09

dma_k


People also ask

How do you Unmarshal Jaxbelement?

To unmarshal an xml string into a JAXB object, you will need to create an Unmarshaller from the JAXBContext, then call the unmarshal() method with a source/reader and the expected root object.

What is JAXB marshalling and Unmarshalling?

JAXB definitionsMarshalling is the process of transforming Java objects into XML documents. Unmarshalling is the process of reading XML documents into Java objects. The JAXBContext class provides the client's entry point to the JAXB API. It provides API for marshalling, unmarshalling and validating.

What is ObjectFactory in JAXB?

jaxb package. An ObjectFactory allows you to programatically construct new instances of the Java representation for XML content. The Java representation of XML content can consist of schema derived interfaces and classes representing the binding of schema type definitions, element declarations and model groups.

What is XmlElementRef?

Annotation Type XmlElementRef. @Retention(value=RUNTIME) @Target(value={FIELD,METHOD}) public @interface XmlElementRef. Maps a JavaBean property to a XML element derived from property's type. Usage. @XmlElementRef annotation can be used with a JavaBean property or from within XmlElementRefs.


3 Answers

Adding to Ian's answer, Any complex element that is named by root element will have factory method annotated with @XmlElementDecl().

You can resolve this, by moving the complex type declaration inline like below.

<xsd:schema xmlns= "http://www.foo.org/bar" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:annox="http://annox.dev.java.net" xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    targetNamespace="http://www.foo.org/bar" jaxb:extensionBindingPrefixes="annox"
    jaxb:version="2.1" elementFormDefault="qualified">
    <xsd:element name="unit">
        <xsd:complexType>
            <xsd:annotation>
                <xsd:appinfo>
                    <annox:annotate>@javax.xml.bind.annotation.XmlRootElement(name="unit")
                    </annox:annotate>
                </xsd:appinfo>
            </xsd:annotation>
            <xsd:sequence>
                <xsd:any processContents="skip" />
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

ObjectFactory.class (no JAXBElement factory method generated here)

@XmlRegistry
public class ObjectFactory {


    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.foo.bar
     * 
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link Unit }
     * 
     */
    public Unit createUnit() {
        return new Unit();
    }

}

Test class:

@Test
public void testUnmarshalling() throws JAXBException, SAXException {
    JAXBContext context = JAXBContext.newInstance(Unit.class);

    Unmarshaller unmarshaller = context.createUnmarshaller();

    unmarshaller.setSchema(SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
            .newSchema(new StreamSource(getClass().getClassLoader().getResourceAsStream("common.xsd"))));

    Object unit = unmarshaller.unmarshal(getClass().getResourceAsStream("unit.xml"));

    System.out.println(">>> Class is: " + unit.getClass().getName());

    StringWriter writer = new StringWriter();
    context.createMarshaller().marshal(unit, writer);

    System.out.println(">>> XML is: " + writer.toString());

    //assertTrue(unit instanceof Unit);
}

Test xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<unit xmlns="http://www.foo.org/bar">
    <text>Name</text>
</unit>

output :

>>> Class is: org.foo.bar.Unit
>>> XML is: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><unit xmlns="http://www.foo.org/bar"><text>Name</text></unit>
like image 120
ulab Avatar answered Nov 03 '22 19:11

ulab


From what I see, there is no way the XSD type unit can have tag name different from unit, so why JAXB needs this factory method?

On the contrary - you will always get a JAXBElement when your schema uses a named complex type by reference. With a named complex type there's always the possibility that the type might be used for a different element (maybe in another importing schema) or that the element may use a subtype of the named type rather than the top type itself.

Unwrapped root elements are used when the global xsd:element declaration has a nested anonymous complexType, as in that scenario the unmarshaller knows that those kinds of substitutions can't happen.

like image 3
Ian Roberts Avatar answered Nov 03 '22 19:11

Ian Roberts


If you are doing something like this:

JAXBContext jaxbContext = JAXBContext.newInstance(Unit.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

JAXBElement<Unit> root = jaxbUnmarshaller.unmarshal(new StreamSource(
        file), Unit.class);
Unit unit = root.getValue();

Try maybe:

Unit unit = JAXBIntrospector.getValue(jaxbUnmarshaller.unmarshal(new StreamSource(
        file), Unit.class);
like image 1
Lazar Lazarov Avatar answered Nov 03 '22 19:11

Lazar Lazarov