Showing posts with label JAXB. Show all posts
Showing posts with label JAXB. Show all posts

Monday, January 21, 2019

Running JAXB xjc Compiler with OpenJDK 11

As described in the post "APIs To Be Removed from Java 11," a JAXB implementation is no longer included with JDK 11. In this post, I look at using the xjc compiler provided with the JAXB (Java Architecture for XML Binding) reference implementation in conjunction with OpenJDK 11 to compile XML schema files into Java classes.

Prior to Java SE 6, developers wanting to use JAXB with a Java SE application needed to acquire a JAXB implementation separately because one was not provided with the Java distribution. A JAXB implementation was included with Java starting with Java SE 6. This was convenient in many cases, but made things a bit more difficult when developers wished to use a newer version or different implementation of JAXB than the one provided with the JDK. When modularity was introduced with OpenJDK 9, the JAXB implementation was moved into the java.xml.bind module and was marked as deprecated for removal. The JAXB implementation was removed altogether with JDK 11. This post looks into using JAXB's xjc compiler with OpenJDK 11.

Because JDK 11 no longer includes an implementation of JAXB, one must be acquired separately. For this post, I will be using version 2.3.0 of the JAXB reference implementation. The JDK version used in this post is JDK 11.0.2 General-Availability Release.

Running the xjc scripts without arguments leads to help/usage being rendered to standard output.

Usage: xjc [-options ...] <schema file/URL/dir/jar> ... [-b <bindinfo>] ...
If dir is specified, all schema files in it will be compiled.
If jar is specified, /META-INF/sun-jaxb.episode binding file will be compiled.
Options:
  -nv                :  do not perform strict validation of the input schema(s)
  -extension         :  allow vendor extensions - do not strictly follow the
                        Compatibility Rules and App E.2 from the JAXB Spec
  -b <file/dir>      :  specify external bindings files (each <file> must have its own -b)
                        If a directory is given, **/*.xjb is searched
  -d <dir>           :  generated files will go into this directory
  -p <pkg>           :  specifies the target package
  -m <name>          :  generate module-info.java with given Java module name
  -httpproxy <proxy> :  set HTTP/HTTPS proxy. Format is [user[:password]@]proxyHost:proxyPort
  -httpproxyfile <f> :  Works like -httpproxy but takes the argument in a file to protect password 
  -classpath <arg>   :  specify where to find user class files
  -catalog <file>    :  specify catalog files to resolve external entity references
                        support TR9401, XCatalog, and OASIS XML Catalog format.
  -readOnly          :  generated files will be in read-only mode
  -npa               :  suppress generation of package level annotations (**/package-info.java)
  -no-header         :  suppress generation of a file header with timestamp
  -target (2.0|2.1)  :  behave like XJC 2.0 or 2.1 and generate code that doesnt use any 2.2 features.
  -encoding <encoding> :  specify character encoding for generated source files
  -enableIntrospection :  enable correct generation of Boolean getters/setters to enable Bean Introspection apis 
  -disableXmlSecurity  :  disables XML security features when parsing XML documents 
  -contentForWildcard  :  generates content property for types with multiple xs:any derived elements 
  -xmlschema         :  treat input as W3C XML Schema (default)
  -dtd               :  treat input as XML DTD (experimental,unsupported)
  -wsdl              :  treat input as WSDL and compile schemas inside it (experimental,unsupported)
  -verbose           :  be extra verbose
  -quiet             :  suppress compiler output
  -help              :  display this help message
  -version           :  display version information
  -fullversion       :  display full version information


Extensions:
  -Xinject-code      :  inject specified Java code fragments into the generated code
  -Xlocator          :  enable source location support for generated code
  -Xsync-methods     :  generate accessor methods with the 'synchronized' keyword
  -mark-generated    :  mark the generated code as @javax.annotation.Generated
  -episode <FILE>    :  generate the episode file for separate compilation
  -Xpropertyaccessors :  Use XmlAccessType PROPERTY instead of FIELD for generated classes

The xjc compiler scripts (bash file and DOS batch file) are conveniences for invoking the jaxb-xjc.jar. The scripts invoke it as an executable JAR (java -jar) as shown in the following excerpts:

  • Windows version (xjc.bat):
    %JAVA% %XJC_OPTS% -jar "%JAXB_HOME%\lib\jaxb-xjc.jar" %*
  • Linux version (xjc.sh):
    exec "$JAVA" $XJC_OPTS -jar "$JAXB_HOME/lib/jaxb-xjc.jar" "$@"

As the script excerpts above show, an environmental variable XJC_OPTS is included in the invocation of the Java launcher. Unfortunately, the JAXB reference implementation JAR cannot be simply added to the classpath via -classpath because running excutable JARs with java -jar only respects the classpath designated within the executable JAR via the MANIFEST.MF's Class-Path (which entry exists in the jaxb-ri-2.3.0.jar as "Class-Path: jaxb-core.jar jaxb-impl.jar").

One way to approach this is to modify the script to use the JAR as a regular JAR (without -jar) and explicitly execute the class XJCFacade, so that the classpath can be explicitly provided to the Java launcher. This is demonstrated for the Windows xjc.bat script:

%JAVA% -cp C:\lib\javax.activation-api-1.2.0.jar;C:\jaxb-ri-2.3.0\lib\jaxb-xjc.jar com.sun.tools.xjc.XJCFacade %*

In addition to the JAXB reference implementation JAR jaxb-xjc.jar, I also needed to include the javax.activation-api-1.2.0.jar JAR on the classpath because the JavaBeans Application Framework (JAF) is a dependency that is also no longer delivered with the JDK (removed via same JEP 320 that removed JAXB).

It's also possible, of course, to not use the XJC scripts at all and to run the Java launcher directly. The script ensures that environment variable JAXB_HOME is set. This environment variable should point to the directory into which the JAXB reference implementation was expanded.

With these changes, the JAXB xjc compiler can be executed against an XSD on the command line using JDK 11.

Wednesday, July 20, 2016

JAXB and Log4j XML Configuration Files

Both Log4j 1.x and Log4j 2.x support use of XML files to specify logging configuration. This post looks into some of the nuances and subtleties associated with using JAXB to work with these XML configuration files via Java classes. The examples in this post are based on Apache Log4j 1.2.17, Apache Log4j 2.6.2, and Java 1.8.0_73 with JAXB xjc 2.2.8-b130911.1802.

Log4j 1.x : log4j.dtd

Log4j 1.x's XML grammar is defined by a DTD instead of an W3C XML Schema. Fortunately, the JAXB implementation that comes with the JDK provides an "experimental,unsupported" option for using DTDs as the input from which Java classes are generated. The following command can be used to run the xjc command-line tool against the log4j.dtd.

    xjc -p dustin.examples.l4j1 -d src -dtd log4j.dtd

The next screen snapshot demonstrates this.

Running the command described above and demonstrated in the screen snapshot leads to Java classes being generated in a Java package in the src directory called dustin.examples.l4fj1 that allow for unmarshalling from log4j.dtd-compliant XML and for marshalling to log4j.dtd-compliant XML.

Log4j 2.x : Log4j-config.xsd

Log4j 2.x's XML configuration can be either "concise" or "strict" and I need to use "strict" in this post because that is the form that uses a grammar defined by the W3C XML Schema file Log4j-config.xsd and I need a schema to generate Java classes with JAXB. The following command can be run against this XML Schema to generate Java classes representing Log4j2 strict XML.

    xjc -p dustin.examples.l4j2 -d src Log4j-config.xsd -b l4j2.jxb

Running the above command leads to Java classes being generated in a Java package in the src directory called dustin.examples.l4j2 that allow for unmarshalling from Log4j-config.xsd-compliant XML and for marshalling to Log4j-config.xsd-compliant XML.

In the previous example, I included a JAXB binding file with the option -b followed by the name of the binding file (-b l4j2.jxb). This binding was needed to avoid an error that prevented xjc from generated Log4j 2.x-compliant Java classes with the error message, "Property "Value" is already defined. Use <jaxb:property> to resolve this conflict." This issue and how to resolve it are discussed in A Brit in Bermuda's post Property "Value" is already defined. Use to resolve this conflict. The source for the JAXB binding file I used here is shown next.

l4j2.jxb

<jxb:bindings version="2.0"
              xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
              xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <jxb:bindings schemaLocation="Log4j-config.xsd" node="/xsd:schema">
      <jxb:bindings node="//xsd:complexType[@name='KeyValuePairType']">
         <jxb:bindings node=".//xsd:attribute[@name='value']">
            <jxb:property name="pairValue"/>
         </jxb:bindings>
      </jxb:bindings>
   </jxb:bindings>
</jxb:bindings>

The JAXB binding file just shown allows xjc to successfully parse the XSD and generate the Java classes. The one small price to pay (besides writing and referencing the binding file) is that the "value" attribute of the KeyValuePairType will need to be accessed in the Java class as a field named pairValue instead of value.

Unmarshalling Log4j 1.x XML

A potential use case for working with JAXB-generated classes for Log4j 1.x's log4j.dtd and Log4j 2.x's Log-config.xsd is conversion of Log4j 1.x XML configuration files to Log4j 2.x "strict" XML configuration files. In this situation, one would need to unmarshall Log4j 1.x log4j.dtd-compliant XML and marshall Log4j 2.x Log4j-config.xsd-compliant XML.

