Contents
1. Basic Usage
    1.1 Parsing XML
    1.2 Querying with XPath 2.0
        1.2.1 The copyOf and valueOf Functions
        1.2.2 Variations on valueOf
        1.2.3 Selecting the First Value
        1.2.4 Selecting Required Values
        1.2.5 Selecting the First Required Value
    1.3 Working With Namespaces
    1.4 Mapping XML to Java Objects
2. Optimizing Performance
    2.1 XML Recycling
    2.2 Querying with Precompiled XPaths
1. Basic Usage

The XML Toolkit's API is designed to be as simple as possible. The Xml class is the sole class required to parse and query XML documents.

1.1 Parsing XML

There are three ways to parse XML with the XML Toolkit:

    1) as a String
    2) as an InputStream
    3) as a Reader

All formats parse XML when constructing a new Xml instance. The following code samples below illustrate how create a new Xml instance.

1. Create an instance from a String:

try {
Xml xml = new Xml("<Hello>world</Hello>");
}
catch(ParserException e) {
// Oops, XML is not well formed.
}

2. Create an instance from an InputStream:

try {
Xml xml = new Xml(new FileInputStream("test.xml"));
}
catch(FileNotFoundException e) {
// Oops, where's the file?
}
catch(ParserException e) {
// Oops, XML is not well formed.
}

3. Create an instance from a Reader:

try {
Xml xml = new Xml(new FileReader("test.xml"));
}
catch(FileNotFoundException e) {
// Oops, where's the file?
}
catch(ParserException e) {
// Oops, XML is not well formed.
}

1.2 Querying with XPath 2.0

1.2.1 The copyOf and valueOf Functions

The Xml class provides numerous methods for querying parsed XML documents - all of which use XPath 2.0 as the query language. The methods fall into two general groups, copyOf and valueOf. These methods behave exactly like their XSL counter parts, copy-of and value-of.

Xml's copyOf returns an array of Strings representing a copy of the sequence selected by the given XPath expression. For example:

try {
// Print all the additive primary colors.
Xml xml = new Xml(
"<Colors>" +
"<Primary type='additive'>red</Primary>" +
"<Primary type='additive'>green</Primary>" +
"<Primary type='additive'>blue</Primary>" +
"<Primary type='subtractive'>yellow</Primary>" +
"<Primary type='subtractive'>cyan</Primary>" +
"<Primary type='subtractive'>magenta</Primary>" +
"</Colors>");

String s[] = xml.copyOf("/Colors/Primary[@type='additive']");

if(s != null) {
for(int i = 0; i < s.length; i++) {
System.out.println("s[" + i + "] = " + s[i]);
}
}
}
catch(XmlException e) {
// Whoops!
}

Prints

s[0] = <Primary type='additive>red</Primary>
s[1] = <Primary type='additive>green</Primary>
s[2] = <Primary type='additive>blue</Primary>


Xml's valueOf returns an array of Strings representing the values of the sequence selected by the given XPath expression cast as xs:string. For example:

try {
// Print all the additive primary colors.
Xml xml = new Xml(
"<Colors>" +
"<Primary type='additive'>red</Primary>" +
"<Primary type='additive'>green</Primary>" +
"<Primary type='additive'>blue</Primary>" +
"<Primary type='subtractive'>yellow</Primary>" +
"<Primary type='subtractive'>cyan</Primary>" +
"<Primary type='subtractive'>magenta</Primary>" +
"</Colors>");

String s[] = xml.valueOf("/Colors/Primary[@type='additive']");

if(s != null) {
for(int i = 0; i < s.length; i++) {
System.out.println("s[" + i + "] = " + s[i]);
}
}
}
catch(XmlException e) {
// Whoops!
}

Prints

s[0] = red
s[1] = green
s[2] = blue

1.2.2 Variations on valueOf

For programming convenience, Xml provides numerous variations of the valueOf method. These methods are known as the "select" methods because they take the form selectXXX(...) where XXX is either "Boolean", "Double", "Integer", or "String".

selectBoolean returns an array of Booleans representing the values of the sequence selected by the given XPath expression cast as xs:boolean. For example:

