Working with XML documents
In this section, you'll learn about running the JiBX binding compiler and using JiBX at run time to work with XML documents.
Running the JiBX binding compiler
To use the generated binding definition in working with XML documents, you first need to run the JiBX binding compiler tool. The binding compiler adds bytecode to your compiled class files that actually implements the conversions to and from XML, as specified by the binding definition. You must run the binding compiler each time
you recompile your Java classes or modify the binding definition, so it's generally best to add the binding compiler step as part of your project's standard build
process.
The binding compiler is included in the JiBX distribution as part of jibx-bind.jar. The JiBX documentation provides full details about different ways to run the binding compiler, including how you can run it at run time rather than as part of the build. JiBX also provides plug-ins for Eclipse and IntelliJ IDEA that automatically run the binding compiler when you're working in these IDEs.
For this tutorial's purposes, you'll keep things simple and just run the binding compiler through Ant, using the build.xml's bind target. Figure 2 shows the output that you should see when you run this target, assuming you've already run the compile and bindgen targets. (You can also run all three targets in sequence by listing them in order on the command line: ant compile bindgen bind.)
Figure 2. Ant build bind task
Using JiBX at run time
Listing 3 shows a simple test document matching the generated schema, included in the tutorial's code download as data.xml:
Listing 3. Default binding test document
<order orderNumber="12345678" orderDate="2008-10-18" shipDate="2008-10-22"
xmlns="http://jibx.org/starter">
<customer customerNumber="5678">
<firstName>John</firstName>
<lastName>Smith</lastName>
</customer>
<billTo>
<street1>12345 Happy Lane</street1>
<city>Plunk</city>
<state>WA</state>
<postCode>98059</postCode>
<country>USA</country>
</billTo>
<shipping>PRIORITY_MAIL</shipping>
<shipTo>
<street1>333 River Avenue</street1>
<city>Kirkland</city>
<state>WA</state>
<postCode>98034</postCode>
<country>USA</country>
</shipTo>
<item quantity="1" price="5.99">
<id>AC4983498512</id>
<description>Left-handed widget</description>
</item>
<item quantity="2" price="9.50">
<id>IW2349050499</id>
<description>Right-handed widget</description>
</item>
<item quantity="1" price="8.95">
<id>RC3000488209</id>
<description>High-speed MP3 rewinder</description>
</item>
</order> |
The download package also includes a simple test program, shown here as Listing 4, that demonstrates using JiBX for both unmarshalling and marshalling documents. (Marshalling is the process of generating an XML representation for an object in memory, potentially including objects linked from the original object. Unmarshalling is the reverse process of marshalling, building an object (and potentially a graph of linked objects) in memory from an XML representation.) The Ant run target executes this test program, using the Listing 3 document as input and writing the marshalled copy of the document to a file named out.xml.
Listing 4. Test program
public class Test
{
/**
* Unmarshal the sample document from a file, compute and set order total, then
* marshal it back out to another file.
*
* @param args
*/
public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Usage: java -cp ... " +
"org.jibx.starter.Test in-file out-file");
System.exit(0);
}
try {
// unmarshal customer information from file
IBindingFactory bfact = BindingDirectory.getFactory(Order.class);
IUnmarshallingContext uctx = bfact.createUnmarshallingContext();
FileInputStream in = new FileInputStream(args[0]);
Order order = (Order)uctx.unmarshalDocument(in, null);
// compute the total amount of the order
float total = 0.0f;
for (Iterator<Item> iter = order.getItems().iterator(); iter.hasNext();) {
Item item = iter.next();
total += item.getPrice() * item.getQuantity();
}
order.setTotal(new Float(total));
// marshal object back out to file (with nice indentation, as UTF-8)
IMarshallingContext mctx = bfact.createMarshallingContext();
mctx.setIndent(2);
FileOutputStream out = new FileOutputStream(args[1]);
mctx.setOutput(out, null);
mctx.marshalDocument(order);
System.out.println("Processed order with " + order.getItems().size() +
" items and total value " + total);
} catch (FileNotFoundException e) {
e.printStackTrace();
System.exit(1);
} catch (JiBXException e) {
e.printStackTrace();
System.exit(1);
}
}
} |
Figure 3 shows the output you should see when you run the run target:
Figure 3. Ant build run task
You can inspect the generated out.xml file to see how it matches the original input shown in Listing 3. Aside from namespace declaration and attribute order and the added total attribute in the output (computed and set by the test program), the two documents should be identical. This won't always be the case! JiBX, like most forms of data binding, works with only the "significant" data in the document, meaning those values being used by your application. Nonsignificant parts of the document (such as whitespace within a start or end tag, text between elements, and comments) are lost when you unmarshal a document. Part of the reason the input and output documents are so similar in this case is that the Listing 4 code sets the output format to use indentation of two spaces per element nesting level, matching the input document.
Sharp observers will notice one difference between the input and output that seems significant, in the item-list portion of the output document, shown in Listing 5:
Listing 5. Item list from output document
<item quantity="1" price="5.99">
<id>AC4983498512</id>
<description>Left-handed widget</description>
</item>
<item quantity="2" price="9.5">
<id>IW2349050499</id>
<description>Right-handed widget</description>
</item>
<item quantity="1" price="8.95">
<id>RC3000488209</id>
<description>High-speed MP3 rewinder</description>
</item>
|
If you compare the line shown in bold in Listing 5 with the corresponding line in the Listing 3 original document, you can see that the price has changed from 9.50 to 9.5, with the trailing zero removed. This is not an error, though. The representation used for the price value is a float, and in terms of both Java and XML schema, leading zeros before the decimal point and trailing zeros after the decimal point are not significant for a float.
|