The following code listing demonstrates how the Log4j 1.x XML might be unmarshalled using the previously generated JAXB classes.

   /**
    * Extract the contents of the Log4j 1.x XML configuration file
    * with the provided path/name.
    *
    * @param log4j1XmlFileName Path/name of Log4j 1.x XML config file.
    * @return Contents of Log4j 1.x configuration file.
    * @throws RuntimeException Thrown if exception occurs that prevents
    *    extracting contents from XML with provided name.
    */
   public Log4JConfiguration readLog4j1Config(final String log4j1XmlFileName)
      throws RuntimeException
   {
      Log4JConfiguration config;
      try
      {
         final File inputFile = new File(log4j1XmlFileName);
         if (!inputFile.isFile())
         {
            throw new RuntimeException(log4j1XmlFileName + " is NOT a parseable file.");
         }

         final SAXParserFactory spf = SAXParserFactory.newInstance();
         final SAXParser sp = spf.newSAXParser();
         final XMLReader xr = sp.getXMLReader();
         
         final JAXBContext jaxbContext = JAXBContext.newInstance("dustin.examples.l4j1");
         final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
         final UnmarshallerHandler unmarshallerHandler = unmarshaller.getUnmarshallerHandler();
         xr.setContentHandler(unmarshallerHandler);

         final FileInputStream xmlStream = new FileInputStream(log4j1XmlFileName);
         final InputSource xmlSource = new InputSource(xmlStream);
         xr.parse(xmlSource);

         final Object unmarshalledObject = unmarshallerHandler.getResult();
         config = (Log4JConfiguration) unmarshalledObject;
      }
      catch (JAXBException | ParserConfigurationException | SAXException | IOException exception)
      {
         throw new RuntimeException(
            "Unable to read from file " + log4j1XmlFileName + " - " + exception,
            exception);
      }
      return config;
   }

Unmarshalling this Log4j 1.x XML was a bit trickier than some XML unmarshalling because of the nature of log4j.dtd's namespace treatment. This approach for dealing with this wrinkle is described in Gik's Jaxb UnMarshall without namespace and in Deepa S's How to instruct JAXB to ignore Namespaces. Using this approach helped avoid the error message:

UnmarshalException: unexpected element (uri:"http://jakarta.apache.org/log4j/", local:"configuration"). Expected elements ...

To unmarshall the Log4j 1.x that in my case references log4j.dtd on the filesystem, I needed to provide a special Java system property to the Java launcher when running this code with Java 8. Specifically, I needed to specify
     -Djavax.xml.accessExternalDTD=all
to avoid the error message, "Failed to read external DTD because 'file' access is not allowed due to restriction set by the accessExternalDTD property." Additional details on this can be found at NetBeans's FaqWSDLExternalSchema Wiki page.

Marshalling Log4j 2.x XML

Marshalling Log4j 2.x XML using the JAXB-generated Java classes is fairly straightforward as demonstrated in the following example code:

   /**
    * Write Log4j 2.x "strict" XML configuration to file with
    * provided name based on provided content.
    *
    * @param log4j2Configuration Content to be written to Log4j 2.x
    *    XML configuration file.
    * @param log4j2XmlFile File to which Log4j 2.x "strict" XML
    *    configuration should be written.
    */
   public void writeStrictLog4j2Config(
      final ConfigurationType log4j2Configuration,
      final String log4j2XmlFile)
   {
      try (final OutputStream os = new FileOutputStream(log4j2XmlFile))
      {
         final JAXBContext jc = JAXBContext.newInstance("dustin.examples.l4j2");
         final Marshaller marshaller = jc.createMarshaller();
         marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
         marshaller.marshal(new ObjectFactory().createConfiguration(log4j2Configuration), os);
      }
      catch (JAXBException | IOException exception)
      {
         throw new RuntimeException(
            "Unable to write Log4 2.x XML configuration - " + exception,
            exception);
      }
   }

There is one subtlety in this marshalling case that may not be obvious in the just-shown code listing. The classes that JAXB's xjc generated from the Log4j-config.xsd lack any class with @XmlRootElement. The JAXB classes that were generated from the Log4j 1.x log4j.dtd did include classes with this @XmlRootElement annotation. Because the Log4j 2.x Log4j-config.xsd-based Java classes don't have this annotation, the following error occurs when trying to marshal the ConfigurationType instance directly:

MarshalException - with linked exception: [com.sun.istack.internal.SAXException2: unable to marshal type "dustin.examples.l4j2.ConfigurationType" as an element because it is missing an @XmlRootElement annotation]

To avoid this error, I instead (line 18 of above code listing) marshalled the result of invoking new ObjectFactory().createConfiguration(ConfigurationType) on the passed-in ConfigurationType instance and it is now successfully marshalled.

Conclusion

JAXB can be used to generate Java classes from Log4j 1.x's log4j.dtd and from Log4j 2.x's Log4j-config.xsd, but there are some subtleties and nuances associated with this process to successfully generate these Java classes and to use the generated Java classes to marshal and unmarshal XML.

Thursday, June 25, 2015

Generating JSON Schema from XSD with JAXB and Jackson

In this post, I demonstrate one approach for generating JSON Schema from an XML Schema (XSD). While providing an overview of an approach for creating JSON Schema from XML Schema, this post also demonstrates use of a JAXB implementation (xjc version 2.2.12-b150331.1824 bundled with JDK 9 [build 1.9.0-ea-b68]) and of a JSON/Java binding implementation (Jackson 2.5.4).

The steps of this approach for generating JSON Schema from an XSD can be summarized as:

  1. Apply JAXB's xjc compiler to generate Java classes from XML Schema (XSD).
  2. Apply Jackson to generate JSON schema from JAXB-generated Java classes.

Generating Java Classes from XSD with JAXB's xjc

For purposes of this discussion, I'll be using the simple Food.xsd used in my previous blog post A JAXB Nuance: String Versus Enum from Enumerated Restricted XSD String. For convenience, I have reproduced that simple schema here without the XML comments specific to that earlier blog post:

Food.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:dustin="http://marxsoftware.blogspot.com/foodxml"
           targetNamespace="http://marxsoftware.blogspot.com/foodxml"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

   <xs:element name="Food">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Vegetable" type="dustin:Vegetable" />
            <xs:element ref="dustin:Fruit" />
            <xs:element name="Dessert" type="dustin:Dessert" />
         </xs:sequence>
      </xs:complexType>
   </xs:element>

   <xs:simpleType name="Vegetable">
      <xs:restriction base="xs:string">
         <xs:enumeration value="Carrot"/>
         <xs:enumeration value="Squash"/>
         <xs:enumeration value="Spinach"/>
         <xs:enumeration value="Celery"/>
      </xs:restriction>
   </xs:simpleType>

   <xs:element name="Fruit">
      <xs:simpleType>
         <xs:restriction base="xs:string">
            <xs:enumeration value="Watermelon"/>
            <xs:enumeration value="Apple"/>
            <xs:enumeration value="Orange"/>
            <xs:enumeration value="Grape"/>
         </xs:restriction>
      </xs:simpleType>
   </xs:element>

   <xs:simpleType name="Dessert">
      <xs:restriction base="xs:string">
         <xs:enumeration value="Pie"/>
         <xs:enumeration value="Cake"/>
         <xs:enumeration value="Ice Cream"/>
      </xs:restriction>
   </xs:simpleType>

</xs:schema>

It is easy to use the xjc command line tool provided by the JDK-provided JAXB implementation to generate Java classes corresponding to this XSD. The next screen snapshot shows this process using the command:

      xjc -d jaxb .\Food.xsd

This simple command generates Java classes corresponding to the provided Food.xsd and places those classes in the specified "jaxb" subdirectory.

Generating JSON from JAXB-Generated Classes with Jackson

