JAXB avoid JAXBElement<?>

DanyX23 picture DanyX23 · Mar 19, 2014 · Viewed 10.1k times · Source

I want to generate Java classes with JAXB from a XSD file.

The problem is, that I always get a few classes like this one (namespace removed):

public static class Action {

            @XmlElementRefs({
                @XmlElementRef(name = "ReportStateCanceled", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportDate", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportStatePreliminary", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "DocumentID", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportStateNotValidated", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "CaseNo", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "PatientID", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "DocumentKey", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportTime", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportHeading", namespace = "http://...", type = JAXBElement.class, required = false),
                @XmlElementRef(name = "ReportStateComplete", namespace = "http://...", type = JAXBElement.class, required = false)
            })
            protected List<JAXBElement<?>> documentKeyOrDocumentIDOrPatientID;
            @XmlAttribute(name = "name")
            protected String name;
            @XmlAttribute(name = "Query")
            protected String query;

            /**
             * Gets the value of the documentKeyOrDocumentIDOrPatientID property.
             * 
             * <p>
             * This accessor method returns a reference to the live list,
             * not a snapshot. Therefore any modification you make to the
             * returned list will be present inside the JAXB object.
             * This is why there is not a <CODE>set</CODE> method for the documentKeyOrDocumentIDOrPatientID property.
             * 
             * <p>
             * For example, to add a new item, do as follows:
             * <pre>
             *    getDocumentKeyOrDocumentIDOrPatientID().add(newItem);
             * </pre>
             * 
             * 
             * <p>
             * Objects of the following type(s) are allowed in the list
             * {@link JAXBElement }{@code <}{@link Template.Page.Actions.Action.ReportDate }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * {@link JAXBElement }{@code <}{@link Template.Page.Actions.Action.ReportTime }{@code >}
             * {@link JAXBElement }{@code <}{@link String }{@code >}
             * 
             * 
             */
            public List<JAXBElement<?>> getDocumentKeyOrDocumentIDOrPatientID() {
                if (documentKeyOrDocumentIDOrPatientID == null) {
                    documentKeyOrDocumentIDOrPatientID = new ArrayList<JAXBElement<?>>();
                }
                return this.documentKeyOrDocumentIDOrPatientID;
            }

            /**
             * Gets the value of the name property.
             * 
             * @return
             *     possible object is
             *     {@link String }
             *     
             */
            public String getName() {
                return name;
            }

            /**
             * Sets the value of the name property.
             * 
             * @param value
             *     allowed object is
             *     {@link String }
             *     
             */
            public void setName(String value) {
                this.name = value;
            }

            /**
             * Gets the value of the query property.
             * 
             * @return
             *     possible object is
             *     {@link String }
             *     
             */
            public String getQuery() {
                return query;
            }

            /**
             * Sets the value of the query property.
             * 
             * @param value
             *     allowed object is
             *     {@link String }
             *     
             */
            public void setQuery(String value) {
                this.query = value;
            }


            /**
             * <p>Java class for anonymous complex type.
             * 
             * <p>The following schema fragment specifies the expected content contained within this class.
             * 
             * <pre>
             * &lt;complexType>
             *   &lt;simpleContent>
             *     &lt;extension base="&lt;http://www.w3.org/2001/XMLSchema>string">
             *       &lt;attribute name="dateFormat" type="{http://www.w3.org/2001/XMLSchema}string" />
             *     &lt;/extension>
             *   &lt;/simpleContent>
             * &lt;/complexType>
             * </pre>
             * 
             * 
             */
            @XmlAccessorType(XmlAccessType.FIELD)
            @XmlType(name = "", propOrder = {
                "value"
            })
            public static class ReportDate {

                @XmlValue
                protected String value;
                @XmlAttribute(name = "dateFormat")
                protected String dateFormat;

                /**
                 * Gets the value of the value property.
                 * 
                 * @return
                 *     possible object is
                 *     {@link String }
                 *     
                 */
                public String getValue() {
                    return value;
                }

                /**
                 * Sets the value of the value property.
                 * 
                 * @param value
                 *     allowed object is
                 *     {@link String }
                 *     
                 */
                public void setValue(String value) {
                    this.value = value;
                }

                /**
                 * Gets the value of the dateFormat property.
                 * 
                 * @return
                 *     possible object is
                 *     {@link String }
                 *     
                 */
                public String getDateFormat() {
                    return dateFormat;
                }

                /**
                 * Sets the value of the dateFormat property.
                 * 
                 * @param value
                 *     allowed object is
                 *     {@link String }
                 *     
                 */
                public void setDateFormat(String value) {
                    this.dateFormat = value;
                }

            }

As you can see JAXB uses JAXBElement. Such classes are unuseable for me.


After some research I found out that JAXB has troubles with nillable="true" and minOccurs="0" together, but I do not use nillable anywhere. Tough I am sure, that the problem is related with the minOccurs and maxOccurs.

Even if I would know the exact problem it would be a problem because I get the XSD from an external person and I am not allowed to modify it.

The XSD snippet:

<xs:element name="Action" minOccurs="0" maxOccurs="unbounded">
    <xs:complexType>
        <xs:choice minOccurs="0" maxOccurs="unbounded">
            <xs:element name="DocumentKey" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="DocumentID" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="PatientID" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="CaseNo" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="ReportHeading" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="ReportDate" minOccurs="0" maxOccurs="1">
                <xs:complexType>
                    <xs:simpleContent>
                        <xs:extension base="xs:string" >
                            <xs:attribute name="dateFormat" type="xs:string" />
                        </xs:extension>
                    </xs:simpleContent>
                </xs:complexType>
            </xs:element>
            <xs:element name="ReportTime" minOccurs="0" maxOccurs="1">
                <xs:complexType>
                    <xs:simpleContent>
                        <xs:extension base="xs:string" >
                            <xs:attribute name="timeFormat" type="xs:string" />
                        </xs:extension>
                    </xs:simpleContent>
                </xs:complexType>
            </xs:element>
            <xs:element name="ReportStateNotValidated" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="ReportStatePreliminary" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="ReportStateComplete" type="xs:string" minOccurs="0" maxOccurs="1"/>
            <xs:element name="ReportStateCanceled" type="xs:string" minOccurs="0" maxOccurs="1"/>
        </xs:choice>
        <xs:attribute name="name" type="xs:string" />
        <xs:attribute name="Query" type="xs:string" />
    </xs:complexType>
</xs:element>

So I searched for another solution. I always came to the conclusion that I could solve the problem if add a jaxb-binding.xml when I am generating the classes from the XSD.

I tried some bindings, but it never worked.

With this binding I get less JAXBElements, but I get interfaces instead of classes.

    <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" generateElementProperty="false" >
                    <xjc:simple />
                </jaxb:globalBindings>
            </xs:appinfo>
        </xs:annotation>
    </xs:schema>

And now I am asking if anyone knows a solution? The best solution would be if someone provides me a working jaxb-binding.xml.

I am using version 2.2.4-2 of the xjc compiler.

Thank you in advance!

Answer

lexicore picture lexicore · Mar 19, 2014

I wrote the Simpify plugin exactly for this problem.

<xs:schema ...
    xmlns:simplify="http://jaxb2-commons.dev.java.net/basic/simplify"
    jaxb:extensionBindingPrefixes="... simplify">

<xs:complexType name="typeWithReferencesProperty">
    <xs:choice maxOccurs="unbounded">
        <xs:element name="a" type="someType">
            <xs:annotation>
                <xs:appinfo>
                    <simplify:as-element-property/>
                </xs:appinfo>
            </xs:annotation>
        </xs:element>
        <xs:element name="b" type="someType"/>
    </xs:choice> 
</xs:complexType>

will give you

@XmlElement(name = "a")
protected List<SomeType> a;
@XmlElement(name = "b")
protected List<SomeType> b;

instead of

@XmlElementRefs({
    @XmlElementRef(name = "a", type = JAXBElement.class),
    @XmlElementRef(name = "b", type = JAXBElement.class)
})
protected List<JAXBElement<SomeType>> aOrB;

Disclaimer: I'm the author.