Use Xalan’s extension functions natively in jOOX

jOOX - a jQuery port to Java jOOX aims at increased ease of use when dealing with Java’s rather complex XML API’s. One example of such a complex API is Xalan, which has a lot of nice functionality, such as its extension namespaces. When you use Xalan, you may have heard of those extensions as documented here:

http://exslt.org

These extensions can typically be used in XSLT. An example is the math:max function:

<!-- Source -->
<values>
   <value>7</value>
   <value>11</value>
   <value>8</value>
   <value>4</value>
</values>

<!-- Stylesheet -->
<xsl:template match="values">
   <result>
      <xsl:text>Maximum: </xsl:text>
      <xsl:value-of select="math:max(value)" />
   </result>
</xsl:template>

<!-- Result -->
<result>Maximum: 11</result>

But in fact, math:max can be used in any type of XPath expression, also the ones that are directly created in Java. Here’s how you can do this:

Document document = // ... this is the DOM document

// Create an XPath object
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

// Initialise Xalan extensions on the XPath object
xpath.setNamespaceContext(
  new org.apache.xalan.extensions.ExtensionNamespaceContext());
xpath.setXPathFunctionResolver(
  new org.apache.xalan.extensions.XPathFunctionResolverImpl());

// Evaluate an expression using an extension function
XPathExpression expression = xpath.compile(
  "//value[number(.) = math:max(//value)]");
NodeList result = (NodeList) expression.evaluate(
  document, XPathConstants.NODESET);

// Iterate over results
for (int i = 0; i < result.getLength(); i++) {
  System.out.println(result.item(i).getTextContent());
}

jOOX is much more convenient

The above is pretty verbose. With jOOX, you can do exactly the same, but with a lot less code:

Document document = // ... this is the DOM document

// jOOX's xpath method already supports Xalan extensions
for (Match value : $(document).xpath(
    "//value[number(.) = math:max(//value)]").each()) {
  System.out.println(value.text());
}

jOOX and JAXB

jOOX - a jQuery port to Java jOOX has been awfully quiet lately due to increased development focus in jOOQ. Nevertheless, the jOOX feature roadmap is full of promising new features. Unlike its inspiration jquery, jOOX is positioning itself in the Java world, where many XML API’s already exist. One of the most important XML APIs in Java is JAXB, a very simple means of mapping XML to Java through annotations (see also my blog stream on the subject of Annotatiomania™).

Let’s have a look at this small XML document

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer id="13">
    <age>30</age>
    <name>Lukas</name>
</customer>

Typically, we would write a Java class like this to map to the above XML document:

@XmlRootElement
public class Customer {
    String name;
    int age;
    int id;

    public String getName() {
        return name;
    }

    @XmlElement
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @XmlElement
    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    @XmlAttribute
    public void setId(int id) {
        this.id = id;
    }
}

And then, we would marshal / unmarshal the above using the following code snippet:

JAXB.marshal(new Customer(), System.out);
Customer c = JAXB.unmarshal(xml, Customer.class);

JAXB and jOOX

This is very neat and convenient. But it gets even better when JAXB is used along with jOOX. Have a look at the following piece of code:

// Use the $ method to wrap a JAXB-annotated object:
$(new Customer());

// Navigate to customer elements in XML:
String id   = $(new Customer()).id();
String name = $(new Customer()).find("name").text();

// Modify the XML structure, and unmarshal it again into 
// a JAXB-annotated object:
Match match = $(new Customer());
match.find("name").text("Peter");
Customer modified = match.unmarshalOne(Customer.class);

Check back soon on jOOX for new features!