Thursday, November 6, 2008

XPath - Querying xml document


Core Java 5 introduced the package javax.xml.xpath, to enable querying and processing
xml documents in a robust manner.

(Note: it only works with
DOM parser, and this may be a drawback in some cases)

In this tutorial, we are going to perform [select, insert, update and delete] in a given xml sample, using javax.xml.xpath.

Assume the following xml document structure, representing student/course relational model

<training>

<students>

<student id="18972" level="C" name="full name"/>

</students>

<courses>

<course id="01" level="C" name="Math"/>

<course id="02" level="B" name="MC"/>

<course id="03" level="A" name="Matlab"/>

<course id="04" level="A" name="Java"/>

</courses>

<student-course>

<registration courseId="01" studentid="18972"/>

</student-course>

</training>


Let's start coding:

- First, import classes we're going to use:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


- Then, we parse given xml & load it to memory:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse(xmlFile);


- We will instantiate javax.xml.xpath.XPath object to allow manipulating the previous document:

TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer tFormer = tFactory.newTransformer();
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

- Assume we have a new student "Ahmad", passed exam with level "B",
thus we are going to register "Ahmad" to all level "B" courses.

Here, we'll add a new student element into <students> and new registration
element into <student-course>

// First, retrieve students element

NodeList nodes=dom.getElementsByTagName("students");
Node studentNDs=nodes.item(0);

// Add new student

Element child = dom.createElement("student");
String studentId=""+new Date().getTime();
child.setAttribute("name", "Ahmad");
child.setAttribute("id", studentId);
child.setAttribute("level", "B");
studentNDs.appendChild(child);

// Retrieve all courses assigned to level B, using xpath query

String xquery="//course[@level='B']";
XPathExpression expr = xpath.compile(xquery);
Object result = expr.evaluate(dom, XPathConstants.NODESET);
NodeList courseNDs = (NodeList) result;

// Add new resgistartion elements given retrieved courses ids, studentId:

NodeList relation=dom.getElementsByTagName("student-course");

for (int i = 0; i < courseNDs.getLength(); i++) {
Node nd=courseNDs.item(i);
String courseId=nd.getAttributes().getNamedItem("id").getNodeValue();
System.out.println("courseId "+courseId);
Element newRegistration= dom.createElement("registration");
newRegistration.setAttribute("studentid", studentId);
newRegistration.setAttribute("courseId", courseId);
relation.item(0).appendChild(newRegistration);
}

// Save changes to the Filesystem

DOMSource source = new DOMSource(dom);
tFormer.transform(source,new StreamResult(new FileOutputStream(xmlFile)));



- By the end of year, Ahmad's passed level B exams, and will upgrade to level A.
So, we're going to update Student Ahmad status, to level 'A',
and delete all previous courses registration assigned to Ahmad.

// retrieve Student Ahmad entry, given Id.

String xquery="//student[@id='"+id+"']";
XPathExpression expr = xpath.compile(xquery);
Object result = expr.evaluate(dom, XPathConstants.NODESET);
NodeList students = (NodeList) result;
Node studentND=students.item(0);

// modify 'level' attribute

String level="A";
studentND.getAttributes().getNamedItem("level").setNodeValue(level);

// Now, let's delete all registration entries, previously assigned to Ahmad

String xquery="//registration[@studentid='"+id+"']";
XPathExpression expr = xpath.compile(xquery);
Object result = expr.evaluate(dom, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
Node registerND=nodes.item(i);
// delete entry
registerND.getParentNode().removeChild(registerND);
}

// Save changes to filesystem

DOMSource source = new DOMSource(dom);
tFormer.transform(source,new StreamResult(new FileOutputStream(xmlFile)));


2 comments:

Anonymous said...

Asallamo 3alykom

How are you Do3a', hope you doing well:)

I'm working on a web service project, I hope you help me with.

If you agree please contact me at this e-mail becoooool_1@yahoo.com

your sister,
UM_ABDULLAH

دعاء said...

wa 3alikom essalam U.A.

You can post your questions/code snippets here.
Hope i can help you.