Version 3

    Object Model Factory

     

    Here is the definition of the ObjectModelFactory interface

     

    package org.jboss.xml.binding;
    
    public interface ObjectModelFactory
    {
       /**
        * This method is called by the object model factory and returns the root of the object graph.
        * If the <code>root</code> argument is null the factory is supposed to create and return a new one.
        * If the <code>root</code> argument is not null (i.e. the user provided the root object through the
        * org.jboss.xml.binding.Unmarshaller) then the factory should either just return it as is
        * or extract the real root from the <code>root</code> argument based on the namespace URI and local name.
        *
        * @param root  an object that is the root or which contains the root object
        * @param navigator  content navigator
        * @param namespaceURI  namespace URI of the root
        * @param localName  local name of the root
        * @param attrs  attributes of the root object
        * @return  the root of the object graph
        */
       Object newRoot(java.lang.Object root,
                      org.jboss.xml.binding.ContentNavigator navigator,
                      java.lang.String namespaceURI,
                      java.lang.String localName,
                      org.xml.sax.Attributes attrs);
    }
    

     

    All object model factories must implement this interface.

    Besides, the newRoot method defined in the ObjectModelFactory interface an object model factory should also implement a set of the following methods that will be discovered by the framework with introspection:

     

    a set of
    newChild
    methods

    This method is called on the object model factory by the framework when parsing of a new XML element started.

    Each

    newChild

    method must have five arguments:

      1. parent object of a concrete Java type (not java.lang.Object) for this new child

      2. instance of org.jboss.xml.binding.ContentNavigator

      3. namespace URI of the child XML element as java.lang.String

      4. local name of the child XML element as java.lang.String

      5. attributes of the child XML element as org.xml.sax.Attributes

     

    Each

    newChild

    method returns either a new instance of an object that represents the XML element with the namespace URI and local name (in this case, the child XML element is accepted, i.e. should be represented in the object graph) or

    null

    if this child XML element should be ignored, i.e. not be represented in the object graph.

     

    a set of
    addChild
    methods

    This method is called on the object model factory by the framework when parsing of a child XML element is complete.

    Each

    addChild

    method must have five arguments:

      1. parent object of a conrete Java type (not java.lang.Object) of the child

      2. child object of a concrete Java type (returned earlier by the

        newChild()

        )

      3. instance of org.jboss.xml.binding.ContentNavigator

      4. namespace URI for the child XML element as java.lang.String

      5. local name for the child XML element as java.lang.String

     

    When

    addChild

    method is called, the child object is supposed to be populated with all the data from the corresponding XML element. The child object now can be validated and added to the parent.

     

    a set of
    setValue
    methods

    This method is called on the object model factory by the framework when a new XML element with text content was parsed.

    The method must have four arguments:

      1. an object of a concrete Java type (not java.lang.Object) which was returned earlier by a

        newChild

        for which the value of an XML element was read

      2. instance of org.jboss.xml.binding.ContentNavigator

      3. namespace URI of the child XML element as java.lang.String

      4. local name of the child XML element as java.lang.String

      5. the value of the child XML element as java.lang.String

     

    In

    setValue

    method the object model factory is supposed to set a value on the field which represents the parsed XML element possibly converting the parsed XML element value to field's Java type.

     

    Code example

     

    Let's now implement

    ObjectModelFactory

    for book following the rules above.

     

    public class BookObjectFactory
       implements org.jboss.xml.binding.ObjectModelFactory
    {
       // ObjectModelFactory implementation
    
       /**
        * Return the root.
        */
       public Object newRoot(Object root,
                             ContentNavigator navigator,
                             String namespaceURI,
                             String localName,
                             Attributes attrs)
       {
          final Book book;
          if(root == null)
          {
             root = book = new Book();
          }
          else
          {
             book = (Book) root;
          }
    
          if(attrs.getLength() > 0)
          {
             for(int i = 0; i < attrs.getLength(); ++i)
             {
                if(attrs.getLocalName(i).equals("isbn"))
                {
                   book.setIsbn(attrs.getValue(i));
                }
             }
          }
    
          return root;
       }
    
       // Methods discovered by introspection
    
       /**
        * Called when a child element with text content is read for book.
        */
       public void setValue(Book book,
                            ContentNavigator navigator,
                            String namespaceURI,
                            String localName,
                            String value)
       {
          if("title".equals(localName))
          {
             book.setTitle(value);
          }
          else if("author".equals(localName))
          {
             book.setAuthor(value);
          }
       }
    
       /**
        * Called when parsing of a new child XML element started.
        */
       public Object newChild(Book book,
                              ContentNavigator navigator,
                              String namespaceURI,
                              String localName,
                              Attributes attrs)
       {
          Object child = null;
          if("character".equals(localName))
          {
             child = new BookCharacter();
          }
          return child;
       }
    
       /**
        * Called when a child XML element with text content is read for character.
        */
       public void setValue(BookCharacter character,
                            ContentNavigator navigator,
                            String namespaceURI,
                            String localName,
                            String value)
       {
          if("name".equals(localName))
          {
             character.setName(value);
          }
          else if("friend-of".equals(localName))
          {
             character.setFriendOf(value);
          }
          else if("since".equals(localName))
          {
             character.setSince(value);
          }
          else if("qualification".equals(localName))
          {
             character.setQualification(value);
          }
       }
    
       /**
        * Called when parsing character is complete.
        */
       public void addChild(Book book, BookCharacter character, ContentNavigator navigator)
       {
          book.addCharacter(character);
       }
    }
    

     

     

    Unmarshalling client

     

    Now, having Java classes and object model factory we can write a client code.

     

          // get the XML stream
          InputStream is = new FileInputStream("resources/xml/book/" + xmlSource);
    
          // create unmarshaller
          Unmarshaller unmarshaller = new Unmarshaller();
    
          // create an instance of ObjectModelFactory
          ObjectModelFactory factory = new BookObjectFactory();
    
          // let the object model factory to create an instance of Book and populate it with data from XML
          Book book = (Book)unmarshaller.unmarshal(is, factory, null);
    
          // close the XML stream
          is.close();
    

     

    To unmarshal into an existing instance of

    Book

    , we would just pass the book instance as the third parameter to

    Unmarshaller.unmarshal()

    , e.g.

     

          Book book = new Book();
          unmarshaller.unmarshal(is, factory, book);
    

     

    That's it!

     

     

    Note, unmarshalling code is aware of neither XML schemas nor DTDs. XML schemas and DTDs are used transparently to the client by the underlying XML parser for XML content validation.

     

    See also, GenericObjectModelFactory.