With the JAXB-generated classes now available, Jackson can be applied to these classes to generate JSON from the Java classes. Jackson is described on its main portal page as "a multi-purpose Java library for processing" that is "inspired by the quality and variety of XML tooling available for the Java platform." The existence of Jackson and similar frameworks and libraries appears to be one of the reasons that Oracle has dropped the JEP 198 ("Light-Weight JSON API") from Java SE 9. [It's worth noting that Java EE 7 already has built-in JSON support with its implementation of JSR 353 ("Java API for JSON Processing"), which is not associated with JEP 198).]

One of the first steps of applying Jackson to generating JSON from our JAXB-generated Java classes is to acquire and configure an instance of Jackson's ObjectMapper class. One approach for accomplishing this is shown in the next code listing.

Acquiring and Configuring Jackson ObjectMapper for JAXB Serialization/Deserialization
/**
 * Create instance of ObjectMapper with JAXB introspector
 * and default type factory.
 *
 * @return Instance of ObjectMapper with JAXB introspector
 *    and default type factory.
 */
private ObjectMapper createJaxbObjectMapper()
{
   final ObjectMapper mapper = new ObjectMapper();
   final TypeFactory typeFactory = TypeFactory.defaultInstance();
   final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
   // make deserializer use JAXB annotations (only)
   mapper.getDeserializationConfig().with(introspector);
   // make serializer use JAXB annotations (only)
   mapper.getSerializationConfig().with(introspector);
   return mapper;
}

The above code listing demonstrates acquiring the Jackson ObjectMapper instance and configuring it to use a default type factory and a JAXB-oriented annotation introspector.

With the Jackson ObjectMapper instantiated and appropriately configured, it's easy to use that ObjectMapper instance to generate JSON from the generated JAXB classes. One way to accomplish this using the deprecated Jackson class JsonSchema is demonstrated in the next code listing.

Generating JSON from Java Classes with Deprecated com.fasterxml.jackson.databind.jsonschema.JsonSchema Class
/**
 * Write JSON Schema to standard output based upon Java source
 * code in class whose fully qualified package and class name
 * have been provided.
 *
 * @param mapper Instance of ObjectMapper from which to
 *     invoke JSON schema generation.
 * @param fullyQualifiedClassName Name of Java class upon
 *    which JSON Schema will be extracted.
 */
private void writeToStandardOutputWithDeprecatedJsonSchema(
   final ObjectMapper mapper, final String fullyQualifiedClassName)
{
   try
   {
      final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
      out.println(jsonSchema);
   }
   catch (ClassNotFoundException cnfEx)
   {
      err.println("Unable to find class " + fullyQualifiedClassName);
   }
   catch (JsonMappingException jsonEx)
   {
      err.println("Unable to map JSON: " + jsonEx);
   }
}

The code in the above listing instantiates acquires the class definition of the provided Java class (the highest level Food class generated by the JAXB xjc compiler in my example) and passes that reference to the JAXB-generated class to ObjectMapper's generateJsonSchema(Class<?>) method. The deprecated JsonSchema class's toString() implementation is very useful and makes it easy to write out the JSON generated from the JAXB-generated classes.

For purposes of this demonstration, I provide the demonstration driver as a main(String[]) function. That function and the entire class to this point (including methods shown above) is provided in the next code listing.

JsonGenerationFromJaxbClasses.java, Version 1
package dustin.examples.jackson;

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

import com.fasterxml.jackson.databind.jsonschema.JsonSchema;

import static java.lang.System.out;
import static java.lang.System.err;

/**
 * Generates JavaScript Object Notation (JSON) from Java classes
 * with Java API for XML Binding (JAXB) annotations.
 */
public class JsonGenerationFromJaxbClasses
{
   /**
    * Create instance of ObjectMapper with JAXB introspector
    * and default type factory.
    *
    * @return Instance of ObjectMapper with JAXB introspector
    *    and default type factory.
    */
   private ObjectMapper createJaxbObjectMapper()
   {
      final ObjectMapper mapper = new ObjectMapper();
      final TypeFactory typeFactory = TypeFactory.defaultInstance();
      final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector(typeFactory);
      // make deserializer use JAXB annotations (only)
      mapper.getDeserializationConfig().with(introspector);
      // make serializer use JAXB annotations (only)
      mapper.getSerializationConfig().with(introspector);
      return mapper;
   }

   /**
    * Write out JSON Schema based upon Java source code in
    * class whose fully qualified package and class name have
    * been provided.
    *
    * @param mapper Instance of ObjectMapper from which to
    *     invoke JSON schema generation.
    * @param fullyQualifiedClassName Name of Java class upon
    *    which JSON Schema will be extracted.
    */
   private void writeToStandardOutputWithDeprecatedJsonSchema(
      final ObjectMapper mapper, final String fullyQualifiedClassName)
   {
      try
      {
         final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
         out.println(jsonSchema);
      }
      catch (ClassNotFoundException cnfEx)
      {
         err.println("Unable to find class " + fullyQualifiedClassName);
      }
      catch (JsonMappingException jsonEx)
      {
         err.println("Unable to map JSON: " + jsonEx);
      }
   }

   /**
    * Accepts the fully qualified (full package) name of a
    * Java class with JAXB annotations that will be used to
    * generate a JSON schema.
    *
    * @param arguments One argument expected: fully qualified
    *     package and class name of Java class with JAXB
    *     annotations.
    */
   public static void main(final String[] arguments)
   {
      if (arguments.length < 1)
      {
         err.println("Need to provide the fully qualified name of the highest-level Java class with JAXB annotations.");
         System.exit(-1);
      }
      final JsonGenerationFromJaxbClasses instance = new JsonGenerationFromJaxbClasses();
      final String fullyQualifiedClassName = arguments[0];
      final ObjectMapper objectMapper = instance.createJaxbObjectMapper();
      instance.writeToStandardOutputWithDeprecatedJsonSchema(objectMapper, fullyQualifiedClassName);
   }
}

To run this relatively generic code against the Java classes generated by JAXB's xjc based upon Food.xsd, I need to provide the fully qualified package name and class name of the highest-level generated class. In this case, that's com.blogspot.marxsoftware.foodxml.Food (package name is based on the XSD's namespace because I did not explicitly override that when running xjc). When I run the above code with that fully qualified class name and with the JAXB classes and Jackson libraries on the classpath, I see the following JSON written to standard output.

Generated JSON
{"type":"object","properties":{"vegetable":{"type":"string","enum":["CARROT","SQUASH","SPINACH","CELERY"]},"fruit":{"type":"string"},"dessert":{"type":"string","enum":["PIE","CAKE","ICE_CREAM"]}}}

Humans (which includes many developers) prefer prettier print than what was just shown for the generated JSON. We can tweak the implementation of the demonstration class's method writeToStandardOutputWithDeprecatedJsonSchema(ObjectMapper, String) as shown below to write out indented JSON that better reflects its hierarchical nature. This modified method is shown next.

Modified writeToStandardOutputWithDeprecatedJsonSchema(ObjectMapper, String) to Write Indented JSON
/**
 * Write out indented JSON Schema based upon Java source
 * code in class whose fully qualified package and class
 * name have been provided.
 *
 * @param mapper Instance of ObjectMapper from which to
 *     invoke JSON schema generation.
 * @param fullyQualifiedClassName Name of Java class upon
 *    which JSON Schema will be extracted.
 */
private void writeToStandardOutputWithDeprecatedJsonSchema(
   final ObjectMapper mapper, final String fullyQualifiedClassName)
{
   try
   {
      final JsonSchema jsonSchema = mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
      out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
   }
   catch (ClassNotFoundException cnfEx)
   {
      err.println("Unable to find class " + fullyQualifiedClassName);
   }
   catch (JsonMappingException jsonEx)
   {
      err.println("Unable to map JSON: " + jsonEx);
   }
   catch (JsonProcessingException jsonEx)
   {
      err.println("Unable to process JSON: " + jsonEx);
   }
}

When I run the demonstration class again with this modified method, the JSON output is more aesthetically pleasing:

Generated JSON with Indentation Communicating Hierarchy
{
  "type" : "object",
  "properties" : {
    "vegetable" : {
      "type" : "string",
      "enum" : [ "CARROT", "SQUASH", "SPINACH", "CELERY" ]
    },
    "fruit" : {
      "type" : "string"
    },
    "dessert" : {
      "type" : "string",
      "enum" : [ "PIE", "CAKE", "ICE_CREAM" ]
    }
  }
}

I have been using Jackson 2.5.4 in this post. The class com.fasterxml.jackson.databind.jsonschema.JsonSchema is deprecated in that version with the comment, "Since 2.2, we recommend use of external JSON Schema generator module." Given that, I now look at using the new preferred approach (Jackson JSON Schema Module approach).

The most significant change is to use the JsonSchema class in the com.fasterxml.jackson.module.jsonSchema package rather than using the JsonSchema class in the com.fasterxml.jackson.databind.jsonschema package. The approaches for obtaining instances of these different versions of JsonSchema classes are also different. The next code listing demonstrates using the newer, preferred approach for generating JSON from Java classes.

Using Jackson's Newer and Preferred com.fasterxml.jackson.module.jsonSchema.JsonSchema
/**
 * Write out JSON Schema based upon Java source code in
 * class whose fully qualified package and class name have
 * been provided. This method uses the newer module JsonSchema
 * class that replaces the deprecated databind JsonSchema.
 *
 * @param fullyQualifiedClassName Name of Java class upon
 *    which JSON Schema will be extracted.
 */
private void writeToStandardOutputWithModuleJsonSchema(
   final String fullyQualifiedClassName)
{
   final SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
   final ObjectMapper mapper = new ObjectMapper();
   try
   {
      mapper.acceptJsonFormatVisitor(mapper.constructType(Class.forName(fullyQualifiedClassName)), visitor);
      final com.fasterxml.jackson.module.jsonSchema.JsonSchema jsonSchema = visitor.finalSchema();
      out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
   }
   catch (ClassNotFoundException cnfEx)
   {
      err.println("Unable to find class " + fullyQualifiedClassName);
   }
   catch (JsonMappingException jsonEx)
   {
      err.println("Unable to map JSON: " + jsonEx);
   }
   catch (JsonProcessingException jsonEx)
   {
      err.println("Unable to process JSON: " + jsonEx);
   }
}

The following table compares usage of the two Jackson JsonSchema classes side-by-side with the deprecated approach shown earlier on the left (adapted a bit for this comparison) and the recommended newer approach on the right. Both generate the same output for the same given Java class from which JSON is to be written.

/**
 * Write out JSON Schema based upon Java source code in
 * class whose fully qualified package and class name have
 * been provided. This method uses the deprecated JsonSchema
 * class in the "databind.jsonschema" package
 * {@see com.fasterxml.jackson.databind.jsonschema}.
 *
 * @param fullyQualifiedClassName Name of Java class upon
 *    which JSON Schema will be extracted.
 */
private void writeToStandardOutputWithDeprecatedDatabindJsonSchema(
   final String fullyQualifiedClassName)
{
   final ObjectMapper mapper = new ObjectMapper();
   try
   {
      final com.fasterxml.jackson.databind.jsonschema.JsonSchema jsonSchema =
         mapper.generateJsonSchema(Class.forName(fullyQualifiedClassName));
      out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
   }
   catch (ClassNotFoundException cnfEx)
   {
      err.println("Unable to find class " + fullyQualifiedClassName);
   }
   catch (JsonMappingException jsonEx)
   {
      err.println("Unable to map JSON: " + jsonEx);
   }
   catch (JsonProcessingException jsonEx)
   {
      err.println("Unable to process JSON: " + jsonEx);
   }
}
/**
 * Write out JSON Schema based upon Java source code in
 * class whose fully qualified package and class name have
 * been provided. This method uses the newer module JsonSchema
 * class that replaces the deprecated databind JsonSchema.
 *
 * @param fullyQualifiedClassName Name of Java class upon
 *    which JSON Schema will be extracted.
 */
private void writeToStandardOutputWithModuleJsonSchema(
   final String fullyQualifiedClassName)
{
   final SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
   final ObjectMapper mapper = new ObjectMapper();
   try
   {
      mapper.acceptJsonFormatVisitor(mapper.constructType(Class.forName(fullyQualifiedClassName)), visitor);
      final com.fasterxml.jackson.module.jsonSchema.JsonSchema jsonSchema = visitor.finalSchema();
      out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonSchema));
   }
   catch (ClassNotFoundException cnfEx)
   {
      err.println("Unable to find class " + fullyQualifiedClassName);
   }
   catch (JsonMappingException jsonEx)
   {
      err.println("Unable to map JSON: " + jsonEx);
   }
   catch (JsonProcessingException jsonEx)
   {
      err.println("Unable to process JSON: " + jsonEx);
   }
}

