Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are static classes special in JAXB unmarshalling

Tags:

java

xml

jaxb

I have several cases where the class generated by JAXB from an xsd file has a series of lists of classes that also contain lists of classes. The class generated uses static inner classes when handling these cases, but this leads to several instances a class existing multiple times.

As an example I have a Parameter class that exists in multiple xsd files. In every instance this parameter class is the same and contains a key vale pair. When generating code for each of these xsd files each generated class contains an inner class with the name Parameter.

To reduce redundancy I have removed the Parameter class from all the generated classes, reimplemented it, and updated all of the references. This works as expected

Trouble occurs when I have another class such as LogicalDevice that is also implemented in several classes. LogicalDevice contains a ParameterList. When LogicalDevice is extracted to its own class, and the references are updated, only the first attribute in the Parameter class is maintained and the rest are null.

This leads me to believe that there is something special with the static inner classes.

For reference here is my xml and code

<Jetstream xmlns="http://Jetstream.TersoSolutions.com/v1.1/GetPoliciesResponse">
    <Header/>
    <GetPoliciesResponse>
        <PolicyList>
            <Policy Id="53bb663f-7ed0-41f8-9757-90ad4e015995" Name="MyPolicy"
                 DeviceDefinitionId="f3536a6d-4610-4d56-82d9-54fd6602a883" >
                <ParameterList>
                    <Parameter Name="aggregateeventscancount" Value="2"/>
                    <Parameter Name="aggregateeventscantime" Value="10"/>
                    <Parameter Name="antenna1rxsensitivity" Value="50"/>
                    <Parameter Name="antenna1txpower" Value="30"/>
                    <Parameter Name="antenna2rxsensitivity" Value="50"/>
                    <Parameter Name="antenna2txpower" Value="30"/>
                    <Parameter Name="antenna3rxsensitivity" Value="50"/>
                    <Parameter Name="antenna3txpower" Value="30"/>
                    <Parameter Name="antenna4rxsensitivity" Value="50"/>
                    <Parameter Name="antenna4txpower" Value="30"/>
                    <Parameter Name="commandpollinterval" Value="60"/>
                    <Parameter Name="dns" Value="0.0.0.0"/>
                    <Parameter Name="dooropentimelimit" Value="300"/>
                    <Parameter Name="gateway" Value="0.0.0.0"/>
                    <Parameter Name="ip" Value="0.0.0.0"/>
                    <Parameter Name="jetstreamdeviceurl" Value="https://us-device.tersosolutions.com/v1.0/device/"/>
                    <Parameter Name="lockdownhightemp" Value="127"/>
                    <Parameter Name="lockdownonacpowerfailure" Value="0"/>
                    <Parameter Name="lockdownonreaderfailure" Value="0"/>
                    <Parameter Name="lockdownonhightemp" Value="0"/>
                    <Parameter Name="logentryeventhightemp" Value="127"/>
                    <Parameter Name="logentryeventlowtemp" Value="-128"/>
                    <Parameter Name="numberofantennas" Value="4"/>
                    <Parameter Name="logentrylevel" Value="Warning"/>
                    <Parameter Name="objecteventscancount" Value="2"/>
                    <Parameter Name="objecteventscantime" Value="10"/>
                    <Parameter Name="Subnet" Value="0.0.0.0"/>
                </ParameterList>
                <LogicalDeviceList>
                    <LogicalDevice LogicalDeviceId="MyDevice">
                        <ParameterList>
                            <Parameter Name="ip" value="192.168.91.100" />
                            <Parameter Name="subnet" value="255.255.255.0" />
                            <Parameter Name="gateway" value="192.168.91.1" />
                            <Parameter Name="dns" value="192.168.91.2" />
                        </ParameterList>
                    </LogicalDevice>
                </LogicalDeviceList>
            </Policy>
        </PolicyList>
    </GetPoliciesResponse>
</Jetstream>

The following is the modified generated code with the references updated.

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference     Implementation, v2.2.7 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.09.09 at 10:24:32 AM CDT 
//


