You ain't never seen an XML trick like this

A new approach to XML – Part 1: Data projection with XMLBeam

SvenEwald
beams

Thought you knew everything there was to know about XML? In the first part of a series of articles, Sven Ewald might surprise you as he introduces the Java library XMLBeam.

The industry standard XML is so pervasive that it may surprise you to discover something new. The Java library XMLBeam implements a new approach in dealing with DOM trees. Instead of coupling the Java world with XML via data binding, it allows you to “project” objects from one world into the other.

Data binding frameworks link XML documents with Java objects by reproducing the structure of the XML entities in an object hierarchy. These object hierarchies are often quite bulky and can cause difficulties designing a Java API. In order to achieve a larger separation between the XML structure and Java code, XMLBeam goes another way: The data remains in the DOM tree and can be used directly in terms of Java types. To this end, a projection interface is defined with projection methods. These determine how data is read from the document or written into the document. At the same time, instances of these projection interfaces can be used like plain old Java objects. They are the link between Java code and DOM tree.

XPath expressions, which are associated with the projection methods via annotations, specify the access to the document. This allows to combine several elements or attributes to one single Java object, or to divide one item on multiple Java objects. Instead of following the structure of the XML document, a user defined view of the associated DOM tree can be defined. This view can provide both, reading and writing methods.

The MSN Weather Service (tutorial in [4 ]) delivers weather information in XML and is used to give data in the first demonstration:

Example 1: Weather data (extract)

<weatherdata>
<weather ... degreetype="F"
lat="50.5520210266113" lon="6.24060010910034"
searchlocation="Monschau, Stadt Aachen, NW, Germany" ... >
<current ... skytext="Clear" temperature="46"/>
</weather>
</weatherdata>

We define a projection interface to readout weather description, location, and temperature. The XML attribute “searchlocation” is to become converted as a Java String, the temperature as an integer. The return value of our projection methods is defined, accordingly. Via the annotation @XBRead, an XPath expression selecting the desired elements or attributes is assigned to each method. Although the name of the projection methods can be chosen freely, it is advisable to keep the Javabean specification up and therefore use names according to getter methods.

Example 1: Projection Interface

Example 1: Projection Interface
public interface WeatherData {
        @XBRead("/weatherdata/weather/@searchlocation")
        String getLocation();

        @XBRead("/weatherdata/weather/current/@temperature")
        int getTemperature();

        @XBRead("/weatherdata/weather/@degreetype")
        String getDegreeType();

        @XBRead("/weatherdata/weather/current/@skytext")
        String getSkytext();

        interface Location {
                @XBRead("@lon")
                double getLongitude();
                
                @XBRead("@lat")
                double getLatitude();
        }
        @XBRead("/weatherdata/weather")
        Location getCoordinates();
        }

What is the inner interface “Location” all about? Whereas the projection methods relate to the entire document in the WeatherData interface, Location is a sub-projection onto a single XML element serving the method GetCoordinates() as a return value. This enables the geo-coordinates to bundle in one single object, even if there is no element “location“ in the document. Note that the XPath expressions are evaluated relative to an element here and not on the whole document. It is dependent on the expression of the projection method to which element a sub-projection is attached.

Example 1: Example Code

private void printWeatherData(String location) throws IOException {
        final String BaseURL = "http://weather.service.msn.com/find.aspx?                                                outputview=search&weasearchstr=";
        WeatherData weatherData = new XBProjector().io().url(BaseURL + location).read(WeatherData.class);
System.out.println("The weather in " + weatherData.getLocation() + ":");
System.out.println(weatherData.getSkytext());
System.out.println("Temperature: " + weatherData.getTemperature() + "°"
+ weatherData.getDegreeType());

Location coordinates = weatherData.getCoordinates();
System.out.println("The place is located at " + coordinates.getLatitude() +     "," + coordinates.getLongitude());
        }

An instance of the class XBProjektor serves as an entry point for creating, reading, or writing projections. For our example the default settings of the projector satisfy, so a call of the default constructor will do.

The input and output operations are grouped thematically behind the method „io()“. So „io(). stream()“ leads to the operations with InputStream and OutputStream, respectively and „io().file ()“ to file operations. In this way, the code completition often saves you having to look into the documentation. Our example uses io (). Url (…). Read (…) to execute a HTTP GET request. The response is projected to the interface WeatherData.

Immediately after that, the projection weatherData can be used like an ordinary plain old Java object. It has even implicit equals() and hashcode() methods, which allow an intuitive use of projections in Java Collections. The operation of creating the projection hasn’t moved any data yet. It’s just a view to the Data in the DOM-Tree. When accessing the weather description or the temperature, values selected by the XPath are converted automatically into their desired Java return type. If the XPath expression finds no match in the document, null is returned.

The sub projection Location is generated via the method GetCoordinates(). In contrast to other methods, the return type of this method is an interface and the associated XPath expression does not select an attribute but an element. Since projections are just views on the DOM tree, they may contain other projections or even overlap with other projections.

If the Xpath selects more than one attribute or element, but the method has a single value return type, only the first one is returned.

Example 2: Data

<example2>
        <elment>
                <subelement> 100 </subelement>
                <subelement> 200 </subelement>
                <subelement> 300 </subelement>
                <subelement> 400 </subelement>
        </element>
</example2>

The second example shows how to read multiple values.
Example 2: Projection interface
public interface Example2 {
@XBRead(“//subelement”)
int[] getAllSubelementValues();

@XBRead(“//subelement”s)
List<Integer> getAllSubElemetValuesAsList();
}

The return type of the method getAllSubelementValues() specifies that all selected values of the elements „subelement“ will be returned in an array of integers. Ignoring the document structure, the values will be read across all elements. If you wish to get a Java List instead of an array, just specify that as the return type. The projection method will create a LinkedList now, filled with all values of the selected elements. Be mindful that you need to specify the generic type for the List. Without, the projector does not know how to fill it.

In the second part of this series, we will learn about bidirectional, ie reading and writing projections as well as dynamic projections defining their XPath expression during runtime.

About the author

Sven Ewald (Twitter handle:@Cfx) is the author of the library XMLBeam (@XMLBeam). He has been creating Java solutions for 15 years with no end in sight. Currently he works in the field of domain-specific languages for the automotive industry.

Links & Literature

[1] http://xmlbeam.org

[2] http://www.w3schools.com/xpath/default.asp

[3] https://github.com/SvenEwald/xmlbeam

[4] http://msdn.microsoft.com/en-us/library/office/jj228383.aspx

Author
Comments
comments powered by Disqus