This blog post has shown two approaches using different versions of classes with name JsonSchema provided by Jackson to write JSON based on Java classes generated from an XSD with JAXB's xjc. The overall process demonstrated in this post is one approach for generating JSON Schema from XML Schema.

Thursday, February 12, 2015

A JAXB Nuance: String Versus Enum from Enumerated Restricted XSD String

Although Java Architecture for XML Binding (JAXB) is fairly easy to use in nominal cases (especially since Java SE 6), it also presents numerous nuances. Some of the common nuances are due to the inability to exactly match (bind) XML Schema Definition (XSD) types to Java types. This post looks at one specific example of this that also demonstrates how different XSD constructs that enforce the same XML structure can lead to different Java types when the JAXB compiler generates the Java classes.

The next code listing, for Food.xsd, defines a schema for food types. The XSD mandates that valid XML will have a root element called "Food" with three nested elements "Vegetable", "Fruit", and "Dessert". Although the approach used to specify the "Vegetable" and "Dessert" elements is different than the approach used to specify the "Fruit" element, both approaches result in similar "valid XML." The "Vegetable" and "Dessert" elements are declared directly as elements of the prescribed simpleTypes defined later in the XSD. The "Fruit" element is defined via reference (ref=) to another defined element that consists of a simpleType.

Food.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:dustin="http://marxsoftware.blogspot.com/foodxml"
           targetNamespace="http://marxsoftware.blogspot.com/foodxml"
           elementFormDefault="qualified"
           attributeFormDefault="unqualified">

   <xs:element name="Food">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="Vegetable" type="dustin:Vegetable" />
            <xs:element ref="dustin:Fruit" />
            <xs:element name="Dessert" type="dustin:Dessert" />
         </xs:sequence>
      </xs:complexType>
   </xs:element>

   <!--
        Direct simple type that restricts xs:string will become enum in
        JAXB-generated Java class.
   -->
   <xs:simpleType name="Vegetable">
      <xs:restriction base="xs:string">
         <xs:enumeration value="Carrot"/>
         <xs:enumeration value="Squash"/>
         <xs:enumeration value="Spinach"/>
         <xs:enumeration value="Celery"/>
      </xs:restriction>
   </xs:simpleType>

   <!--
        Simple type that restricts xs:string but is wrapped in xs:element
        (making it an Element rather than a SimpleType) will become Java
        String in JAXB-generated Java class for Elements that reference it.
   -->
   <xs:element name="Fruit">
      <xs:simpleType>
         <xs:restriction base="xs:string">
            <xs:enumeration value="Watermelon"/>
            <xs:enumeration value="Apple"/>
            <xs:enumeration value="Orange"/>
            <xs:enumeration value="Grape"/>
         </xs:restriction>
      </xs:simpleType>
   </xs:element>

   <!--
        Direct simple type that restricts xs:string will become enum in
        JAXB-generated Java class.        
   -->
   <xs:simpleType name="Dessert">
      <xs:restriction base="xs:string">
         <xs:enumeration value="Pie"/>
         <xs:enumeration value="Cake"/>
         <xs:enumeration value="Ice Cream"/>
      </xs:restriction>
   </xs:simpleType>

</xs:schema>

Although Vegetable and Dessert elements are defined in the schema differently than Fruit, the resulting valid XML is the same. A valid XML file is shown next in the code listing for food1.xml.

food1.xml
<?xml version="1.0" encoding="utf-8"?>
<Food xmlns="http://marxsoftware.blogspot.com/foodxml"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Vegetable>Spinach</Vegetable>
   <Fruit>Watermelon</Fruit>
   <Dessert>Pie</Dessert>
</Food>

At this point, I'll use a simple Groovy script to validate the above XML against the above XSD. The code for this Groovy XML validation script (validateXmlAgainstXsd.groovy) is shown next.

validateXmlAgainstXsd.groovy
#!/usr/bin/env groovy

// validateXmlAgainstXsd.groovy
//
// Accepts paths/names of two files. The first is the XML file to be validated
// and the second is the XSD against which to validate that XML.

if (args.length < 2)
{
   println "USAGE: groovy validateXmlAgainstXsd.groovy <xmlFile> <xsdFile>"
   System.exit(-1)
}

String xml = args[0]
String xsd = args[1]

import javax.xml.validation.Schema
import javax.xml.validation.SchemaFactory
import javax.xml.validation.Validator

try
{
   SchemaFactory schemaFactory =
      SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)
   Schema schema = schemaFactory.newSchema(new File(xsd))
   Validator validator = schema.newValidator()
   validator.validate(new javax.xml.transform.stream.StreamSource(xml))
}
catch (Exception exception)
{
   println "\nERROR: Unable to validate ${xml} against ${xsd} due to '${exception}'\n"
   System.exit(-1)
}
println "\nXML file ${xml} validated successfully against ${xsd}.\n"

The next screen snapshot demonstrates running the above Groovy XML validation script against food1.xml and Food.xsd.

The objective of this post so far has been to show how different approaches in an XSD can lead to the same XML being valid. Although these different XSD approaches prescribe the same valid XML, they lead to different Java class behavior when JAXB is used to generate classes based on the XSD. The next screen snapshot demonstrates running the JDK-provided JAXB xjc compiler against the Food.xsd to generate the Java classes.

The output from the JAXB generation shown above indicates that Java classes were created for the "Vegetable" and "Dessert" elements but not for the "Fruit" element. This is because "Vegetable" and "Dessert" were defined differently than "Fruit" in the XSD. The next code listing is for the Food.java class generated by the xjc compiler. From this we can see that the generated Food.java class references specific generated Java types for Vegetable and Dessert, but references simply a generic Java String for Fruit.

Food.java (generated by JAXB jxc compiler)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// 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: 2015.02.11 at 10:17:32 PM MST 
//


package com.blogspot.marxsoftware.foodxml;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * <complexType>
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="Vegetable" type="{http://marxsoftware.blogspot.com/foodxml}Vegetable"/>
 *         <element ref="{http://marxsoftware.blogspot.com/foodxml}Fruit"/>
 *         <element name="Dessert" type="{http://marxsoftware.blogspot.com/foodxml}Dessert"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "vegetable",
    "fruit",
    "dessert"
})
@XmlRootElement(name = "Food")
public class Food {

    @XmlElement(name = "Vegetable", required = true)
    @XmlSchemaType(name = "string")
    protected Vegetable vegetable;
    @XmlElement(name = "Fruit", required = true)
    protected String fruit;
    @XmlElement(name = "Dessert", required = true)
    @XmlSchemaType(name = "string")
    protected Dessert dessert;

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

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

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

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

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

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

}

The advantage of having specific Vegetable and Dessert classes is the additional type safety they bring as compared to a general Java String. Both Vegetable.java and Dessert.java are actually enums because they come from enumerated values in the XSD. The two generated enums are shown in the next two code listings.

Vegetable.java (generated with JAXB xjc compiler)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// 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: 2015.02.11 at 10:17:32 PM MST 
//


package com.blogspot.marxsoftware.foodxml;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Vegetable.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * <p>
 * <pre>
 * <simpleType name="Vegetable">
 *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *     <enumeration value="Carrot"/>
 *     <enumeration value="Squash"/>
 *     <enumeration value="Spinach"/>
 *     <enumeration value="Celery"/>
 *   </restriction>
 * </simpleType>
 * </pre>
 * 
 */