try {
// Find the truth.
Xml xml = new Xml(
"<Truths>" +
"<True>true</True>" +
"<False>false</False>" +
"<True>1</True>" +
"<False>0</False>" +
"<True>What was the question?</True>" +
"<False> </False>" +
"</Truths>");

Boolean truths[] = xml.selectBoolean("/Truths/(True|False)/text()");

if(truths != null) {
for(int i = 0; i < truths.length; i++) {
System.out.println("truths[" + i + "] = " + truths[i]);
}
}
}
catch(XmlException e) {
// Whoops!
}

Prints

truths[0] = true
truths[1] = false
truths[2] = true
truths[3] = false
truths[4] = true
truths[5] = false

selectDouble returns an array of doubles representing the values of the sequence selected by the given XPath expression cast as xs:double. For example:

try {
// Find the highest planetary temperature in Fahrenheit.
Xml xml = new Xml(
"<Planets>" +
"<Planet name='Mercury' maxSurfaceTemp='800.33'/>" +
"<Planet name='Venus' maxSurfaceTemp='931.73'/>" +
"<Planet name='Earth' maxSurfaceTemp='136.13'/>" +
"<Planet name='Mars' maxSurfaceTemp='67.73'/>" +
"<Planet name='Jupiter' maxSurfaceTemp='-186.07'/>" +
"<Planet name='Saturn' maxSurfaceTemp='-202.27'/>" +
"<Planet name='Uranus' maxSurfaceTemp='-337.27'/>" +
"<Planet name='Neptune' maxSurfaceTemp='-364.27'/>" +
"<Planet name='Pluto' maxSurfaceTemp='-380.47'/>" +
"</Planets>");

double temps[] =
xml.selectDouble("max(//@maxSurfaceTemp)"); // See section 1.2.5 for a simpler solution.

if(temps != null) {
System.out.println("The highest temperature is " + temps[0] + "F");
}
}
catch(XmlException e) {
// Whoops!
}

Prints

The highest temperature is 931.73F

selectInteger returns an array of ints representing the values of the sequence selected by the given XPath expression cast as xs:integer. For example:

try {
// Calculate the total number of pages for all given novels.
Xml xml = new Xml(
"<GreatLiteraryWorks>" +
"<Novel name='Moby-Dick' pageCount='654'/>" +
"<Novel name='The Republic' pageCount='320'/>" +
"<Novel name='Garfield At Large' pageCount='128'/>" +
"</GreatLiteraryWorks>");

int pageCount[] = xml.selectInteger("/GreatLiteraryWorks/Novel/@pageCount");
int totalPages = 0;

if(pageCount != null) {
for(int i = 0; i < pageCount.length; i++) {
totalPages += pageCount[i];
}
}
System.out.println("total pages = " + totalPages);
}
catch(XmlException e) {
// Whoops!
}

Prints

total pages = 1102

selectString returns an array of Strings representing the values of the sequence selected by the given XPath expression cast as xs:string. Note that this method behaves exactly as the method valueOf described above.

1.2.3 Selecting the First Value

The copyOf, valueOf, and select methods all offer variations that return the first item in the result set in document order. These methods are copyOfFirst, valueOfFirst, and selectFirstXXX where XXX is either "Boolean", "Double", "Integer", or "String" as described in section 1.2.2 above. These methods are offered as another programming convenience as shown below:

try {
// Get the name of the first novel.
Xml xml = new Xml(
"<GreatLiteraryWorks>" +
"<Novel name='Moby-Dick' pageCount='654'/>" +
"<Novel name='The Republic' pageCount='320'/>" +
"<Novel name='Garfield At Large' pageCount='128'/>" +
"</GreatLiteraryWorks>");

String name = xml.valueOfFirst("/GreatLiteraryWorks/Novel/@name");

if(name != null) {
System.out.println("The first book is " + name);
}
}
catch(XmlException e) {
// Whoops!
}

Prints

The first book is Moby-Dick

1.2.4 Selecting Required Values

When querying an XML document for values that may not be null, consider using copyOfRequired, valueOfRequired, or selectRequiredXXX where XXX is either "Boolean", "Double", "Integer", or "String" as described in section 1.2.2 above. These methods throw an EmptyResultException if the given query does not match any nodes. These methods are designed to reduce code complexity.

The more complex code here:

