HierarchicalMap Tutorial - Intermediate Level
HierarchicalMap is an interface. So anyone can implement it. This tutorial is based on BasicHierarchicalmap, a reference implementation of HierarchicalMap.
Following subjects are covered in this tutorial:
Intermediate Level6. Working with Stream
HierarchicalMap can be streamed in two ways: Binary format and XML format. The package org.dhmp.io contains classes that help stream handling:
Writing and reading a HierarchicalMap is straight forward:
For serialization, it is preferable to use Java's native mechanism instead of using above class. Also note that this class uses internal cache to avoid infinite loop when the map contains recursive reference. Therefore, try to renew the class from time to time and avoid keeping it opened for long period of time such in server usage.
There are also three other method to read/write a HierarchicalMap: readMap(start), readMap(start, stop) and readMapUntil(stop).
The MapInputStream traverses the structure during read operation. There is an imaginary cursor that lies between nodes. When "start" key is specified, actual read operation begins only when this imaginary cursor position matches the "start" key. In the similar way, the "stop" key tells to the MapInputStream to finish reading just before the matching "stop" key.
The next figure shows what happens when the operation readMap("A","B/F") is issued on the following structure:
Notice that reading starts right after imaginary cursor position (node "A"). And finishes just before the imaginary cursor position (node "F"). Be aware that "stop" key is relative from "start" key and only nodes are considered for matching these keys.
The lines above will produce the following output:
readMap("A","B/F"): <B><C><D>value D</D><E>value E</E></C></B> readMap(): <F><G>value G</G><H>value H</H></F> readMap(): <I><J><K>value K</K></J></I>
As mentioned earlier, only nodes are considered. Therefore calling readMap("A","B/C/E") instead of readMap("A","B/F"), will result in reading entire map, because there is no "stop" key node ("B/C/E" is a leaf) in the structure.7. Working with XML
Working with XML can be done in very similar way. This implementation of HierarchicalMap uses Apache's Xerces project for XML handling.
XML is a powerful data structure with many concepts like documents, elements, attributes, namespaces and so on. The HierarchicalMap has only the nodes (another HierarchicalMap) and leaves (any other Objects). Therefore, some simplification must be done to be able to map XML into HierarchicalMap.
XML file below will be used for following examples.
Create a text file containing above XML. Name it "sample.xml" and put it in "/temp" directory for further examples.
Reading XML file into HierarchicalMap is similar to reading a binary stream.
The result of above code is a long line of string like following:
<Root_Element><Root_attribute>roots attribute ...
Copy the result into text file and save it as "result.xml". Then open it with an browser or any other XML aware editor. The first portion will be like the following:
- <Root_Element> <Root_attribute>roots attribute</Root_attribute> <_Element_Value>roots text</_Element_Value> <Element /> <ElementWithValue>elements value</ElementWithValue> - <ElementWithChild> - <Element> <Attrib1>a1</Attrib1> <Attrib2>a2</Attrib2> </Element> - <Element> <Attrib3>a3</Attrib3> <Attrib4>a4</Attrib4> <_Element_Value>e0</_Element_Value> </Element> ...
The result is slightly different from original XML file:
Usually this simplification does not affect the interpretation of the data contained in the XML. But sometimes it is necessary to preserve the XML structure. The only way to do that using HierarchicalMap is by wrapping each type of leaf with distinct classes. There are some wrapper classes for this purpose: org.dhmp.util.xml.Attribute for attributes and org.dhmp.util.xml.ElementValue for element's values.
XMLMapInputStream has an option to automatically wraps nodes using these classes.
Note the line "xin.init("<Param pure='true'/>")". This instructs the XMLMapInputStream to preserve the XML structure wrapping the leaf using wrapper classes when required. The XMLMapOutputStream recognizes the wrapper classes and converts to corresponding XML syntax. Open the "sample.xml" and "result2.xml" using browser and verify that there is no difference between them.
Another usage of XMLMapInputStream can be like following:
The above snippet of code is used in XMLMapInputStream initialization to change its behavior.
Continue to Intermediate Level - Handling Large XML