@XmlType(name = "Vegetable")
@XmlEnum
public enum Vegetable {

    @XmlEnumValue("Carrot")
    CARROT("Carrot"),
    @XmlEnumValue("Squash")
    SQUASH("Squash"),
    @XmlEnumValue("Spinach")
    SPINACH("Spinach"),
    @XmlEnumValue("Celery")
    CELERY("Celery");
    private final String value;

    Vegetable(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static Vegetable fromValue(String v) {
        for (Vegetable c: Vegetable.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }

}
Dessert.java (generated with JAXB xjc compiler)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// 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: 2015.02.11 at 10:17:32 PM MST 
//


package com.blogspot.marxsoftware.foodxml;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for Dessert.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * <p>
 * <pre>
 * <simpleType name="Dessert">
 *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *     <enumeration value="Pie"/>
 *     <enumeration value="Cake"/>
 *     <enumeration value="Ice Cream"/>
 *   </restriction>
 * </simpleType>
 * </pre>
 * 
 */
@XmlType(name = "Dessert")
@XmlEnum
public enum Dessert {

    @XmlEnumValue("Pie")
    PIE("Pie"),
    @XmlEnumValue("Cake")
    CAKE("Cake"),
    @XmlEnumValue("Ice Cream")
    ICE_CREAM("Ice Cream");
    private final String value;

    Dessert(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static Dessert fromValue(String v) {
        for (Dessert c: Dessert.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }

}

Having enums generated for the XML elements ensures that only valid values for those elements can be represented in Java.

Conclusion

JAXB makes it relatively easy to map Java to XML, but because there is not a one-to-one mapping between Java and XML types, there can be some cases where the generated Java type for a particular XSD prescribed element is not obvious. This post has shown how two different approaches to building an XSD to enforce the same basic XML structure can lead to very different results in the Java classes generated with the JAXB xjc compiler. In the example shown in this post, declaring elements in the XSD directly on simpleTypes restricting XSD's string to a specific set of enumerated values is preferable to declaring elements as references to other elements wrapping a simpleType of restricted string enumerated values because of the type safety that is achieved when enums are generated rather than use of general Java Strings.

Monday, December 23, 2013

Orika: Mapping JAXB Objects to Business/Domain Objects

This post looks at mapping JAXB objects to business domain objects with Orika. Earlier this month, I covered the same mapping use case using reflection-based Dozer. In this post, I'll assume the same example classes need to be mapped, but they will be mapped using Orika instead of Dozer.

Dozer and Orika are intended to solve the same type of problem: the automatic mapping of two "data" objects that do not share a common inheritance but represent the same same of data fields. Dozer uses reflection to accomplish this while Orika uses reflection and bytecode manipulation to accomplish it. Orika's slogan is, "simpler, lighter and faster Java bean mapping."

Orika has an Apache License, Version 2, and can be downloaded at https://github.com/orika-mapper/orika/archive/master.zip (sources) or at http://search.maven.org/#search|ga|1|orika (binaries). Orika has dependencies on Javassist (for bytecode manipulation), SLF4J, and paranamer (to access method/constructor parameter names at runtime). Two of these three dependencies (JavaAssist and paranamer but not SLF4J) are bundled in orika-core-1.4.4-deps-included.jar. If the dependencies are already available, the slimmer orika-core-1.4.4.jar can be used instead. As the names of these JARs suggest, I'm using Orika 1.4.4 for my examples in this post.

In my post Dozer: Mapping JAXB Objects to Business/Domain Objects, I discussed reasons that using instances of JAXB-generatated classes as business or domain objects is often not desirable. I then showed "traditional" ways of mapping between JAXB-generated classes and custom data classes so that data could be passed throughout an application in the business domain data objects. For this post, I will be using the same approach, but with Orika doing the mapping rather than doing custom mapping or using Dozer for the mapping. For convenience, I include the cost listings here for the JAXB-generated classes com.blogspot.marxsoftware.AddressType and com.blogspot.marxsoftware.PersonType as well as the renamed custom data classes dustin.examples.orikademo.Address and dustin.examples.orikademo.Person.

JAXB-generated AddressType.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 
// 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.12.03 at 11:44:32 PM MST 
//


package com.blogspot.marxsoftware;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for AddressType complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * <complexType name="AddressType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <attribute name="streetAddress1" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="streetAddress2" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="city" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="state" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="zipcode" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AddressType")
public class AddressType {

    @XmlAttribute(name = "streetAddress1", required = true)
    protected String streetAddress1;
    @XmlAttribute(name = "streetAddress2")
    protected String streetAddress2;
    @XmlAttribute(name = "city", required = true)
    protected String city;
    @XmlAttribute(name = "state", required = true)
    protected String state;
    @XmlAttribute(name = "zipcode", required = true)
    protected String zipcode;

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

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

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

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

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

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

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

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

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

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

}
JAXB-generated PersonType.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 
// 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.12.03 at 11:44:32 PM MST 
//


package com.blogspot.marxsoftware;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for PersonType complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * <complexType name="PersonType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="MailingAddress" type="{http://marxsoftware.blogspot.com/}AddressType"/>
 *         <element name="ResidentialAddress" type="{http://marxsoftware.blogspot.com/}AddressType" minOccurs="0"/>
 *       </sequence>
 *       <attribute name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PersonType", propOrder = {
    "mailingAddress",
    "residentialAddress"
})
public class PersonType {

    @XmlElement(name = "MailingAddress", required = true)
    protected AddressType mailingAddress;
    @XmlElement(name = "ResidentialAddress")
    protected AddressType residentialAddress;
    @XmlAttribute(name = "firstName")
    protected String firstName;
    @XmlAttribute(name = "lastName")
    protected String lastName;

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

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

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

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

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

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

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

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

}
Domain/Business Class Address.java
package dustin.examples.orikademo;

import java.util.Objects;

/**
 * Address class.
 * 
 * @author Dustin
 */
public class Address
{
   private String streetAddress1;
   private String streetAddress2;
   private String municipality;
   private String state;
   private String zipCode;

   public Address() {}

   public Address(
      final String newStreetAddress1,
      final String newStreetAddress2,
      final String newMunicipality,
      final String newState,
      final String newZipCode)
   {
      this.streetAddress1 = newStreetAddress1;
      this.streetAddress2 = newStreetAddress2;
      this.municipality = newMunicipality;
      this.state = newState;
      this.zipCode = newZipCode;
   }

   public String getStreetAddress1()
   {
      return this.streetAddress1;
   }

   public void setStreetAddress1(String streetAddress1)
   {
      this.streetAddress1 = streetAddress1;
   }

   public String getStreetAddress2()
   {
      return this.streetAddress2;
   }

   public void setStreetAddress2(String streetAddress2)
   {
      this.streetAddress2 = streetAddress2;
   }

   public String getMunicipality()
   {
      return this.municipality;
   }

   public void setMunicipality(String municipality)
   {
      this.municipality = municipality;
   }

   public String getState() {
      return this.state;
   }

   public void setState(String state)
   {
      this.state = state;
   }

   public String getZipCode() 
   {
      return this.zipCode;
   }

   public void setZipCode(String zipCode)
   {
      this.zipCode = zipCode;
   }

   @Override
   public int hashCode()
   {
      return Objects.hash(
         this.streetAddress1, this.streetAddress2, this.municipality,
         this.state, this.zipCode);
   }

   @Override
   public boolean equals(Object obj)
   {
      if (obj == null) {
         return false;
      }
      if (getClass() != obj.getClass()) {
         return false;
      }
      final Address other = (Address) obj;
      if (!Objects.equals(this.streetAddress1, other.streetAddress1))
      {
         return false;
      }
      if (!Objects.equals(this.streetAddress2, other.streetAddress2))
      {
         return false;
      }
      if (!Objects.equals(this.municipality, other.municipality))
      {
         return false;
      }
      if (!Objects.equals(this.state, other.state))
      {
         return false;
      }
      if (!Objects.equals(this.zipCode, other.zipCode))
      {
         return false;
      }
      return true;
   }

   @Override
   public String toString()
   {
      return "Address{" + "streetAddress1=" + streetAddress1 + ", streetAddress2="
         + streetAddress2 + ", municipality=" + municipality + ", state=" + state
         + ", zipCode=" + zipCode + '}';
   }
   
}
Domain/Business Class Person.java
package dustin.examples.orikademo;

import java.util.Objects;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class Person
{
   private String lastName;
   private String firstName;
   private Address mailingAddress;
   private Address residentialAddress;

   public Person() {}

   public Person(
      final String newLastName,
      final String newFirstName,
      final Address newResidentialAddress,
      final Address newMailingAddress)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.residentialAddress = newResidentialAddress;
      this.mailingAddress = newMailingAddress;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   public void setLastName(String lastName) {
      this.lastName = lastName;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }

   public Address getMailingAddress()
   {
      return this.mailingAddress;
   }

   public void setMailingAddress(Address mailingAddress)
   {
      this.mailingAddress = mailingAddress;
   }

   public Address getResidentialAddress()
   {
      return this.residentialAddress;
   }

   public void setResidentialAddress(Address residentialAddress)
   {
      this.residentialAddress = residentialAddress;
   }

   @Override
   public int hashCode()
   {
      int hash = 3;
      hash = 19 * hash + Objects.hashCode(this.lastName);
      hash = 19 * hash + Objects.hashCode(this.firstName);
      hash = 19 * hash + Objects.hashCode(this.mailingAddress);
      hash = 19 * hash + Objects.hashCode(this.residentialAddress);
      return hash;
   }

   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final Person other = (Person) obj;
      if (!Objects.equals(this.lastName, other.lastName))
      {
         return false;
      }
      if (!Objects.equals(this.firstName, other.firstName))
      {
         return false;
      }
      if (!Objects.equals(this.mailingAddress, other.mailingAddress))
      {
         return false;
      }
      if (!Objects.equals(this.residentialAddress, other.residentialAddress))
      {
         return false;
      }
      return true;
   }

   @Override
   public String toString() {
      return  "Person{" + "lastName=" + lastName + ", firstName=" + firstName
            + ", mailingAddress=" + mailingAddress + ", residentialAddress="
            + residentialAddress + '}';
   }

}

As was the case with Dozer, the classes being mapped need to have no-arguments constructors and "set" and "get" methods to support conversion in both directions without any special additional configuration. Also, as was the case with Dozer, Orika maps same-named fields automatically and makes it easy to configure the mapping of the exceptions (fields whose names don't match). The next code listing, for a class I call OrikaPersonConverter, demonstrates the instantiation and configuration of an Orika MapperFactory to map most fields by default and to map the fields with different names than each other ("municipality" and "city") through explicit mapping configuration. Once the MapperFactory is configured, copying from one object to another is easy and both directions are depicted in the methods copyPersonTypeFromPerson and copyPersonFromPersonType.

OrikaPersonConverter
package dustin.examples.orikademo;

import com.blogspot.marxsoftware.AddressType;
import com.blogspot.marxsoftware.PersonType;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;

/**
 * Convert between instances of {@link com.blogspot.marxsoftware.PersonType}
 * and {@link dustin.examples.orikademo.Person}.
 * 
 * @author Dustin
 */
public class OrikaPersonConverter
{
   /** Orika Mapper Facade. */
   private final static MapperFacade mapper;

   static
   {
      final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
      mapperFactory.classMap(Address.class, AddressType.class)
                   .field("municipality", "city")
                   .byDefault()
                   .register();
      mapper = mapperFactory.getMapperFacade();
   }

   /** No-arguments constructor. */
   public OrikaPersonConverter() {}

   /**
    * Provide an instance of {@link com.blogspot.marxsoftware.PersonType}
    * that corresponds with provided {@link dustin.examples.orikademo.Person} as
    * mapped by Orika Mapper.
    * 
    * @param person Instance of {@link dustin.examples.orikademo.Person} from which
    *    {@link com.blogspot.marxsoftware.PersonType} will be extracted.
    * @return Instance of {@link com.blogspot.marxsoftware.PersonType} that
    *    is based on provided {@link dustin.examples.orikademo.Person} instance.
    */
   public PersonType copyPersonTypeFromPerson(final Person person)
   {
      PersonType personType = mapper.map(person, PersonType.class);
      return personType;
   }

   /**
    * Provide an instance of {@link dustin.examples.orikademo.Person} that corresponds
    * with the provided {@link com.blogspot.marxsoftware.PersonType} as 
    * mapped by Orika Mapper.
    * 
    * @param personType Instance of {@link com.blogspot.marxsoftware.PersonType}
    *    from which {@link dustin.examples.orikademo.Person} will be extracted.
    * @return Instance of {@link dustin.examples.orikademo.Person} that is based on the
    *    provided {@link com.blogspot.marxsoftware.PersonType}.
    */
   public Person copyPersonFromPersonType(final PersonType personType)
   {
      Person person = mapper.map(personType, Person.class);
      return person;
   }
}

As is the case with Dozer, the mapping between two classes is bidirectional and so only needs to be made once and will apply in copying from either object to the other.

Conclusion

Like Dozer, Orika offers much more customizability and flexibility than demonstrated in this post. However, for relatively simple mappings (which are very common with applications using JAXB-generated objects), Orika is very easy to use out of the box. A good resource for learning more about Orika is the Orika User Guide.

Monday, December 9, 2013

Dozer: Mapping JAXB Objects to Business/Domain Objects

Dozer is an open source (Apache 2 license) "Java Bean to Java Bean mapper that recursively copies data from one object to another." As this description from its main web page states, it is used to map two JavaBeans instances for automatic data copying between the instances. Although these can be any of the many types of JavaBeans instances, I will focus this post on using Dozer to map JAXB-generated objects to "business data objects" (sometimes referred to as "domain objects").

In Java applications making use of the Java Architecture for XML Binding (JAXB), it is very common for developers to write specific business or domain objects for use in the application itself and only use the JAXB-generated objects for reading (unmarshalling) and writing (marshalling) XML. Although using the JAXB-generated objects themselves as the business/domain objects has some appeal (DRY), there are disadvantages to this approach. JAXB-generated classes do not have toString(), equals(Object), or hashCode() implementations, making these generated classes unsuitable for use in many types of collections, unsuitable for comparison other than identity comparison, and unsuitable for easily logging their contents. Manually editing these generated classes after their generation is tedious and is not conducive to regeneration of the JAXB classes again when even slight changes might be made to the source XSD.

Although JAXB2 Basics can be used to ensure that JAXB-generated classes have some of the common methods needs for use in collections, use in comparisons, and for logging of their contents, a potentially even bigger issue with using JAXB-generated classes as domain/business objects is the tight coupling of business logic to XSD this entails. A schema change in an XSD (such as for version update) typically leads to a different package structure for classes generated from that XSD via JAXB. The different package structure then forces all code that imports those JAXB-generated classes to change their import statements. Content changes to the XSD can have even more dramatic impacts, affecting get/set methods on the JAXB classes that would be strewn throughout the application if the JAXB classes are used for domain/business objects.

Assuming that one decides to not use JAXB-generated classes as business/domain classes, there are multiple ways to map the generated JAXB classes to the classes defining the business/domain objects via a "mapping layer" described in code or configuration. To demonstrate two code-based mapping layer implementations and to demonstrate a Dozer-based mapping layer, I introduce some simple examples of JAXB-generated classes and custom built business/domain classes.

The first part of the example for this post is the XSD from which JAXB'x xjc will be instructed to general classes for marshalling to XML described by that XSD or unmarshalling from XML described by that XSD. The XSD, which is shown next, defines a Person element which can have nested MailingAddress and ResidentialAddress elements and two String attributes for first and last names. Note also that the main namespace is http://marxsoftware.blogspot.com/, which JAXB will use to determine the Java package hierarchy for classes generated from this XSD.

Person.xsd
<?xml version="1.0"?>
<xs:schema version="1.0"
           xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:marx="http://marxsoftware.blogspot.com/"
           targetNamespace="http://marxsoftware.blogspot.com/"
           elementFormDefault="qualified">
   
   <xs:element name="Person" type="marx:PersonType" />
   
   <xs:complexType name="PersonType">
      <xs:sequence>
         <xs:element name="MailingAddress" type="marx:AddressType" />
         <xs:element name="ResidentialAddress" type="marx:AddressType" minOccurs="0" />
      </xs:sequence>
      <xs:attribute name="firstName" type="xs:string" />
      <xs:attribute name="lastName" type="xs:string" />
   </xs:complexType>
   
   <xs:complexType name="AddressType">
      <xs:attribute name="streetAddress1" type="xs:string" use="required" />
      <xs:attribute name="streetAddress2" type="xs:string" use="optional" />
      <xs:attribute name="city" type="xs:string" use="required" />
      <xs:attribute name="state" type="xs:string" use="required" />
      <xs:attribute name="zipcode" type="xs:string" use="required" />
   </xs:complexType>
   
</xs:schema>

When xjc (the JAXB compiler delivered with Oracle's JDK) is executed against the above XSD, the following four classes are generated in the directory com/blogspot/marxsoftware (derived from the XSD's namespace): AddressType.java, PersonType.java, ObjectFactory.java, and package-info.java.

The next two code listings are of the two main classes of interest (PersonType.java and AddressType.java) generated by JAXB. The primary purpose of showing them here is as a reminder that they lack methods we often need our business/domain classes to have.

JAXB-generated PersonType.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.12.03 at 11:44:32 PM MST 
//


package com.blogspot.marxsoftware;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for PersonType complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * <complexType name="PersonType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="MailingAddress" type="{http://marxsoftware.blogspot.com/}AddressType"/>
 *         <element name="ResidentialAddress" type="{http://marxsoftware.blogspot.com/}AddressType" minOccurs="0"/>
 *       </sequence>
 *       <attribute name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "PersonType", propOrder = {
    "mailingAddress",
    "residentialAddress"
})
public class PersonType {

    @XmlElement(name = "MailingAddress", required = true)
    protected AddressType mailingAddress;
    @XmlElement(name = "ResidentialAddress")
    protected AddressType residentialAddress;
    @XmlAttribute(name = "firstName")
    protected String firstName;
    @XmlAttribute(name = "lastName")
    protected String lastName;

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

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

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

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

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

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

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

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

}
JAXB-generated AddressType.java
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2 
// See http://java.sun.com/xml/jaxb 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2013.12.03 at 11:44:32 PM MST 
//


package com.blogspot.marxsoftware;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;


/**
 * <p>Java class for AddressType complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * <complexType name="AddressType">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <attribute name="streetAddress1" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="streetAddress2" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="city" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="state" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *       <attribute name="zipcode" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AddressType")
public class AddressType {

    @XmlAttribute(name = "streetAddress1", required = true)
    protected String streetAddress1;
    @XmlAttribute(name = "streetAddress2")
    protected String streetAddress2;
    @XmlAttribute(name = "city", required = true)
    protected String city;
    @XmlAttribute(name = "state", required = true)
    protected String state;
    @XmlAttribute(name = "zipcode", required = true)
    protected String zipcode;

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

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

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

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

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

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

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

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

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

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

}

A common and straightforward tactic for copying data between the JAXB-generated objects and the custom-written business/domain objects is to use the "get" methods of one object and pass its return value to the "set" method of the other object. For example, in the process of unmarshalling/reading XML into the application, the results of "get" methods called on the JAXB-generated objects can be passed to the "set" methods of the business/domain objects. In the opposite direction, marshalling/writing XML can be easily accomplished by passing the result of "get" methods on the domain/business objects to corresponding "set" methods of the JAXB-generated objects. The next code listing is for PersonCoverter.java and illustrates one implementation of this approach.

PersonConverter.java
package dustin.examples.dozerdemo;

import com.blogspot.marxsoftware.AddressType;
import com.blogspot.marxsoftware.ObjectFactory;
import com.blogspot.marxsoftware.PersonType;
import dustin.examples.Address;
import dustin.examples.Person;

/**
 * Static functions for converting between JAXB-generated objects and domain
 * objects.
 * 
 * @author Dustin
 */
public class PersonConverter
{
   /**
    * Extract business object {@link dustin.examples.Person} from the JAXB
    * generated object {@link com.blogspot.marxsoftware.PersonType}.
    * 
    * @param personType JAXB-generated {@link com.blogspot.marxsoftware.PersonType}
    *    from which to extract {@link dustin.examples.Person} object.
    * @return Instance of {@link dustin.examples.Person} based on the provided
    *    {@link com.blogspot.marxsoftware.PersonType}.
    */
   public static Person extractPersonFromPersonType(final PersonType personType)
   {
      final String lastName = personType.getLastName();
      final String firstName = personType.getFirstName();
      final Address residentialAddress =
         extractAddressFromAddressType(personType.getResidentialAddress());
      final Address mailingAddress =
         extractAddressFromAddressType(personType.getMailingAddress());
      return new Person(lastName, firstName, residentialAddress, mailingAddress);
   }

   /**
    * Extract business object {@link dustin.examples.Address} from the JAXB
    * generated object {@link com.blogspot.marxsoftware.AddressType}.
    * 
    * @param addressType JAXB-generated {@link com.blogspot.marxsoftware.AddressType}
    *    from which to extract {@link dustin.examples.Address} object.
    * @return Instance of {@link dustin.examples.Address} based on the provided
    *    {@link com.blogspot.marxsoftware.AddressType}.
    */
   public static Address extractAddressFromAddressType(final AddressType addressType)
   {
      return new Address(
         addressType.getStreetAddress1(), addressType.getStreetAddress2(),
         addressType.getCity(), addressType.getState(), addressType.getZipcode());
   }

   /**
    * Extract an instance of {@link com.blogspot.marxsoftware.PersonType} from
    * an instance of {@link dustin.examples.Person}.
    * 
    * @param person Instance of {@link dustin.examples.Person} from which
    *    instance of JAXB-generated {@link com.blogspot.marxsoftware.PersonType}
    *    is desired.
    * @return Instance of {@link com.blogspot.marxsoftware.PersonType} based on
    *    provided instance of {@link dustin.examples.Person}.
    */
   public static PersonType extractPersonTypeFromPerson(final Person person)
   {
      final ObjectFactory objectFactory = new ObjectFactory();
      final AddressType residentialAddressType =
         extractAddressTypeFromAddress(person.getResidentialAddress());
      final AddressType mailingAddressType =
         extractAddressTypeFromAddress(person.getMailingAddress());
      
      final PersonType personType = objectFactory.createPersonType();
      personType.setLastName(person.getLastName());
      personType.setFirstName(person.getFirstName());
      personType.setResidentialAddress(residentialAddressType);
      personType.setMailingAddress(mailingAddressType);
      
      return personType;
   }

   /**
    * Extract an instance of {@link com.blogspot.marxsoftware.AddressType} from
    * an instance of {@link dustin.examples.Address}.
    * 
    * @param address Instance of {@link dustin.examples.Address} from which
    *    instance of JAXB-generated {@link com.blogspot.marxsoftware.AddressType}
    *    is desired.
    * @return Instance of {@link com.blogspot.marxsoftware.AddressType} based on
    *    provided instance of {@link dustin.examples.Address}.
    */
   public static AddressType extractAddressTypeFromAddress(final Address address)
   {
      final ObjectFactory objectFactory = new ObjectFactory();
      final AddressType addressType = objectFactory.createAddressType();
      addressType.setStreetAddress1(address.getStreetAddress1());
      addressType.setStreetAddress2(address.getStreetAddress2());
      addressType.setCity(address.getMunicipality());
      addressType.setState(address.getState());
      addressType.setZipcode(address.getZipCode());
      return addressType;
   }
}

The last code listing demonstrated a common third-party class approach to copying data in both directions between the JAXB-generated objects and the domain/business objects. Another approach is to build this copying capability into the domain/business objects themselves. This is shown in the next two code listings for PersonPlus.java and AddressPlus.java which are versions of the previously covered Person.java and Address.java with support added for copying data to and from JAXB-generated objects. For convenience, I added the new methods to the bottom of the classes after the toString implementations.

PersonPlus.java
package dustin.examples;

import com.blogspot.marxsoftware.ObjectFactory;
import com.blogspot.marxsoftware.PersonType;
import java.util.Objects;

/**
 * Person class enhanced to support copying to/from JAXB-generated PersonType.
 * 
 * @author Dustin
 */
public class PersonPlus
{
   private String lastName;
   private String firstName;
   private AddressPlus mailingAddress;
   private AddressPlus residentialAddress;

   public PersonPlus(
      final String newLastName,
      final String newFirstName,
      final AddressPlus newResidentialAddress,
      final AddressPlus newMailingAddress)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.residentialAddress = newResidentialAddress;
      this.mailingAddress = newMailingAddress;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   public void setLastName(String lastName) {
      this.lastName = lastName;
   }

   public String getFirstName()
   {
      return this.firstName;
   }
   
   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }
   
   public AddressPlus getMailingAddress()
   {
      return this.mailingAddress;
   }

   public void setMailingAddress(AddressPlus mailingAddress)
   {
      this.mailingAddress = mailingAddress;
   }

   public AddressPlus getResidentialAddress()
   {
      return this.residentialAddress;
   }

   public void setResidentialAddress(AddressPlus residentialAddress)
   {
      this.residentialAddress = residentialAddress;
   }

   @Override
   public int hashCode()
   {
      int hash = 3;
      hash = 19 * hash + Objects.hashCode(this.lastName);
      hash = 19 * hash + Objects.hashCode(this.firstName);
      hash = 19 * hash + Objects.hashCode(this.mailingAddress);
      hash = 19 * hash + Objects.hashCode(this.residentialAddress);
      return hash;
   }

   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final PersonPlus other = (PersonPlus) obj;
      if (!Objects.equals(this.lastName, other.lastName))
      {
         return false;
      }
      if (!Objects.equals(this.firstName, other.firstName))
      {
         return false;
      }
      if (!Objects.equals(this.mailingAddress, other.mailingAddress))
      {
         return false;
      }
      if (!Objects.equals(this.residentialAddress, other.residentialAddress))
      {
         return false;
      }
      return true;
   }

