Saturday 30 May 2009

A Simple Way to Parse XML in Java

Dear All,
This post is now hosted here, which is hopefully somewhat easier on the eyes.
Many thanks

You can do a lot with XML, but often all you want to do is to read a simple file with some basic data in it. The options for doing this, SAX, DOM and JAXB are all relatively verbose and often off-putting. I've devised a simple XML API for parsing simple XML files in Java. Parsing a file such as:

<config>
<title>test</title>
<version
major="1"
minor
="2"/>
<roles>
<role name="admin"/>
<role name="user"/>
</roles>
<users>
<user name="joe" password="pass" role="admin"/>
<user name="harry" password="secret" role="user"/>
</users>
</config>

Can be achieved with the following code:


Xml config = new Xml("config.xml","config");

System.out.println(
"title: "+config.child("title").content());

Xml version
= config.child("version");
System.out.println(
"version: "+version.integer("major")+"."+version.integer("minor"));

for(Xml role:config.child("roles").children("role"))
System.out.println(
"role: name: "+role.string("name"));

for(Xml user:config.child("users").children("user"))
{
System.out.println(
"user: name: "+user.string("name")+
", password: "+user.string("password")+
", role: "+user.string("role"));
}

As you can see, it's nice and simple and allows you to get to the information quickly and without any hassle. The API uses the DOM parser underneath, but attempts to make the data more easily available. All you need is the following class, which you can of course customise however you like:

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Xml
{
private static Element rootElement(String filename, String rootName)
{
FileInputStream fileInputStream
= null;
try
{
fileInputStream
= new FileInputStream(filename);
DocumentBuilderFactory builderFactory
= DocumentBuilderFactory.newInstance();
DocumentBuilder builder
= builderFactory.newDocumentBuilder();
Document document
= builder.parse(fileInputStream);
Element rootElement
= document.getDocumentElement();
if(!rootElement.getNodeName().equals(rootName))
throw new RuntimeException("Could not find root node: "+rootName);
return rootElement;
}
catch(Exception exception)
{
throw new RuntimeException(exception);
}
finally
{
if(fileInputStream!=null)
{
try
{
fileInputStream.close();
}
catch(Exception exception)
{
throw new RuntimeException(exception);
}
}
}
}

public Xml(String filename, String rootName)
{
this(rootElement(filename,rootName));
}

private Xml(Element element)
{
this.name = element.getNodeName();
this.content = element.getTextContent();
NamedNodeMap namedNodeMap
= element.getAttributes();
int n = namedNodeMap.getLength();
for(int i=0;i<n;i++)
{
Node node
= namedNodeMap.item(i);
String name
= node.getNodeName();
addAttribute(name,node.getNodeValue());
}
NodeList nodes
= element.getChildNodes();
n
= nodes.getLength();
for(int i=0;i<n;i++)
{
Node node
= nodes.item(i);
int type = node.getNodeType();
if(type==Node.ELEMENT_NODE) addChild(node.getNodeName(),new Xml((Element)node));
}
}

private void addAttribute(String name, String value)
{
nameAttributes.put(name,value);
}

private void addChild(String name, Xml child)
{
List
<Xml> children = nameChildren.get(name);
if(children==null)
{
children
= new ArrayList<Xml>();
nameChildren.put(name,children);
}
children.add(child);
}

public String name()
{
return name;
}

public String content()
{
return content;
}

public Xml child(String name)
{
List
<Xml> children = children(name);
if(children.size()!=1) throw new RuntimeException("Could not find individual child node: "+name);
return children.get(0);
}

public List<Xml> children(String name)
{
List
<Xml> children = nameChildren.get(name);
return children==null ? new ArrayList<Xml>() : children;
}

public String string(String name)
{
String value
= nameAttributes.get(name);
if(value==null) throw new RuntimeException("Could not find attribute: "+name+", in node: "+this.name);
return value;
}

public int integer(String name)
{
return Integer.parseInt(string(name));
}

private String name;
private String content;
private Map<String,String> nameAttributes = new HashMap<String,String>();
private Map<String,List<Xml>> nameChildren = new HashMap<String,List<Xml>>();
}