package com.TersoSolutions.Jetstream.SDK.XML.Model.GetPoliciesResponse;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import com.TersoSolutions.Jetstream.SDK.XML.LogicalDeviceList;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Jetstream", propOrder = {
"header",
"getPoliciesResponse"
})
@XmlRootElement(name = "Jetstream")
public class Jetstream {

@XmlElement(name = "Header", required = true)
protected Jetstream.Header header;
@XmlElement(name = "GetPoliciesResponse", required = true)
protected Jetstream.GetPoliciesResponse getPoliciesResponse;

/**
 * Gets the value of the header property.
 * 
 * @return
 *     possible object is
 *     {@link Jetstream.Header }
 *     
 */
public Jetstream.Header getHeader() {
    return header;
}

/**
 * Gets the value of the getPoliciesResponse property.
 * 
 * @return
 *     possible object is
 *     {@link Jetstream.GetPoliciesResponse }
 *     
 */
public Jetstream.GetPoliciesResponse getGetPoliciesResponse() {
    return getPoliciesResponse;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "GetPoliciesResponse", propOrder = {
    "policyList",
    "any"
})
public static class GetPoliciesResponse {

    @XmlElement(name = "PolicyList", required = true)
    protected Jetstream.GetPoliciesResponse.PolicyList policyList;
    @XmlAnyElement(lax = true)
    protected List<Object> any;
    @XmlAnyAttribute
    private Map<QName, String> otherAttributes = new HashMap<QName, String>();

    /**
     * Gets the value of the policyList property.
     * 
     * @return
     *     possible object is
     *     {@link Jetstream.GetPoliciesResponse.PolicyList }
     *     
     */
    public Jetstream.GetPoliciesResponse.PolicyList getPolicyList() {
        return policyList;
    }


    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "PolicyList", propOrder = {
        "policy"
    })
    public static class PolicyList {

        @XmlElement(name = "Policy")
        protected List<Jetstream.GetPoliciesResponse.PolicyList.Policy> policy;

        /**
         * Gets the value of the policy 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 policy property.
         * 
         * <p>
         * For example, to add a new item, do as follows:
         * <pre>
         *    getPolicy().add(newItem);
         * </pre>
         * 
         * 
         * <p>
         * Objects of the following type(s) are allowed in the list
         * {@link Jetstream.GetPoliciesResponse.PolicyList.Policy }
         * 
         * 
         */
        public List<Jetstream.GetPoliciesResponse.PolicyList.Policy> getPolicy() {
            if (policy == null) {
                policy = new ArrayList<Jetstream.GetPoliciesResponse.PolicyList.Policy>();
            }
            return this.policy;
        }

        @XmlAccessorType(XmlAccessType.FIELD)
        @XmlType(name = "Policy", propOrder = {
            "parameterList",
            "logicalDeviceList",
            "any"
        })
        public static class Policy {

            @XmlElement(name = "ParameterList", required = true)
            protected com.TersoSolutions.Jetstream.SDK.XML.ParameterList parameterList;
            @XmlElement(name = "LogicalDeviceList", required = true)
            protected LogicalDeviceList logicalDeviceList;
            @XmlAnyElement(lax = true)
            protected List<Object> any;
            @XmlAttribute(name = "Id", required = true)
            protected String id;
            @XmlAttribute(name = "Name", required = true)
            protected String name;
            @XmlAttribute(name = "DeviceDefinitionId", required = true)
            protected String deviceDefinitionId;
            @XmlAnyAttribute
            private Map<QName, String> otherAttributes = new HashMap<QName, String>();

            /**
             * Gets the value of the parameterList property.
             * 
             * @return
             *     possible object is
             *     {@link Jetstream.GetPoliciesResponse.PolicyList.Policy.ParameterList }
             *     
             */
            public com.TersoSolutions.Jetstream.SDK.XML.ParameterList getParameterList() {
                return parameterList;
            }

            /**
             * Gets the value of the logicalDeviceList property.
             * 
             * @return
             *     possible object is
             *     {@link Jetstream.GetPoliciesResponse.PolicyList.Policy.LogicalDeviceList }
             *     
             */
            public LogicalDeviceList getLogicalDeviceList() {
                return logicalDeviceList;
            }

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

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

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

        }

    }

}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Header")
public static class Header {

    @XmlAnyAttribute
    private Map<QName, String> otherAttributes = new HashMap<QName, String>();

}

}

This is the updated Logical Device