try {
// Display some interesting information about Neptune.
Xml xml = new Xml(
"<Neptune>" +
"<Mass unit='kg'>1.0243E26</Mass>"+
"<MeanTemperature unit='k'>53</MeanTemperature>"+
"<WindSpeed unit='km/h'>2000</WindSpeed>"+
"</Neptune>");

String mass[] = xml.valueOf("/Neptune/Mass");

if(mass == null) {
// Throw an exception. The mass is required.
}

String meanTemp[] = xml.valueOf("/Neptune/MeanTemperature");

if(meanTemp == null) {
// Throw an exception. The mean temperature is required.
}

String windSpeed[] = xml.valueOf("/Neptune/WindSpeed");

if(windSpeed == null) {
// Throw an exception. The wind speed is required.
}

System.out.println(
"Neptune's mass is " + mass[0] +
"kg, mean temperature is " + meanTemp[0] +
"k, and its wind speeds can reach " + windSpeed[0] + "km/h");
}
catch(XmlException e) {
// Whoops!
}

Can be simplified to:

try {
// Display some interesting information about Neptune.
Xml xml = new Xml(
"<Neptune>" +
"<Mass unit='kg'>1.0243E26</Mass>"+
"<MeanTemperature unit='k'>53</MeanTemperature>"+
"<WindSpeed unit='km/h'>2000</WindSpeed>"+
"</Neptune>");

String mass[] = xml.valueOfRequired("/Neptune/Mass");
String meanTemp[] = xml.valueOfRequired("/Neptune/MeanTemperature");
String windSpeed[] = xml.valueOfRequired("/Neptune/WindSpeed");

System.out.println(
"Neptune's mass is " + mass[0] +
"kg, mean temperature is " + meanTemp[0] +
"k, and its wind speeds can reach " + windSpeed[0] + "km/h");
}
catch(EmptyResultException e) {
// D'oh! Missing required data.
}
catch(XmlException e) {
// Whoops!
}

1.2.5 Selecting the First Required Value

The additional functionality described in sections 1.2.3 and 1.2.4 are combined in the set of methods copyOfFirstRequired, valueOfFirstRequired, and selectFirstRequiredXXX where XXX is either "Boolean", "Double", "Integer", or "String" as described in section 1.2.2 above. As the naming indicates, these methods return the first item in the result set in document order, or throw an EmptyResultException if the given query does not match any nodes:

try {
// Find the highest planetary temperature in Fahrenheit.
Xml xml = new Xml(
"<Planets>" +
"<Planet name='Mercury' maxSurfaceTemp='800.33'/>" +
"<Planet name='Venus' maxSurfaceTemp='931.73'/>" +
"<Planet name='Earth' maxSurfaceTemp='136.13'/>" +
"<Planet name='Mars' maxSurfaceTemp='67.73'/>" +
"<Planet name='Jupiter' maxSurfaceTemp='-186.07'/>" +
"<Planet name='Saturn' maxSurfaceTemp='-202.27'/>" +
"<Planet name='Uranus' maxSurfaceTemp='-337.27'/>" +
"<Planet name='Neptune' maxSurfaceTemp='-364.27'/>" +
"<Planet name='Pluto' maxSurfaceTemp='-380.47'/>" +
"</Planets>");

System.out.println(
"The highest temperature is " +
xml.valueOfFirstRequired("max(//@maxSurfaceTemp)") +
"F");
}
catch(EmptyResultException e) {
// D'oh! Missing required data.
}
catch(XmlException e) {
// Whoops!
}

Prints

The highest temperature is 931.73F

1.3 Working with Namespaces

The XML Toolkit library is fully namespace aware. When the query to an XML document contains a namespace, call setNamespace before executing the query. For example:

try {
// Print the secret message sent by the client.
Xml xml = new Xml(
"<soap:Envelope xmlns:soap='http://www.w3.org/2003/05/soap-envelope'>" +
"<soap:Body>" +
"<soatoolkits:Message xmlns:soatoolkits='http://www.soatoolkits.com/contrived/example'>Hello World</soatoolkits:Message>" +
"</soap:Body>" +
"</soap:Envelope>");

xml.setNamespace("s", "http://www.w3.org/2003/05/soap-envelope");
xml.setNamespace("x", "http://www.soatoolkits.com/contrived/example");

String message = xml.valueOfFirstRequired("/s:Envelope/s:Body/x:Message");

System.out.println("The secret message is " + message);
}
catch(EmptyResultException e) {
// D'oh! Missing required data.
}
catch(XmlException e) {
// Whoops!
}

Prints

The secret message is Hello World

