Package org.opencypher.tools.xml

This package contains utilities that read XML files and build up a custom object graph by mapping the XML structure to annotated classes.

In order to parse XML into you object graph, you need to create an XmlParser for your root object type:

XmlParser<MyRoot> PARSER = XmlParser.xmlParser( MyRoot.class );
Your class MyRoot and the child node types should be annotated with @Element and have fields annotated with @Attribute to map the attributes of the xml element, and methods annotated with @Child to map the child elements of the xml element.

Accepted types for fields annotated with @Attribute are:

(See the source code of AttributeHandler for details)

It is also possible to use the @Attribute annotation on a method that accepts a single argument (and returns void). The accepted argument types are the same as for fields annotated with @Attribute.

Methods annotated with @Child may have any name, should return void and have one of the following parameters lists:

  • A single parameter, where the type of that parameter is annotated with @Element
    Example:
    
     @Child
     void add( SomeChildType child ) {
         // do what you want with the child object here
     }
    
     @Element(uri=YOUR_NAMESPACE_URI, name="some-child")
     class SomeChildType {
     }
     
  • A single parameter, that is a supertype of all of the types supplied as argument to the @Child annotation. Each of the types in the arguments list of the @Child annotation should be annotated with @Element, but the actual parameter type of the method does not need that annotation.
    Example:
    
     @Child(ActualChildOne.class, ActualChildTwo.class)
     void add( SomeChildInterface child ) {
         // do what you want with the child object here
     }
    
     interface SomeChildInterface {
     }
     @Element(uri=YOUR_NAMESPACE_URI, name="one")
     class ActualChildOne implements SomeChildInterface {
     }
     @Element(uri=YOUR_NAMESPACE_URI, name="two")
     class ActualChildTwo implements SomeChildInterface {
     }
     
  • A single String parameter. This handles text within the XML element. Alternatively, if the @Child annotation is given an argument of Comment.class the method handles XML comments within the XML element instead. For the root element type, it is also possible to give the @Child annotation an argument of Comment.Header.class, which makes the method handle XML comments from before the start of the first XML element.
    Examples:
    
     @Child
     void text( String text ) {
         // handle embedded text
     }
     @Child( Comment.class )
     void comment( String comment ) {
         // handle embedded comments
     }
     @Child( Comment.Header.class )
     void header( String headerComment ) {
         // handle embedded the header comment(s)
     }
     
  • Three parameters: char[] text, int start, int length. This works in the same way as if the method had a single String parameter, but uses the raw char[] from the parser, with a start offset and a length of the string found at that offset and does not have to instantiate a new string. This might be preferable if further parsing is to be made on the characters in the embedded text. As with String-methods, Comment.class and Comment.Header.class can be given as arguments to the @Child annotation in order to handle embedded comments or the header comment(s) of the file instead of embedded text.
    Examples:
    
     @Child
     void text( char[] text, int start, int length ) {
         // handle embedded text
     }
     @Child( Comment.class )
     void comment( char[] comment, int start, int length ) {
         // handle embedded comments
     }
     @Child( Comment.Header.class )
     void header( char[] headerComment, int start, int length ) {
         // handle embedded the header comment(s)
     }
     
Once a parser has been created, parsing an XML file to generate an object graph is as simple as invoking one of the parse-methods: There are also some more advanced features of the XML-to-object mapping available, such as:
  • Getting information about which XML file, and where in that file, an element was parsed from. This is done by having the class (that is annotated with @Element) implement the LocationAware interface.
  • Accepting (optional) attributes from other XML namespaces. This is done by specifying the uri-parameter of the @Attributeannotation.
In terms of implementation, mapping the object class structure into a parser that can be used to parse an XML file is handled by Structure. This class is responsible for creating NodeBuilder objects that knows how to instantiate and build objects for the various XML entities that the parser encounters. The actual parsing, and keeping track of which NodeBuilder maps to what part of the XML file is handled by ParserStateMachine. Converting XML attributes into values that can be assigned to fields, or passed into methods annotated with @Attribute is handled by AttributeHandler, which also handles the actual field assignment or invocation. For creating XmlFile attribute values, a Resolver instance is used for finding the xml file in question.