   @Override
   public String toString() {
      return  "PersonPlus{" + "lastName=" + lastName + ", firstName=" + firstName
            + ", mailingAddress=" + mailingAddress + ", residentialAddress="
            + residentialAddress + '}';
   }
   
   /**
    * Provide a JAXB-generated instance of {@link com.blogspot.marxsoftware.PersonType}
    * that corresponds to me.
    * 
    * @return Instance of {@link com.blogspot.marxsoftware.PersonType} that
    *    corresponds to me.
    */
   public PersonType toPersonType()
   {
      final ObjectFactory objectFactory = new ObjectFactory();
      final PersonType personType = objectFactory.createPersonType();
      personType.setFirstName(this.firstName);
      personType.setLastName(this.lastName);
      personType.setResidentialAddress(this.residentialAddress.toAddressType());
      personType.setMailingAddress(this.mailingAddress.toAddressType());
      return personType;
   }

   /**
    * Provide instance of {@link dustin.examples.PersonPlus} corresponding
    * to the provided instance of JAXB-generated object
    * {@link com.blogspot.marxsoftware.PersonType}.
    * 
    * @param personType Instance of JAXB-generated object
    *    {@link com.blogspot.marxsoftware.PersonType}.
    * @return Instance of me corresponding to provided JAXB-generated object
    *    {@link com.blogspot.marxsoftware.PersonType}.
    */
   public static PersonPlus fromPersonType(final PersonType personType)
   {
      final AddressPlus residentialAddress =
         AddressPlus.fromAddressType(personType.getResidentialAddress());
      final AddressPlus mailingAddress =
         AddressPlus.fromAddressType(personType.getMailingAddress());
      return new PersonPlus(personType.getLastName(), personType.getFirstName(),
                            residentialAddress, mailingAddress);
   }
}
AddressPlus.java
package dustin.examples;