package com.TersoSolutions.Jetstream.SDK.XML;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyAttribute;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import com.TersoSolutions.Jetstream.SDK.XML.Model.GetConfigurationResponse.Jetstream;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "LogicalDeviceList")
public class LogicalDeviceList {

@XmlElement(name = "LogicalDevice")
protected List<LogicalDevice> logicalDevice;

/**
 * Gets the value of the logicalDevice 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 logicalDevice property.
 * 
 * <p>
 * For example, to add a new item, do as follows:
 * <pre>
 *    getLogicalDevice().add(newItem);
 * </pre>
 * 
 * 
 * <p>
 * Objects of the following type(s) are allowed in the list
 * {@link Jetstream.GetConfigurationResponse.LogicalDeviceList.LogicalDevice }
 * 
 * 
 */
public List<LogicalDevice> getLogicalDevice() {
    if (logicalDevice == null) {
        logicalDevice = new ArrayList<LogicalDevice>();
    }
    return this.logicalDevice;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "LogicalDevice")
public static class LogicalDevice {

    @XmlElement(name = "ParameterList")
    protected com.TersoSolutions.Jetstream.SDK.XML.ParameterList parameterList;
    @XmlAttribute(name = "DeviceDefinitionId")
    protected String deviceDefinitionId;
    @XmlAttribute(name = "DeviceSerialNumber")
    protected String deviceSerialNumber;
    @XmlAttribute(name = "Health")
    protected String health;
    @XmlAttribute(name = "LogicalDeviceId", required = true)
    protected String logicalDeviceId;
    @XmlAttribute(name = "Region")
    protected String region;
    @XmlAttribute(name = "PolicyId")
    protected String policyId;
    @XmlAnyAttribute
    private Map<QName, String> otherAttributes = new HashMap<QName, String>();

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

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

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


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


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


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

    /**
     * Gets the value of the parameterList property.
     * 
     * @return
     *     possible object is
     *     {@link Jetstream.GetPoliciesResponse.PolicyList.Policy.LogicalDeviceList.LogicalDevice.ParameterList }
     *     
     */
    public com.TersoSolutions.Jetstream.SDK.XML.ParameterList getParameterList() {
        return parameterList;
    }

}
}

and finally the Parameter class

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ParameterList")
public class ParameterList {

@XmlElement(name = "Parameter", required = true)
protected List<com.TersoSolutions.Jetstream.SDK.XML.Parameter> parameter;
@XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();

/**
 * Gets the value of the parameter 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 parameter property.
 * 
 * <p>
 * For example, to add a new item, do as follows:
 * <pre>
 *    getParameter().add(newItem);
 * </pre>
 * 
 * 
 * <p>
 * Objects of the following type(s) are allowed in the list
 * {@link Jetstream.LogEntryEvent.LogEntryList.LogEntry.ParameterList.Parameter }
 * 
 * 
 */
public List<com.TersoSolutions.Jetstream.SDK.XML.Parameter> getParameter() {
    if (parameter == null) {
        parameter = new ArrayList<com.TersoSolutions.Jetstream.SDK.XML.Parameter>();
    }
    return this.parameter;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Parameter")
public static class Parameter {

    @XmlAttribute(name = "Name", required = true)
    protected String name;
    @XmlAttribute(name = "Value", required = true)
    protected String value;
    @XmlAnyAttribute
    private Map<QName, String> otherAttributes = new HashMap<QName, String>();

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

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

To reiterate what is happening. The Parameter data contained within the Policy static class is there as expected, but the Parameter data under the LogicalDevice only contains the keys and all of the value data is set to null.

Does anyone know why I am losing data with these changes, or what makes the static classes necessary.

like image 777
JME Avatar asked Nov 14 '13 19:11

JME


1 Answers

A JAXB (JSR-222) implementation will generate a top level class for each named complex type and a static nested class for each anonymous complex type. If you have complex types that are logically equivalent but defined separately you will end up with classes that are logically equivalent but defined separately.

Approaches

1 - Schema Import/Include & JAXB Episode Files

Define the type in one XML schema that is imported/included by other schemas. Then you can leverage episode files so that only one class is generated for that type.

  • What is an .episode file..?

2 - Use JAXB Binding File to Associate an Existing Class with a Complex Type

You can use a JAXB external binding file to have XJC use an existing class instead of generating a new one.

  • Shared class for child element in JAXB in different xmls/roots
like image 190
bdoughan Avatar answered Oct 13 '22 11:10

bdoughan