samedi 2 février 2013

JAXB/XJC: Select properties included in equals, hashcode and toString methods

When you generate source code from your WSDL/XSD using XJC, you may also want to generate equals, hashcode and/or toString methods. This is done by passing extra arguments to XJC. For example, when using the cxf-codegen-plugin with Maven, these arguments are passed as follows:


<plugin>
   <groupid>org.apache.cxf</groupid>
   <artifactid>cxf-codegen-plugin</artifactid>
   <version>${cxf.version}</version>
   <executions>
      <execution>
         <id>generate-sources</id>
         <phase>generate-sources</phase>
         <configuration>
            <wsdloptions>
               <wsdloption>
                  <wsdl>....</wsdl>
                  <extraargs>
                     <extraarg>-xjc-XhashCode</extraarg>
                     <extraarg>-xjc-Xequals</extraarg>
                     <extraarg>-xjc-XtoString</extraarg>
                  </extraargs>
              </wsdloption>
           </wsdloptions>
        </configuration>
        <goals>
           <goal>wsdl2java</goal>
        </goals>
     </execution>
  </executions>
  <dependencies>
     <dependency>
        <groupid>org.jvnet.jaxb2_commons</groupid>
        <artifactid>jaxb2-basics</artifactid>
        <version>${jaxb2.version}</version>
     </dependency>
  </dependencies>
</plugin>


By default, the generated methods use all the properties in the generated classes. Often, only a part of the properties compose the business key, and uniqueness should be determined based only on these properties. This concerns the equals and hashcode methods. For the toString method, one may want part of the properties to be displayed by the method.

In order to select the properties to be used by these methods, custom bindings have to be set. These bindings may be provided directly in the XML schema or externally using a separate file.

Here, I will show how this can be done using a separate file but the same applies when the first way is used.

First, the bindings file must be created. There is no required extension for this file but since it consists of an XML document, the .xml extension is appropriated. Assume we have the following complex type declared in the XML schema:


<xs:schema targetnamespace="target-namespace" xmlns:tns="target-namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:complextype name="PersonType">
      <xs:sequence>
         <xs:element name="idNumber" type="xs:string"/>
         <xs:element name="lastName" type="xs:string"/>
         <xs:element name="firstName" type="xs:string"/>
      </xs:sequence>
   </xs:complextype>
</xs:schema>

In the generated class PersonType, we only want the idNumber property to be used by the equals and hashcode methods. Assume we also want only the lastName and firstName properties to be displayed by the toString method. Therefore, we need to specify this as bindings as follows:


<jxb:bindings node="/xs:schema" schemalocation="XmlSchema.xsd" version="2.1" xmlns:equals="http://jaxb2-commons.dev.java.net/basic/equals" xmlns:hashcode="http://jaxb2-commons.dev.java.net/basic/hashCode" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:tostring="http://jaxb2-commons.dev.java.net/basic/toString" xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <jxb:bindings node="//xs:complexType[@name='PersonType']/xs:sequence/xs:element[@name='idNumber']">
           <tostring:ignored/>
        </jxb:bindings>
   
        <jxb:bindings multiple="true" node="//xs:complexType[@name='PersonType']/xs:sequence/xs:element[position()&gt;1]">
           <equals:ignored/>
           <hashcode:ignored/>
        </jxb:bindings>
</jxb:bindings>

The path of the XML entity on which the custom JAXB binding applies is indicated in the node attribute as an XPath expression. Note that these paths are relative to the node identified by the XPath expression in the node attribute present in the top <jxb:bindings> element (here it is /xs:schema). 

It is also important to note that the order of definition of the elements in the complex type is important since the position() method is used to select all the elements but the first one (i.e. idNumber).

Also, when the XPath expression present in the node attribute targets more than one node, the multiple attribute must be set to true.

Aucun commentaire:

Enregistrer un commentaire