import com.blogspot.marxsoftware.AddressType;
import com.blogspot.marxsoftware.ObjectFactory;
import java.util.Objects;

/**
 * Address class with support for copying to/from JAXB-generated class
 * {@link com.blogspot.marxsoftware.AddressType}.
 * 
 * @author Dustin
 */
public class AddressPlus
{
   private String streetAddress1;
   private String streetAddress2;
   private String municipality;
   private String state;
   private String zipCode;

   public AddressPlus(
      final String newStreetAddress1,
      final String newStreetAddress2,
      final String newMunicipality,
      final String newState,
      final String newZipCode)
   {
      this.streetAddress1 = newStreetAddress1;
      this.streetAddress2 = newStreetAddress2;
      this.municipality = newMunicipality;
      this.state = newState;
      this.zipCode = newZipCode;
   }

   public String getStreetAddress1()
   {
      return this.streetAddress1;
   }

   public void setStreetAddress1(String streetAddress1)
   {
      this.streetAddress1 = streetAddress1;
   }

   public String getStreetAddress2()
   {
      return this.streetAddress2;
   }

   public void setStreetAddress2(String streetAddress2)
   {
      this.streetAddress2 = streetAddress2;
   }

   public String getMunicipality()
   {
      return this.municipality;
   }

   public void setMunicipality(String municipality)
   {
      this.municipality = municipality;
   }

