How To Create, Get and Set MinXML

How to Create a MinXML object a.k.a. element

In order to MinXML objects you need a class that implements the MinXML interface. The reference implementation comes equipped with the class FlexiMinXML from the package com.steelypip.powerups.minxml. This class provides an all-round, flexible implementation that is quick to access and update and isn't too profligate with memory. It is also a complete implementation, including all the optional methods, so it never throws UnsupportedOperationExceptions.

FlexiMinXML has a simple constructor that takes the element name as an
argument; children and attributes would be added subsequently. Here's how
we might build <foo><bar/></foo>.

MinXML foo = new FlexiMinXML( "foo" ); // create <foo/>
foo.add( new FlexiMinXML( "bar" ) );  // add <bar/> to make <foo><bar/></foo>

Copying a MinXML tree

FlexiMinXML also has two static copying methods that use another element as a
template. One copies the top node but shares all the child-nodes, i.e. a shallow-copy.
The other copies the tree all the way down, ensuring that all the nodes in
the element tree are freshly minted and unshared i.e. a deep copy. Importantly, all
the newly created nodes are instances of FlexiMinXML.

MinXML shallow = FlexiMinXML.shallowCopy( foo );
MinXML deep = FlexiMinXML.deepCopy( foo );

We can tell the difference between these copies because if we modify the child of the original foo we will see that shallow changes but deep does not. Here's an example:

foo.get( 0 ).putAttribute( "left", "right" );        
System.out.println( shallow );
System.out.println( deep );

And this is the output, as expected:

<foo><bar left="right"/></foo>

In addition to these static methods, there are the shallowCopy() and deepCopy() methods that every MinXML object supports. However, these are free to use any implementation that is at least as general as the one being copied.

That means that the copy must implement all the optional methods that the original implemented, although it may implement more methods. So if the original implementation throws a java.lang.UnsupportedOperationException for a method, then and only then the copy may also throw the same exception for the that method.

MinXML shallow2 = shallow.shallowCopy();
MinXML deep2 = table.deepCopy();

Using the Name of an Element

A MinXML element has a name string that can be fetched with the method getName(). The usual purpose of the name is to categorise the type of element, which can be summed up as constraining what the attributes may be and what the types of the children may be.

The name may be changed to a non-null value using the optional method setName( String new_name ), which will throw UnsupportedOperationException if the implementation does not permit it.

String name = foo.getName();
foo.setName( "bar" );

Using the Attributes of an Element

The attributes of an element work much like a key-value map of type Map<String, String>. You can access and update the individual values associated with keys using getAttribute and the optional method setAttribute. Because a particular key might be missing, getAttribute may return null. Alternatively it can be given an additional argument to return instead of null.

foo.putAttribute( "", "Stephen" );
System.out.println( "Hello, " + foo.getAttribute( "", "World" ) + "!" );

A common operation on elements is to check whether or not they have an attribute and sometimes whether it has a particular value:

boolean one_or_more_attributes = foo.hasAttribute();
boolean has_TERM_attribute = foo.hasAttribute( "TERM" );
boolean TERM_is_vt100 = foo.hasAttribute( "TERM", "vt100" );

You can also update the attributes in bulk using putAllAttributes, which takes a either a Map<String,String> or another element, which may even be the same element.

foo.putAllAttributes( System.getenv() );
bar.putAllAttributes( foo );

And you can remove all the attributes from an element by using clear.


Working with all the attributes of an element gives rise to the question of what happens when the attributes are updated. For example, when you get the set of keys from an element, is it safe to iterate across the set while updating the element's attributes? Because of these kinds of questions, the MinXML interface provides two versions of each method for working with all the attributes of an element.

The most straightforward approach is to get the attributes as if they were a map. This can be done with getAttributes if you want a new, independent map. But if you want a map that shares with the element then you should use asMap.

Map< String, String > new_map = foo.getAttributes();
Map< String, String > sharing_map = foo.asMap();

Similarly there are methods for getting the keys of an element, using keys or asMapKeys, and the entries of an element, using entries and entriesAsMap. Use keys and entries if you want the results to be independent of the original element. Use asMapKeys and asMapEntries if you want the results to share updates with the original.

Using the Children of an Element

MinXML implement lists, where the children of the element are the members of the list. So, you can get and set members, iterate using iterator and for-each loops, find members, take sublists and so on.

MinXML first = foo.get( 0 );          // Get the first child
foo.set( 0, new FlexiXML( "bar" ) );  // Set the first child to <bar/>
for ( MinXML child : foo ) { 
    System.out.println( child );      // Print the children of foo.

If you want to iterate over the all the descendants of an element, then you should read the next section on tree "walking".