It is important to note that the XML Toolkit defines three namespaces by default. The prefix "fn" is bound to "http://www.w3.org/2005/xpath-functions", "xs" is bound to "http://w3.org/2001/XMLSchema", and "xdt" is bound to "http://www.w3.org/2005/xpath-datatypes" (XQuery 1.0 and XPath 2.0 Functions and Operators). It is not necessary to change these bindings, however, it can by done by calling xml.setNamespace("xs", "new URI here"). Please note that if the bindings are changed, any system functions and constructors must be referenced by a user defined namespace mapping:

try {
// Have some fun with namespaces.
Xml xml = new Xml("<x/>");

Boolean b = xml.selectFirstBoolean("xs:boolean('true')"); // ok
System.out.println(b);

xml.setNamespace("newNs", "http://www.w3.org/2001/XMLSchema");
b = xml.selectFirstBoolean("newNs:boolean('true')"); // still ok, user defined
System.out.println(b);

xml.setNamespace("xs", "not a good idea");
b = xml.selectFirstBoolean("xs:boolean('true')"); // oops, xs is no longer set to the correct uri
System.out.println(b);
}
catch(XmlException e) {
// Whoops!
System.out.println(e.getMessage());
}

Prints

true
true
Error [err.XPATH0017]: line 1, column 11-> The function "[not a good idea]boolean" is undefined.

1.4 Mapping XML to Java Objects

The XML Toolkit maps XML to Java objects using the Xml::mapTo function. This function parses the Java object and invokes methods annotated as XmlMethods with values taken from the Xml instance itself. To see how this is done, let’s assume you want to map the following XML to the Java object shown below:

<User id="user123">
<Username>superuser</Username>
<Password>abc123</Password>
<NotificationEmail>superuser@soatoolkits.com</NotificationEmail>
</User>

