Abstract

xmlobj is a small collection of classes that allows an XML document to be used to load an object tree and vice versa. The mapping between the XML file and the object tree is done using a config XML file. A simple example followed by a more complicated one is described.

xmlobj can be used to build companion XML files for persisting object trees. A few constraints are placed upon the objects in the tree - they need to adhere to a javabean-like interface. Apart from this, the classes themselves contain no XML related code and the work is done entirely by the classes that are part of xmlobj.



Image generated using xmlobj


Mapping XML to Objects

Each XML tag can be represented by an object. Attributes within a tag can be represented as fields within that object. A tag can contain zero or more subtags. If a subtag can occur more than once, it will be represented by an object. These objects will be contained within the object representing the parent tag. If there is expected not to occur more than once, it can be represented as a field.

Loading and Saving the Object Tree

The relationship between XML tags and attributes and the corresponding objects and their fields is expressed in, what else, another XML file. The xmlobj routines use the contents of this file to create and populate the object tree. In order for the program to do this, the objects in the tree must have the right hooks.

Implementing an xmlobj friendly object

Each object has zero or more fields representing attributes or single occurrence subtags. Each object has zero or more child objects representing subtags.

Each object in the tree has a javabean like interface. All fields are represented by a property. To read and save to the property, the object implements get and set methods that take a value of the required type. For example, if the object has a property called name of type String the following methods are implemented.

	public void setName(String s);
	public String getName();
    

Subtags representing child objects are represented similar to indexed properties. This varies from the javabean interface. Two methods, one to add an object and another to get an array of all objects, are implemented for each indexed property. For example, if the object has an indexed property called acc of type AccountModel the following methods are implemented.

	public void addAcc(AccountModel c);
	public AccountModel[] getAcc();
	

Inside the config XML file

The xmlobj program uses the information in the config file to read the data file and create the object tree. It also uses the config file to query the object tree and write an XML file. A part of a sample config file is shown below.

  <TagFactory>
    <Tag name="Bank" type="xml.tag.bank.BankModel">
      <TagAttr name="name" prop="name"/>
      <SubTag name="Customer" iprop="cust" type="xml.tag.bank.CustomerModel"/>
    </Tag>
    ...
  </TagFactory>
    

The description of the elements in the XML file structure is shown below.

Tag/Attribute Type Purpose
TagFactoryTagOutermost tag
TagTagSpecifies how to handle a particular tag in the data file
 - nameAttributeName of the tag in the data file
 - typeAttributeObject type representing the tag
TagTagSpecifies how to handle a particular tag in the data file
 - nameAttributeName of the tag in the data file
 - typeAttributeObject type representing the tag
TagAttrTagSpecifies how to handle the attribute in the tag
 - nameAttributeName of the attribute in the data file
 - propAttributeProperty in the object holding the attribute value
SubTagTagSpecifies how to handle the enclosed tag
 - nameAttributeName of the enclosed the data file
 - propAttributeProperty in the object holding the attribute value
 - ipropAttributeIndexed property in the object holding the attribute value
 - typeAttributeObject type representing the tag

A special attribute text() is used within TagAttr to handle text. An example is shown below.

  <Tag name="Author" type="xml.tag.shape.helper.StringHelper">
    <TagAttr name="text()" prop="text"/>
  </Tag>
    

How does xmlobj work

xmlobj is driven by the XMLFactory class. XMLFactory starts off by loading the config file using the ConfigFile object. The ConfigFile loads the config file into a DOM. The information in the DOM is then used to create the configuration data structure. This structure uses the TagConfig, TagAttrConfig and SubTagConfig classes to store the configuration information.

Once the config file is loaded, XMLFactory loads the data file into a DOM as well. It then processes each node, its attributes, followed by all its children recursively.

The object tree is created by instantiating classes and calling their methods using reflection. Each node is processed by instantiating the corresponding object. Each attribute is processed by calling set methods on the object corresponding to the tag. Subtags are first instantiated and their attributes set before being added to the parent by calling the add methods.

To save the object tree as an XML file, the object is written using the tag specified in the config file. The names of its attributes are obtained from the config file and the values obtained by using the get methods. Subtags are processed by using get methods to get the array of children. Each element in this array is processed similar to an object.

You will note that xmlobj does not use this mechanism to load its own config file. This is because the file has to be loaded before the file can be loaded. The config object tree is created by loading the config file into a DOM and then passing each element as a parameter to the right constructor. This does involve writing each class so that it is XML aware. Also, there is no need to output a config XML file.

A Simple Example

A simple example of xmlobj based on three objects - a bank, accounts and customer reads an XML file, constructs an object tree and writes out another XML file. Get it from the downloads section. The config XML file is shown below.

  <TagFactory>
    <Tag name="Bank" type="xml.tag.bank.BankModel">
      <TagAttr name="name" prop="name"/>
      <SubTag name="Customer" iprop="cust" type="xml.tag.bank.CustomerModel"/>
    </Tag>
    <Tag name="Customer" type="xml.tag.bank.CustomerModel">
      <TagAttr name="name" prop="name"/>
      <TagAttr name="custno" prop="custNo"/>
      <SubTag name="Account" iprop="acc" type="xml.tag.bank.AccountModel"/>
    </Tag>
    <Tag name="Account" type="xml.tag.bank.AccountModel">
      <TagAttr name="type" prop="type"/>
      <TagAttr name="accno" prop="accNo"/>
    </Tag>
  </TagFactory>
    

A Not So Simple Example

This example uses an XML file to describe a geometry figure. It consists of geometric primitives like line, circle, etc. It is used to draw a circuit element and is meant to be a part of a future XML project. The data XML file is loaded using the config XML file and then rendered in a window. The result is shown below. The XML structure is complex enough to need a set of helper objects that represent some XML tags but are used only during building the object tree.


Image generated using xmlobj


Downloads

Download the xmlobj classes and the two examples.

Contributions

Thanks to Craig Edwards for providing the idea of loading a DOM by passing DOM elements to objects. I use this to load the config file itself. He also acted as a sounding board for some of the ideas used here.

Scott Rowell suggested support for text within tags.


Updated on 7 Oct 2003. Feedback to