Tuesday 18 December 2012

Basics - XML

Here's the next entry in my Java basics series. Earlier we looked at how to read a text file. Now we'll look at some simple XML processing. XML is pretty ubiquitous; at some point you're going to run in to it. This post will show how to read XML and it parse into a Document. We'll extract data from the Document and then create a new one.

There are many XML libraries around that help with XML processing, such as jdom or dom4j, but the standard JDK comes with everything you need. Lets get started!

Getting a Document

The first step in processing XML is to create a Document from the raw XML. The Document is the central class for XML processing. We use a DocumentBuilder to parse XML into a Document. The builder has several overloaded parse methods allowing you to create a Document from a File, InputStream, InputSource or a URI. Here we are using a file.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
File file = new File("employees.xml");
Document doc = builder.parse(file);

In this example we're going to work with a basic XML file, employees;

<employees>
  <employee id="1">
    <name>Fred</name>
    <age>20</age>
    <department>Sales</department>
  </employee>
  <employee id="2">
    <name>Bob</name>
    <age>30</age>
    <department>Sales</department>
  </employee>
  <employee id="3">
    <name>Jim</name>
    <age>23</age>
    <department>Marketing</department>
  </employee>
</employees>

Working with the Document


Now we have our Document, we can extract data from it using XPath. XPath is a powerful query language for selecting nodes from an XML Document. Lets find the name of the employee with an ID of 1.

XPathFactory xpFactory = XPathFactory.newInstance();
XPath xpath = xpFactory.newXPath();
String qry = "/employees/employee[@id = '1']/name";
String name = (String)xpath.evaluate(qry, doc, XPathConstants.STRING);
System.out.println(name);

When this code is run, it will output Fred, which is the name of the employee with an id attribute of '1'. Now lets get all the employee nodes in the sales department.

xpath.reset();
qry = "/employees/employee[department = 'Sales']";
NodeList employees = (NodeList)xpath.evaluate(qry, doc, XPathConstants.NODESET);
System.out.println("Employees in Sales department;");
for (int i=0; i<employees.getLength(); i++) {
  Element employee = (Element)employees.item(i);
  name = employee.getElementsByTagName("name").item(0).getTextContent();
  String age = employee.getElementsByTagName("age").item(0).getTextContent();
  String id = employee.getAttribute("id");
  System.out.format("%2$s - %1$s age %3$s\n", name, id, age);
}

In the above example we get a NodeList and loop over it to create a report of the employees in the Sales department;

Employees in Sales department;
1 - Fred age 20
2 - Bob age 30

Creating a new Document

Now lets look at creating a new Document from scratch. We use a DocumentBuilder to create a new empty Document, then we create some elements and build up the DOM tree.

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();

Element order = doc.createElement("order");
order.setAttribute("number", "1");
doc.appendChild(order);

Element total = doc.createElement("total");
total.setTextContent("10.00");
order.appendChild(total);

Element status = doc.createElement("status");
status.setTextContent("dispatched");
order.appendChild(status);

Now we have a small order Document! In order to see what a Document looks like we can output the Document to the console using a Transformer.

TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(order);
StreamResult result = new StreamResult(System.out);
transformer.transform(source, result);

which gives us the output;

<?xml version="1.0" encoding="UTF-8"?>
<order number="1">
<total>10.00</total>
<status>dispatched</status>
</order>

Tuesday 11 December 2012

Basics - reading a file

I quite often see new developers struggling with the basics in Java. I guess everyone has to begin somewhere, so here's the first in a series of posts demonstrating some of the basics the you might need when you're starting out.

This post deals with reading a text file line by line. First of all, lets create a text file called names.txt;

Bob
Fred
Jim

Now were going to write a class to read the file. This revolves around the BufferedReader class.

package querky.blog.basics;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadAFile {

  public static void main(String[] args) {
 
    String fileName = "names.txt";
    BufferedReader reader = null;
 
    try {
      System.out.println("Reading " + fileName);
      reader = new BufferedReader(new FileReader(fileName));
      String line = null;
      while ((line = reader.readLine()) != null) {
        System.out.println(line);
      }
    } catch (IOException ioe) {
      ioe.printStackTrace();
    } finally {
      if (reader != null) {
        System.out.println("Closing " + fileName);
        try {
          reader.close();
        } catch (IOException ioe) {
          ioe.printStackTrace();
        }
      }
    }
  }
}

You can see that we create a BufferedReader and then call it's readLine() method in a loop until it returns null. This gives us each line in the file in turn. Notice that we also close the reader in a finally block. This is important as we want to release the resources used.

When run, the program should output this;

Reading names.txt
Bob
Fred
Jim
Closing names.txt

Common problems

The only problem you're likely to encounter is a FileNotFoundException - that is, your program can't find the input file it is supposed to be reading. The error will look something like this;

Reading names.txt
java.io.FileNotFoundException: names.txt (The system cannot find the file specified)
   at java.io.FileInputStream.open(Native Method)
   at java.io.FileInputStream.<init>(FileInputStream.java:106)
   at java.io.FileInputStream.<init>(FileInputStream.java:66)
   at java.io.FileReader.<init>(FileReader.java:41)
   at querky.blog.basics.ReadAFile.main(ReadAFile.java:16)

First, check you have a names.txt file in the right location. What's the right location? As the file name is specified as simply;

String fileName = "names.txt";

Then our program will look for the file in the current working directory, that is to say the directory where the program was launched from. If you don't know what that is, then you can easily find out with a debugging statement;

System.out.println(new File(fileName).getCanonicalPath());

Here we're using the getCanonicalPath() method to show exactly where on the file system Java is looking. If you're getting a FileNotFoundException odds are that the file isn't there or Java doesn't have permission to read it.