This JAXB plugin utilizes the power of @XmlElementWrapper
annotation. Originally xjc
trends to create wrapper classes which are the containers for collections. This plugin goes through all properties to find ones which can be represented in the model in more optimal way.
To illustrate the problem let's take the following XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
elementFormDefault="qualified">
<xs:element name="order">
<xs:complexType>
<xs:sequence>
<xs:element ref="items" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="items">
<xs:complexType>
<xs:sequence>
<xs:element name="item" type="xs:string" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
From this XSD by default xjc
will generate two classes:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "items" })
@XmlRootElement(name = "order")
public class Order {
@XmlElement(required = true)
protected Items items;
public Items getItems() {
return items;
}
public void setItems(Items value) {
this.items = value;
}
}
and
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "item" })
@XmlRootElement(name = "items")
public class Items {
@XmlElement(required = true)
protected List<String> item;
public List<String> getItem() {
if (item == null) {
item = new ArrayList<String>();
}
return this.item;
}
}
So to access a particular item one need to write a but clumsy code order.getItems().getItems().get(itemIndex)
. The solution is to use @XmlElementWrapper
which cures exactly this case. The result will be only one class with direct access to items:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = { "items" })
@XmlRootElement(name = "order")
public class Order {
@XmlElementWrapper(name = "items", required = true)
@XmlElement(name = "item")
protected List<String> items;
public List<String> getItems() {
if (items == null) {
items = new ArrayList<String>();
}
return items;
}
public void setItems(List<String> items) {
this.items = items;
}
}
The plugin is used together with the xjc
from the command line or from an Ant task or via maven-jaxb2-plugin
.
The following options are applicable for plugin:
Option | Comment |
---|---|
-Xxew | Activate the XML Element Wrapper plugin |
-Xxew:include filename | Specify a filename with candidate classes to include in the compilation. |
-Xxew:exclude filename | Specify a filename with candidate classes to exclude from the compilation. |
-Xxew:summary filename | Specify a filename to contain summary information on the compilation. |
-Xxew:collection FQCN | Specify the class name of the collection type to use. |
-Xxew:instantiate [lazy|early] | Specify when the collection class should be instantiated. |
-Xxew:delete | Delete candidate classes having been replaced during compilation. |
First you need to download the plugin jar (for example, from Maven repository) and put it to your project lib
folder.
To use the plugin from Ant you will need something like the following in your build file:
<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask">
<classpath>
<fileset dir="${lib}/jaxb" includes="*.jar" />
<fileset dir="lib" includes="jaxb-xew-plugin.jar" />
</classpath>
</taskdef>
<xjc destdir="${src-generated}" package="dk.conspicio.example.xml2code.v2">
<arg value="-Xxew" />
<arg value="-Xxew:summary ${build}/xew-summary.txt" />
<arg value="-Xxew:instantiate lazy" />
<schema dir="xsd" includes="*.xsd" />
<binding dir="xsd" includes="*.xjb" />
</xjc>
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<verbose>true</verbose>
<generateDirectory>${project.build.sourceDirectory}</generateDirectory>
<schemaDirectory>xsd</schemaDirectory>
<removeOldOutput>false</removeOldOutput>
<episode>false</episode>
<extension>true</extension>
<args>
<arg>-no-header</arg>
<arg>-Xxew</arg>
<arg>-Xxew:instantiate lazy</arg>
<arg>-Xxew:delete</arg>
</args>
<plugins>
<plugin>
<groupId>com.github.jaxb-xew-plugin</groupId>
<artifactId>jaxb-xew-plugin</artifactId>
<version>1.0</version>
</plugin>
</plugins>
</configuration>
</execution>
</executions>
</plugin>
You can find more examples of this plugin in samples
directory, including how to call this plugin from jaxws:wsimport.
If you have time and desire to contribute to this project you can do it in many ways:
- Improve this very documentation.
- Implement Unit tests.
- Provide more samples.
Everybody is very welcomed to send patches by email. But the best way would be:
- Fork the repository
- Apply the formatting rules (the ones for Eclipse can be found in
dist
folder) - Do the changes
- Commit to your own fork
- Request for pull
- There are no specific coding and naming conventions for this project except ones given in Code Conventions for the Java Programming Language by Sun. Use best practices and common sense.
- For code formatting basically Eclipse build-in formatting rules were used with following changes:
- Indentation → Align fields on columns: on
- Indentation → Tab policy: Mixed
- Indentation → Use spaces to indent wrapped lines: on
- Line Wrapping → Maximum line width: 120
- Line Wrapping → Default indentation for wrapped lines: 3
- Comments → Maximum line width for comments: 120
- Comments → Enable line comment formatting: off
- New Lines → Insert new line in empty anonymous class body: off
- New Lines → Insert new line in empty block: off
-
Read Sonatype OSS Maven Repository Usage Guide from cover to cover.
-
Use the following
true http proxy 8080 *.internal.domain true https proxy 8080 *.internal.domainsettings.xml
for your Maven:<servers> <server> <id>sonatype-nexus-snapshots</id> <username>...sonatype_user...</username> <password>...sonatype_password...</password> </server> <server> <id>sonatype-nexus-staging</id> <username>...sonatype_user...</username> <password>...sonatype_password...</password> </server> </servers> <profiles> <profile> <id>gpg</id> <properties> <gpg.passphrase>...passphrase...</gpg.passphrase> </properties> </profile> </profiles>
-
For Hudson freestyle job specify:
- Pre-release step
git checkout master; git reset --hard origin/master
(see [http://stackoverflow.com/questions/1877027](Can't get automated release working with Hudson + Git + Maven Release Plugin) for more details about the problem). - Next step (release):
release:prepare release:perform -Pstage-release -Pgpg -Dresume=false -Dusername=...github_user... -Dpassword=...github_password...
- Pre-release step
The plugin flow consists of the following parts:
- Parse arguments.
- Find classes which are candidates for removal:
- The candidate class must have exactly one property
- The candidate class should not extend any other class (as the total number of properties will be more than 1)
- Visit all classes again to check if the candidate is not eligible for removal:
- If there are classes that extend the candidate
- If there are class fields, that refer the candidate by e.g.
@XmlElementRef
annotation
- Visit all classes again to replace the property having the candidate class type with collection plus
@XmlElementWrapper
annotation. On this step getters/setters are update and ObjectFactory methods are corrected. Also lazy initialization policy is applied. - Candidates which are still marked for removal are finally removed (and ObjectFactory is updated accordingly).
Original code by Bjarne Hansen. Committers:
The whole project is licensed under LGPLv3 (or any later version).