public class User {
private String notificationEmail;
private String password;
private String userId;
private String username;

public void setNotificationEmail(String notificationEmail) {
this.notificationEmail = notificationEmail;
}
public void setPassword(String password) {
this.password = password;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setUserName(String username) {
this.username = username;
}
public String toString() {
return("User id=" + userId + " username=" + username
+ " password=" + password + " notification email="
+ notificationEmail);
}
}

To map the XML to the Java object:

1. Annotate the object as an XmlObject. This marks the class as one that has methods with parameter values taken from XML. The XmlObject takes an optional array of namespaces that will be used when specifying the XPaths to the XML (more on this in step 3). Since the user XML has a namespace defined above, the namespace must be set here.

import com.soatoolkits.xml.XmlObject;

@XmlObject(namespaces={"soatoolkits='http://soatoolkits.com/schemas/1.0/'"})
public class User {
private String notificationEmail;
private String password;
private String userId;
private String username;

public void setNotificationEmail(String notificationEmail) {
this.notificationEmail = notificationEmail;
}
public void setPassword(String password) {
this.password = password;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setUserName(String username) {
this.username = username;
}
public String toString() {
return("User id=" + userId + " username=" + username
+ " password=" + password + " notification email="
+ notificationEmail);
}
}

2. Annotate the methods as XmlMethods. This marks the methods as having parameter values taken from XML.

import com.soatoolkits.xml.XmlMethod;
import com.soatoolkits.xml.XmlObject;

@XmlObject(namespaces={"soatoolkits='http://soatoolkits.com/schemas/1.0/'"})
public class User {
private String notificationEmail;
private String password;
private String userId;
private String username;

@XmlMethod
public void setNotificationEmail(String notificationEmail) {
this.notificationEmail = notificationEmail;
}
@XmlMethod
public void setPassword(String password) {
this.password = password;
}
@XmlMethod
public void setUserId(String userId) {
this.userId = userId;
}
@XmlMethod
public void setUserName(String username) {
this.username = username;
}
public String toString() {
return("User id=" + userId + " username=" + username
+ " password=" + password + " notification email="
+ notificationEmail);
}
}

3. Annoate the parameters as XmlParams. This marks the parameters as having values taken from XML. This annotation specifies the XPath expression to evaluate against the XML.

import com.soatoolkits.xml.XmlMethod;
import com.soatoolkits.xml.XmlObject;
import com.soatoolkits.xml.XmlParam;

@XmlObject(namespaces={"soatoolkits='http://soatoolkits.com/schemas/1.0/'"})
public class User {
private String notificationEmail;
private String password;
private String userId;
private String username;

@XmlMethod
public void setNotificationEmail(@XmlParam(xpath="/User/NotificationEmail") String notificationEmail) {
this.notificationEmail = notificationEmail;
}
@XmlMethod
public void setPassword(@XmlParam(xpath="/User/Password") String password) {
this.password = password;
}
@XmlMethod
public void setUserId(@XmlParam(xpath="/User/@id") String userId) {
this.userId = userId;
}
@XmlMethod
public void setUserName(@XmlParam(xpath="/User/Username") String username) {
this.username = username;
}
public String toString() {
return("User id=" + userId + " username=" + username
+ " password=" + password + " notification email="
+ notificationEmail);
}
}

4. Invoke the Xml::mapTo function to map the XML to the annotated object.

private static final String USER_XML =
"<soatoolkits:User id=\"user123\" xmlns:soatoolkits=\"http://soatoolkits.com/schemas/1.0/\">\n" +
" <Username>testuser</Username>\n" +
" <Password>abc123</Password>\n" +
" <NotificationEmail>testuser@soatoolkits.com</NotificationEmail>\n" +
"</soatoolkits:User>";

public static void main(final String args[]) {

try {
Xml userXml = new Xml(USER_XML);
User user = userXml.mapTo(User.class);
System.out.println(user);
}
catch(XmlException e) {
// oops!
e.printStackTrace();
}
}

The sourcecode for this sample can be found here.

2. Optimizing Performance

The XML Toolkit library is designed to be the fastest in existence. While the toolkit is faster than any other XML Library, developers can further triple its performance by using the recycling capabilities described in section 2.1. A further performance gain can be realized if queries are be precompiled as described in section 2.2.

2.1 XML Recycling

With existing XML libraries, applications that frequently process XML documents (including SOAP messages) can spend up to two-thirds of their time collecting garbage. Xml's recycle method allows the XML engine to reuse objects created when parsing and querying XML documents. It has been shown that high-throughput applications, such as those used for web services, can triple their performance by calling this method. In addition, any application that processes XML will realize a performance gain by calling recycle:

Xml xml;

try {
// Parse some XML.
xml = new Xml("<x/>");

// Do something profound and amazing with the XML.
...
}
catch(XmlException e) {
// Whoops!
System.out.println(e.getMessage());
}
finally {
// Recycle the Xml instance for an easy performance gain.
Xml.recycle(xml); // xml.recycle(); works too
}

2.2 Querying with Precompiled XPaths

When an application uses XPath queries more than once, it is better to compile them as constants and reuse them. Compilation is easy and can have a significant impact on performance because the system does not need to parse the XPath every time.

Statically defined code:

private static XPath selectWorks;

static {
try {
selectWorks = new XPathParser().parse("/GreatLiteraryWorks/Novel/@name");
}
catch(XmlException e) {
// Eeps! The XPath is not well formed.
}
}

Locally defined code:

Xml xml = null;

try {
// Get the names of the great literary works.
xml = new Xml(
"<GreatLiteraryWorks>" +
"<Novel name='Moby-Dick' pageCount='654'/>" +
"<Novel name='The Republic' pageCount='320'/>" +
"<Novel name='Garfield At Large' pageCount='128'/>" +
"</GreatLiteraryWorks>");

// Use a compiled XPath. This will save processing if the XPath is used
// more than once (i.e. in a server environment).

String works[] = xml.valueOfRequired(selectWorks);

for(int i = 0; i < works.length; i++) {
System.out.println("works[" + i + "] = " + works[i]);
}
}
catch(EmptyResultException e) {
// D'oh! Missing required data.
}
catch(XmlException e) {
// Whoops!
System.out.println(e.getMessage());
}
finally {
// Recycle the Xml instance for an easy performance gain.
Xml.recycle(xml); // xml.recycle(); works too
}

Prints

works[0] = Moby-Dick
works[1] = The Republic
works[2] = Garfield At Large

Navigation
Home
FreeTrialLicense
Buy Now
Download
Documentation
Performance
Contact

Runs on:
Buy New Licenses
XML Toolkit (from $199)
WS Toolkit (from $499)
Enterprise Toolkit (from $999)


Quotes
"This is by far the best way to create web services in java. I'm amazed how easy and fast it is."

- AC, Navio Systems, Inc.


Copyright 2005-2008 Enterprise Mechanics, Inc.