Showing posts with label xpath. Show all posts
Showing posts with label xpath. Show all posts

Tuesday, 6 July 2010

JDOM, XPath and Namespaces

The combination of JDOM and XPath seems to be the source of greatest woe for me of late - following my last blog post I spent (wasted) another day wrestling with these technologies on another sticking point. I'll not claim to be an XPath guru by any stretch (I am learning though!), but it does seem to throw some real curveballs. The latest was down to attempting to extract a List of JDOM sub-Elements from an Element using XPath.selectNodes(element). It continually refused to work, and seemed to be related to the fact that there was a namespace declaration in the root Element. After much head scratching and the help of the wider community, it came out that all that was needed was to wrap the Element in a Document. Can anyone with a deeper knowledge shed a light on why?

Here's the code that doesn't (but to my mind should) work:

Namespace ns = Namespace.getNamespace("srv","http://ess.com/ws/srv");
XPath filterXpression = XPath.newInstance("//srv:ItemCost");
filterXpression.addNamespace(ns);
List nodes = filterXpression.selectNodes(response);

And here's the working code:

Document build = new Document(response);
XPath filterXpression = XPath.newInstance("//srv:ItemCost");
List nodes = filterXpression.selectNodes(build);

You can get the full lowdown in this StackOverflow question.

Wednesday, 30 June 2010

JDOM & XPath Contexts

Second post today - its been a week of minor but frustrating issues so far and I want to publish as many solutions as I can find time for.

This issue had me scratching my head for a good few hours, and there didn't seem to be much solid documentation online to help out. I was using a combination of JDOM 1.0 and XPath to manipulate a short XML document (arriving via a SOAP interface). Here's the basic document structure that was the subject (Names have been changed to protect the innocent):

<srv:Root>
<srv:Name>Overall Name</srv:Name>
<srv:Items>
<srv:Item>
<srv:ItemName>Name 1</srv:ItemName>
<srv:ItemPrice>20</srv:ItemPrice>
</srv:Item>
<srv:Item>
<srv:ItemName>Name 2</srv:ItemName>
<srv:ItemPrice>30</srv:ItemPrice>
</srv:Item>
</srv:Items>
</srv:Root>

The above conforms to the schema set out for the message, everything validates fine. I was using the following to extract the Items, then iterate through each Item and print out the ItemName and the ItemPrice:


XPath itemXpression = XPath.newInstance("//srv:Item");
itemXpression.addNamespace(ns);
XPath itemXpression = XPath.newInstance("//srv:ItemName");
itemnameXpression.addNamespace(ns);
XPath itemXpression = XPath.newInstance("//srv:ItemPrice");
itempriceXpression.addNamespace(ns);

List nodes = itemXpression.selectNodes(document);

// Ensure we have some items to iterate through
if(!nodes.isEmpty()) {

Iterator it = nodes.iterator();

//Init list of Item
List items = new ArrayList();

//Iterate through all items
while(it.hasNext()) {

Element item = (Element)it.next();

Item i = new Item();

// Evaluate XPath expressions for values
String itemname = itemnameXpression.valueOf(item);
String itemprice = itempriceXpression.valueOf(item);

System.out.println(itemname);
System.out.println(itemprice);

}
}
}


However, the code always prints out the Name and Price of the first item twice, rather than the details of the first and then the details of the second.
I tried mixing it up with using pure JDOM to no avail, tried numerous different approaches, and ether got NPEs or that first item repeated twice. In the end, the solution was simply to adjust the XPath search expression from "//srv:Item..." to "/srv:Item...". It seems that despite the fact I'm explicitly passing in an Element containing only one Item (and using an XMLOutputter verifies this), preceeding the expression with // resets the search context to the overall document root. Strange behaviour, but there you go.