Jump to: navigation, search

EclipseLink/Examples/MOXy/MeetInTheMiddle/Advantages

Overview

Domain Model

For this example our Java object model will consist of two objects (each with one property):

  • Customer(address: Address)
  • Address(street: String).

XML Input

The following XML document will be used as the input for each of the following code examples:

<customer>
     <contact-info>
          <address>
               <street>123 Any Street</street>
          </address>
     </contact-info>
</customer>

Approach #1 – Using the JAXP APIs

In this approach developers convert their XML into a DOM or SAX events using the JAXP APIs and then write code to pull out the relevant data and populate their domain objects. The following code demonstrates what such an approach might look like.

// XML Input
File xmlInput = new File(input.xml);
 
// Process the XML
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(xmlInput);
 
// Build the Customer Object
Node customerNode = document.getDocumentElement();
Customer customer = new Customer();
 
// Build the Address Object
Node contactInfoNode = getChildByName(customerNode, “contact-info”);
if(null != contactInfoNode) {
     Node addressNode = getChildByName(contactInfoNode, “address”);
     if(null != addressNode) {
          Address address = new Address();
          customer.setAddress(address);
          Node streetNode = getChildByName(addressNode, “street”);
          address.setStreet(getText(streetNode));
     }
}
 
private Node getChildByName(Node parent, String name) {
     Node child = parent.getFirstChild();
     while(child != null) {
          if(child.getNodeType() == Node.ELEMENT_NODE) {
               if(child.getNodeName().equals(name)) {
                    return child;
               }
          }
          child = child.getNextSibling();
     }
     return null;
}
 
private String getText(Node parent) {
     StringBuilder stringBuilder = new StringBuilder();
     NodeList childNodes = parent.getChildNodes();
     for(int x=0; x<childNodes.getlength();> Node possibleTextNode = childNodes.item(x);
          if(possibleTextNode.getNodeType() == Node.TEXT_NODE) {
               stringBuilder.append(possibleTextNode.getNodeValue());
          }
     }
     return stringBuilder.toString();
}

Approach #2 – Code Generation Based XML Binding

After using code similar to the first approach many developers look for an easier means of processing XML data. This is where many feel code generated binding solutions come into play. An object model is generated from an XML schema, and then during the parsing stage instead of a DOM, the developer is returned a tree of objects. The JAXB 1.0 specification was introduced as an attempt to standardize this code generation approach.

At first glance, you have achieved your objective of representing your data in object form. The problem is that this object model bares little resemblance to one you may have created yourself. An object is created for each level of nesting in the XML document, and the code itself contains all the marshalling and unmarshalling code.

Generated Object Model

  • CustomerImpl(contactInfo: ContactInfoImpl)
  • ContactInfoImpl(address: AddressImpl)
  • AddressImpl(street: String)

Desired Object Model

  • Customer(address: Address)
  • Address(street: String)

Your object model will be used for more than XML handling (application logic, persistence to a database using JPA, etc), and since the generated model is not suitable, you will need to convert from the generated model to your domain model. The following code demonstrates how this can be done. Note in the code below that we have eliminated the need for the helper functions, but code must still be put in place to walk the tree and copy the data.

// XML Input
File xmlInput = new File(input.xml);
 
// Processs the XML
JAXBContext context = JAXBContext.newInstance("model");
Unmarshaller unmarshaller = context.createUnmarshaller();
CustomerImpl customerImpl =
(CustomerImpl) unmarshaller.unmarshal(xmlInput);
 
// Build the Customer Object
Customer customer = new Customer();
 
// Build the Address Object
ContactInfoImpl contactInfoImpl = customerImpl.getContactInfo();
if(null != contactInfoImpl) {
     AddressImpl addressImpl = customerImpl.getAddress();
     if(null != addressImpl) {
          Address address = new Address();
          customer.setAddress(address);
          address.setStreet(addressImpl.getStreet());
     }
}

Approach #3 – EclipseLink JAXB (MOXy)

The JAXB 2.0 specification has moved JAXB into this arena, but still relies on vendor extensions (such as those available in EclipseLink's JAXB (MOXy) component for true meet-in-the-middle mapping. Now let us look at the code example for the third and final approach. As you can see below the amount of coding effort is greatly reduced, especially when you consider that unlike the previous approaches the code required here remains constant as the complexity of the object model and XML grow.

// XML Input
File xmlInput = new File(input.xml);
 
// Process the XML
JAXBContext context = JAXBContext().newInstance("model");
Unmarshaller unmarshaller = context.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(xmlInput);

Where did the conversion logic go? Obviously there still has to be code to convert the XML input into the desired object model. This is handled by the object-to-XML runtime that uses mapping metadata created by the developer during design time to do the conversion. This metadata is supplied using annotations or in an external bindings file. In this example an @XmlPath annotation is added to the address field of Customer to customize the mapping information.

@XmlRootElement
public class Customer {
 
   @XmlPath("contact-info/address")
   private Address address;
}

Summary

At the end of the day, the core problem is that working with XML in Java is hard, or at least it can be. Code-generation based solutions tried to ease the problem by making XML easier to interact with. What I ultimately want as a developer though is to be isolated from XML altogether, which is what object-to-XML mapping provides.