tags:

views:

159

answers:

2

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?

A: 

In another question, I described one case where JAXBElement is used.

Usually, if you have something that isn't easily representable in Java's type system, like substitution groups, or an element that could have both text and child elements as its contents (those are by no means the only cases, but just ones that I could quickly come up with), then JAXBElement will be used.

Chris Jester-Young
A: 

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:

Blaise Doughan
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.
TL Stillman
This appears to be a bug in Metro JAXB (the reference implementation, included in Java SE 6). In the MOXy JAXB implementation (http://www.eclipse.org/eclipselink/moxy.php) the correct parent object is passed to the afterUnmarshal method.
Blaise Doughan