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