   public String getState() {
      return this.state;
   }

   public void setState(String state)
   {
      this.state = state;
   }

   public String getZipCode() 
   {
      return this.zipCode;
   }

   public void setZipCode(String zipCode)
   {
      this.zipCode = zipCode;
   }

   @Override
   public int hashCode()
   {
      return Objects.hash(
         this.streetAddress1, this.streetAddress2, this.municipality,
         this.state, this.zipCode);
   }

   @Override
   public boolean equals(Object obj)
   {
      if (obj == null) {
         return false;
      }
      if (getClass() != obj.getClass()) {
         return false;
      }
      final AddressPlus other = (AddressPlus) obj;
      if (!Objects.equals(this.streetAddress1, other.streetAddress1))
      {
         return false;
      }
      if (!Objects.equals(this.streetAddress2, other.streetAddress2))
      {
         return false;
      }
      if (!Objects.equals(this.municipality, other.municipality))
      {
         return false;
      }
      if (!Objects.equals(this.state, other.state))
      {
         return false;
      }
      if (!Objects.equals(this.zipCode, other.zipCode))
      {
         return false;
      }
      return true;
   }

   @Override
   public String toString()
   {
      return "Address{" + "streetAddress1=" + streetAddress1 + ", streetAddress2="
         + streetAddress2 + ", municipality=" + municipality + ", state=" + state
         + ", zipCode=" + zipCode + '}';
   }

    /**
    * Provide a JAXB-generated instance of {@link com.blogspot.marxsoftware.AddressType}
    * that corresponds to an instance of me.
    *
    * @return Instance of JAXB-generated {@link com.blogspot.marxsoftware.AddressType}
    *    that corresponds to me.
    */
   public AddressType toAddressType()
   {
      final ObjectFactory objectFactory = new ObjectFactory();
      final AddressType addressType = objectFactory.createAddressType();
      addressType.setStreetAddress1(this.streetAddress1);
      addressType.setStreetAddress2(this.streetAddress2);
      addressType.setCity(this.municipality);
      addressType.setState(this.state);
      addressType.setZipcode(this.zipCode);
      return addressType;
   }

   /**
    * Provide instance of {@link dustin.examples.AddressPlus} corresponding
    * to the provided instance of JAXB-generated object
    * {@link com.blogspot.marxsoftware.AddressType}.
    * 
    * @param addressType Instance of JAXB-generated object
    *    {@link com.blogspot.marxsoftware.AddressType}.
    * @return Instance of me corresponding to provided JAXB-generated object
    *    {@link com.blogspot.marxsoftware.AddressType}.
    */
   public static AddressPlus fromAddressType(final AddressType addressType)
   {
      return new AddressPlus(
         addressType.getStreetAddress1(),
         addressType.getStreetAddress2(),
         addressType.getCity(),
         addressType.getState(),
         addressType.getZipcode());
   }
}

The two approaches demonstrated above for mapping JAXB-generated objects to business/domain objects will definitely work and for my simple example might be considered the best approaches to use (especially given that NetBeans made the generation of the business/domain objects almost trivial). However, for more significant object hierarchies that require mapping, the Dozer configuration-based mapping might be considered preferable.

Dozer is downloaded from the download page (dozer-5.3.2.jar in this case). The Getting Started page shows that mapping is really easy (minimal configuration) when the attributes of the classes being mapped have the same names. This is not the case in my example in which I intentionally made one attribute "city" and the other "municipality" to make the mapping more interesting. Because these names are different, I need to customize the Dozer mapping and this is done with XML mapping configuration. The necessary mapping file is named with a "default mapping name" of dozerBeanMapping.xml and is shown next. I only needed to map the two fields with different names (city and municipality) because all other fields of the two classes being mapped have the same names and are automatically mapped together without explicit configuration.

dozerBeanMapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">

  <configuration>
    <stop-on-errors>true</stop-on-errors>
    <date-format>MM/dd/yyyy HH:mm:ss</date-format>
    <wildcard>true</wildcard>
  </configuration>

  <mapping>
    <class-a>dustin.examples.Address</class-a>
    <class-b>com.blogspot.marxsoftware.AddressType</class-b>
      <field>
        <a>municipality</a>
        <b>city</b>
      </field>
  </mapping>  
                   
</mappings>  

Note that XML is not the only approach that can be used to customize Dozer mapping; annotations and programmatic API are also supported.

The Dozer 3rd Party Object Factories page briefly covers using Dozer with JAXB and using the JAXBBeanFactory. It is also recommended that injection be used with Dozer and an example of Spring integration is provided. For my simple example of applying Dozer, I'm not using those approaches, but use the very straight forward instantiation approach. This is shown in the next code listing.

DozerPersonConverter.java
package dustin.examples.dozerdemo;

import com.blogspot.marxsoftware.PersonType;
import dustin.examples.Person;
import java.util.ArrayList;
import java.util.List;
import org.dozer.DozerBeanMapper;

/**
 * Dozer-based converter.
 * 
 * @author Dustin
 */
public class DozerPersonConverter
{
   static final DozerBeanMapper mapper = new DozerBeanMapper();
   
   static
   {
      final List<String> mappingFilesNames = new ArrayList<>();
      mappingFilesNames.add("dozerBeanMapping.xml");
      mapper.setMappingFiles(mappingFilesNames);
   }

   /**
    * Provide an instance of {@link com.blogspot.marxsoftware.PersonType}
    * that corresponds with provided {@link dustin.examples.Person} as
    * mapped by Dozer Mapper.
    * 
    * @param person Instance of {@link dustin.examples.Person} from which
    *    {@link com.blogspot.marxsoftware.PersonType} will be extracted.
    * @return Instance of {@link com.blogspot.marxsoftware.PersonType} that
    *    is based on provided {@link dustin.examples.Person} instance.
    */
   public PersonType copyPersonTypeFromPerson(final Person person)
   {
      final PersonType personType = 
         mapper.map(person, PersonType.class);
      return personType;
   }

   /**
    * Provide an instance of {@link dustin.examples.Person} that corresponds
    * with the provided {@link com.blogspot.marxsoftware.PersonType} as 
    * mapped by Dozer Mapper.
    * 
    * @param personType Instance of {@link com.blogspot.marxsoftware.PersonType}
    *    from which {@link dustin.examples.Person} will be extracted.
    * @return Instance of {@link dustin.examples.Person} that is based on the
    *    provided {@link com.blogspot.marxsoftware.PersonType}.
    */
   public Person copyPersonFromPersonType(final PersonType personType)
   {
      final Person person = 
         mapper.map(personType, Person.class);
      return person;
   }
}

The previous example shows how little code is required to map the JAXB-generated objects to business/domain objects. Of course, there was some XML needed, but only for fields with different names. This implies that the more the names of the fields differ, the more configuration is required. However, as long as fields are mostly mapped one-to-one without any special "conversion" logic between them, Dozer replaces much of the tedious code with configuration mapping.

If fields needed to be converted (such as converting meters in one object to kilometers in another object), then this mapping support may be less appealing when custom converters must be written. Dozer mapping can also become more difficult to apply correctly with deeply nested objects, but my example did nest Address within Person as a simple example. Although complex mappings might become less appealing in Dozer, many mappings of JAXB-generated objects to business/domain objects are simple enough mappings to be well served by Dozer.

One last thing I wanted to point out in this post is that Dozer has runtime dependencies on some third-party libraries. Fortunately, these libraries are commonly used in Java projects anyway and are readily available. As the next two images indicate, the required runtime dependencies are SLF4J, Apache Commons Lang, Apache Commons Logging, and Apache BeanUtils.

Dozer Runtime Dependencies Page
NetBeans 7.4 Project Libraries for this Post's Examples

There is a small amount of effort required to set up Dozer and its dependencies and then to configure mappings, but this effort can be well rewarded with significantly reduced mapping code in many common JAXB-to-business object data copying applications.