JiBX extras

The /lib/jibx-extras.jar provides a number of optional (but useful) classes you may want to use in your JiBX work. These classes are described on this page. To use them, you'll need to include the jar in your runtime classpath.

Roundtripping documents

Binding definitions can be complex to construct and verify. It's often useful to try some tests with a new binding to make sure it's working the way you expect. In general this is going to require application code to verify that documents are being marshalled and/or unmarshalled as expected, but you can try some simple tests just using the code included with JiBX along with your bound classes.

To support this JiBX includes a pair of classes in the "extras" jar. The org.jibx.extras.DocumentComparator class provides an easy way of comparing two XML documents. It uses a pair of parsers to read the documents in parallel, comparing the streams of components seen from the two documents. The comparison ignores differences in whitespace separating elements, but treats whitespace as significant within elements with only character data content. It also ignores comments and processing instructions included in the documents, since these are normally not processed by JiBX.

The org.jibx.extras.TestRoundtrip class builds on this support, using the DocumentComparator to compare the results of a round-trip conversion. It first unmarshals an input document using your binding definition and classes, then marshals the resulting object structure to an output document. It ends with comparing the original documents with the output document, reporting an error if they down match.

In order to make running a roundtrip test as simple as possible the TestRoundtrip class is the main class for the jar file. To run a roundtrip test with your bound classes and a sample XML document file you just need to go to the root directory of your application class structure and run the command (as a single line, show split here only for formatting):

java -jar jibx-root/lib/jibx-extras.jar
    org.jibx.extras.TestRoundtrip class-name sample.xml

where jibx-root is the path to the root directory for the JiBX distribution on your system, class-name is the fully-qualified name (including the full package specification, so com.sosnoski.site.Menu to name the Menu class within the com.sosnoski.site package) for the expected root unmarshalled object, and sample.xml is the sample document file.

This technique of running TestRoundtrip directly from the jar only works when all the classes needed by your application are available as separate class files (rather than jars or such) within a single directory structure. TestRoundtrip automatically includes the current execution directory in the classpath for loading your files (along with the jar files used for the JiBX runtime), so this works well for simple applications. For applications that need classes from jars or other directories you need to instead run TestRoundtrip directly using a command line like (on Unix/Linux; if you're using Windows or MS-DOS you'll need to reverse the slashs and use ';' instead of ':' as a path separator character):

java -cp jibx-root/lib/jibx-extras.jar:lib/lib1.jar:lib/lib2.jar:.
    class-name sample.xml

This allows you to include any required library files in the classpath you define for the JVM (in this case lib/lib1.jar and lib/lib2.jar).

You can also use TestRoundtrip in combination with the bind-on-load feature, which can be especially useful when you're experimenting with binding definitions to use with your classes and documents. Here's the equivalent to the first of the above command lines, but this time using the binding.xml binding definition for the test:

java -cp .:jibx-root/lib/jibx-bind.jar:jibx-root/lib/jibx-extras.jar
    org.jibx.binding.Run -b binding.xml org.jibx.extras.TestRoundtrip class-name sample.xml

The direct comparison provided by using DocumentComparator to match the input document against the output document isn't always an appropriate test. Some types of binding definition constructs (such as default values, or unordered child elements) can cause the output to differ from the input even though both are equivalent in terms of the binding. To handle this type of situation, TestRoundtrip allows you to supply a third command line parameter giving the name of a separate comparison document to be matched against the output. It also allows multiple sets of arguments for testing more than one binding at a time - see the JavaDocs or source code for details.

If the result of running TestRoundtrip is a successful comparison it just exits silently. If there's an error in the comparison, it prints out the error information to the console and saves a copy of the generated output document as temp.xml in the working directory.

Map handling

Another useful class in jibx-extras.jar supports marshalling and unmarshalling of java.util.Map instances. This is the org.jibx.extras.HashMapperStringToComplex class. The support it provides is limited to maps with java.lang.String keys and object values with mappings defined by the binding (using <mapping> elements), and the only flexibility it provides is in choosing the name of the root element, but it's still useful for many cases. It's also intended as a sample implementation that can be modified for your own particular requirements. For more discussion of both this specific code and the use of custom marshallers and unmarshallers in general, see the binding tutorial section on Custom marshallers and unmarshallers.

The implementation included in jibx-extras.jar includes some nice customization features. By subclassing this implementation in your own code you can override the default names used for the item wrapper element and the key attribute, as well as control whether an item count is included and the name used for that attribute if so. There's also an alternative handler for maps with value objects that correspond to simple schema types (such as java.lang.String, java.lang.Integer, java.util.Date, etc.). This is the org.jibx.extras.HashMapperStringToSchemaTyp class. See the Javadocs for details on both of these classes.

ID and IDREF handlers

The org.jibx.extras.IdRefMapperBase and org.jibx.extras.IdDefRefMapperBase classes support special handling of objects using ID and IDREFs. These differ from the other classes in jibx-extras.jar in that they're abstract base classes which need to be subclassed by the user in order to construct usable marshaller/unmarshallers. The flexibility they provide is often worth the extra effort, though.

org.jibx.extras.IdRefMapperBase lets you use IDREF values directly in a collection, which is not supported by the basic JiBX code. It expects the XML structure for each item to be an element with no content and a single attribute giving the IDREF value (like ). When a subclass is used as the marshaller/unmarshaller for a structure within a collection the referenced objects will be converted to and from the XML IDREF representation. Subclasses can also be used as marshaller/unmarshallers outside of a collection, but in this case you could just as easily use an IDREF value directly. This class only works with IDREF values which have been defined before the point of reference.

