Sunday, August 26, 2012

Reading XML files using XPath

Nowadays declarative programming has took a well-deserved place in imperative languages. In Java we can declare what we want in annotations or in XML. The main plus of XML is that we can reconfigure our application in one place and update it without rebuilding.

There a lot of ways to read data from XML. The most common ones are: XPath, XSLT and XQuery.
  • XPath consists of path expressions, conditions and some XPath functions
  • XSLT(Extensible Stylesheet Language (with) Transformation) consists of XPath and some transformations
  • XQuery consists of XPath and a Query Language. This query language is pretty powerful but it does not have strong underlying mathematics like SQL.

In this tutorial we will develop a simple XPath XML file parser. But as you probably know, there are different kind of parsers:
  • DOM(Document Object Model) parsers. DOM API creates a DOM tree in memory for a XML document
  • SAX(Simple API For XML) parsers. They are event driven(as they parse documents they invoke the callback methods)

We will develop DOM parser. For this purpose we will use only standard Java classes without any additional dependencies.
As an example XML file we will use JBoss 5.1 datasource file. Here it is:
<?xml version="1.0" encoding="UTF-8"?>

<datasources>
    <xa-datasource>
        <jndi-name>DevDbDS</jndi-name>
 <!--
        <use-java-context>false</use-java-context>
 -->
 <xa-datasource-class>
  com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
 </xa-datasource-class>
 <xa-datasource-property name="URL">
  URL:jdbc:mysql://localhost:3306/dev_db
 </xa-datasource-property>

        <user-name>root</user-name>
        <password>root</password>
 <!--
        <security-domain>VocdmsDSEncryptedLogon</security-domain>
 -->
    <xa-datasource-property name="characterEncoding">UTF-8</xa-datasource-property>

 <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
        <min-pool-size>5</min-pool-size>
        <max-pool-size>20</max-pool-size>
        <blocking-timeout-millis>30000</blocking-timeout-millis>
        <idle-timeout-minutes>15</idle-timeout-minutes>
        <prefill>true</prefill>
        <transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
        <connection-property name="characterEncoding">UTF-8</connection-property>
        <connection-property name="autoReconnect">true</connection-property>
        <connection-property name="maxReconnects">4</connection-property>
        <connection-property name="initialTimeout">3</connection-property>
        <metadata>
            <type-mapping>mySQL</type-mapping>
        </metadata>
    </xa-datasource>
</datasources>

Next we need to remind ourselves how to write XPath expressions. You can do it here

Now finally we are ready to write the code to get some information from the datasource. In this example let's assume we need user name, password and URL(you may need this data to establish java.sql.Connection). So here is our code:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import java.io.*;

public class XmlParser {
 //Immutable object for data storage
 private static class ConnectionInfo {
  private final String url;
  private final String userName;
  private final String password;
  
  public ConnectionInfo(String url, String userName, String password) {
   this.url = url;
   this.userName = userName;
   this.password = password;
  }
 
  public String getUrl() {
   return url;
  }
  public String getUserName() {
   return userName;
  }
  public String getPassword() {
   return password;
  } 
  @Override
  public String toString() {
   return "URL: " + url +
    "\nuserName: " + userName + 
    "\npassword: " + password;
  }
 }

 public static void main(String[] args) {
  System.out.println(parseDataSource("mysql-ds.xml"));
 }

 public static ConnectionInfo parseDataSource(String fileName) {
  try {   
      DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
      domFactory.setNamespaceAware(true); // never forget this!
      DocumentBuilder builder = domFactory.newDocumentBuilder();
      Document doc = builder.parse(new FileInputStream(fileName));

      XPathFactory factory = XPathFactory.newInstance();
      XPath xpath = factory.newXPath();
      String url = xpath.evaluate("//datasources/xa-datasource/xa-datasource-property[@name='URL']/text()", doc).trim();     
      String userName = xpath.evaluate("//datasources/xa-datasource/user-name/text()", doc).trim();
      String password = xpath.evaluate("//datasources/xa-datasource/password/text()", doc).trim();
      return new ConnectionInfo(url, userName, password);
  } catch (Exception ex) {
      ex.printStackTrace(); 
      return null;
  }
     } 
}