Why does JAXB sometimes map to JAXBElement?
Asked Answered
P

2

18

There is a placeholder answer over at the unofficial guide with a link to an article which (to me) seems quite unrelated.

I use XJC to generate my JAXB classes and while most of them map to each other as expected, some elements get mapped to JAXBElement<Foo>. This is most annoying for graphs with cycles, where sometimes the parent node of a Foo element will be the JAXBElement<Foo>, which doesn't itself have a parent property, breaking the cycle.

I can think of various workarounds, but it would be much nicer if someone could explain this behaviour to me. Why does JAXB sometimes map a <Foo> element to JAXBElement<Foo> instead of Foo?

Pagas answered 7/9, 2010 at 11:51 Comment(3)
See #3639813 for an explanationChromaticness
@skaffman: Eh? I don't see how your answer relates to whether xjc chooses JAXBElement<Foo> over Foo.Nape
@Chris: Because it is determined in part by the choice of anonymous types or names types in the source schema.Chromaticness
M
11

JAXBElement is used to preserve the element name/namespace in use cases where enough information is not present in the object model. The most common occurence is with substitution groups:

With Substitution Group:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org" 
    xmlns="http://www.example.org" 
    elementFormDefault="qualified">

    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="anElement"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="anElement" type="xs:string"/>

    <xs:element name="aSubstituteElement" type="xs:string" substitutionGroup="anElement"/>

</xs:schema>

Will generate:

package org.example;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "anElement"
})
@XmlRootElement(name = "root")
public class Root {

    @XmlElementRef(name = "anElement", namespace = "http://www.example.org", type = JAXBElement.class)
    protected JAXBElement<String> anElement;

    public JAXBElement<String> getAnElement() {
        return anElement;
    }

    public void setAnElement(JAXBElement<String> value) {
        this.anElement = ((JAXBElement<String> ) value);
    }

}

Without Substitution Group:

If you remove the substitution group:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.example.org" 
    xmlns="http://www.example.org" 
    elementFormDefault="qualified">

    <xs:element name="root">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="anElement"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="anElement" type="xs:string"/>

</xs:schema>

The following class will be generated:

package org.example;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "anElement"
})
@XmlRootElement(name = "root")
public class Root {

    @XmlElement(required = true)
    protected String anElement;

    public String getAnElement() {
        return anElement;
    }

    public void setAnElement(String value) {
        this.anElement = value;
    }

}

You may also get a JAXBElement when you unmarshal, compare the following examples:

Majoriemajority answered 7/9, 2010 at 14:3 Comment(2)
Thanks, substitution groups are indeed where this is happening. So is it then a bug that in afterUnmarshal(Unmarshaller u, Object parent) these substitutable classes are being passed their own JAXBElement as their parent? Especially since there doesn't seem to be a way to get the real parent element from the JAXBElement.Pagas
This appears to be a bug in Metro JAXB (the reference implementation, included in Java SE 6). In the MOXy JAXB implementation (eclipse.org/eclipselink/moxy.php) the correct parent object is passed to the afterUnmarshal method.Majoriemajority
D
0

To avoid mapping to JAXBElement? I have done this things it work for me :

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
           xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
           jaxb:extensionBindingPrefixes="xjc"
           jaxb:version="2.0">

    <xs:annotation>
       <xs:appinfo>
          <jaxb:globalBindings generateValueClass="false">
           <xjc:simple />
          </jaxb:globalBindings>
       </xs:appinfo>
    </xs:annotation>

</xs:schema>

Save this file in the xml file and supply as binding files.

Reference Link

Generate JAXB classes using eclipse dont forget to give binding files created with above xml and check "Allow Vendor extensions" as below:

enter image description here

With changes i am able Get rid of JAXBElement in classes generated xjc for convert xsd to JAXB generated classes.

Danby answered 11/5, 2017 at 12:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.