org.jibx.extras.IdDefRefMapperBase is even more flexible. Subclasses can be used as marshaller/unmarshallers which will use a smart representation for objects, marshalling the full XML expression of an object the first time it's referenced and thereafter using only an IDREF representation (and unmarshalling the same type of structure).

Both of these classes define an abstract method which must be implemented by subclasses. The abstract method links the base class code to a particular object type, allowing the base code to load the ID value from an object instance. You can also override another method to control the name of the ID/IDREF attribute. See the Javadocs for details on both of these classes.

Versioned bindings

The org.jibx.extras.BindingSelector class supports marshalling and unmarshalling documents with different binding versions. As with the above classes, the support it provides is limited but useful. In the case of BindingSelector, the root element of the document needs to have some attribute that identifies the particular version used for a document. The version attribute value is used to select the binding to be applied when unmarshalling a document, and a supplied version can also be used when marshalling a document. See the binding tutorial section on Controlling JiBX with front-end code for full details.

Using document models

The extras also include marshaller/unmarshaller classes that allow you to use document models for portions of your documents. This can be useful in situations where documents can contain extension information that's not well structured, or where you need to work with XML document components (such as comments or processing instructions) that are not easily handled with data binding. The classes included currently support dom4j and W3C DOM models.

The dom4j support is provided by the org.jibx.extras.Dom4JElementMapper and org.jibx.extras.Dom4JListMapper marshaller/unmarshaller classes. To make use of these you'll need to separately download the dom4j library from the project web site, since this is an optional add-on that's not included in the JiBX distribution. The classes have been tested using dom4j release 1.4, but should be compatible with both older and future versions since the dom4j APIs are stable.

Dom4JElementMapper works with a single element, which may be optional or required in your binding definition. When unmarshalling it creates an instance of org.dom4j.Element to hold the content of that element, and when marshalling it requires that the object you supply is of that type. If you specify an element name when you use this in your binding definition that name will be matched when unmarshalling.

Dom4JListMapper works with a linked object type that implements java.util.List (the actual runtime type - as with Dom4JElementMapper the declared type in your binding definition is ignored and can be anything). When unmarshalling it will create an instance of java.util.ArrayList if a list is not passed in and any content is present, then return with all the content up to the close tag for the enclosing element added to the list as the appropriate types of dom4j components. When marshalling, it will simply write out any content in the list you provide directly (where the items in the list must be dom4j components).

The DOM support is provided by the org.jibx.extras.DomElementMapper, org.jibx.extras.DomListMapper, and org.jibx.extras.DomFragmentMapper marshaller/unmarshaller classes. These all make use of the JAXP API support included in recent version of the Java platform. They have been tested with the version of JAXP included with the Java 1.4.2 JRE from Sun Microsystems, but should be compatible with most versions of JAXP (despite being a "standard extension", versions of JAXP have been known to include broken code and/or incompatibilities between versions; it was used in this case mainly because it's bundled with the JRE and does not require a separate download).

DomElementMapper works with a single element, which may be optional or required in your binding definition. When unmarshalling it creates an instance of org.w3c.dom.Element to hold the content of that element, and when marshalling it requires that the object you supply is of that type. If you specify an element name when you use this in your binding definition that name will be matched when unmarshalling. Since the DOM API requires that each document component belongs to a particular document, this will create a org.w3c.dom.Document each time an instance of this marshaller/unmarshaller class is created.

DomListMapper works with a linked object type that implements java.util.List (the actual runtime type - as with the other marshaller/unmarshaller classes in this group the declared type in your binding definition is ignored and can be anything), while DomFragmentMapper works with an instance of org.w3c.dom.DocumentFragment. When unmarshalling, each of these will create an instance of the appropriate type (using java.util.ArrayList for DomListMapper) if one is not passed in and any content is present, then return with all the content up to the close tag for the enclosing element added to that instance as the appropriate types of DOM components. When marshalling, they will simply write out any content in the list or fragment you provide directly (where the items in the list must be DOM components). As with DomElementMapper, these will create a org.w3c.dom.Document each time an instance of the marshaller/unmarshaller class is created.

Document model output

The extras jar includes an alternative to the standard output writers that instead of marshalling into an OutputStream or Writer creates a document model. Currently there is an implementation for JDOM, although other implementations like dom4j and W3C DOM are possible.

In order to use the JDOM implementation JDOMWriter you need to put jdom.jar into your classpath. You should use release 1.0 available from http://www.jdom.org/.

There a three different usage patterns for JDOMWriter. The first one is the equivalent of marshalling into e.g. a StringWriter. It creates a new JDOM Document on every run:

     IBindingFactory bfact = BindingDirectory.getFactory(clazz);
    IMarshallingContext mctx = bfact.createMarshallingContext();
    
    JDOMWriter jdomWriter = new JDOMWriter(mctx.getNamespaces());
    mctx.setXmlWriter(jdomWriter);
    mctx.marshalDocument(example);
    mctx.endDocument();
    
    Document document = jdomWriter.getDocument();

(You need to change "clazz" and "example" of course.)

The other two possibilities are appending to an existing Document's root node or to an existing Element (possibly deeply nested within a Document). The only thing that changes in the code fragment above is the JDOMWriter constructor you need to use. You need to pass it your existing Document or Element instance as second parameter.

The resulting Document can be used as you like. Refer to the JDOM documentation for more information. Wrapping it into a JDOMSource e.g. could be the input to a XSLT transformation.