XPath processing with namespaces does not work. Please review my code..

Dear all,

I am working on with the IMS Group's Enterprise XML standard (http://www.imsproject.org/enterprise/) and need to retrieve member information using XPath.

After significant fault finding, I have narrowed down the problem to the XML file containing multiple namespaces in the root element. I have checked out some forum postings here and on Google (programmer's best friend) for possible solutions... namely the implementation of the my own NamespaceContext object. I have implemented and yet I am still not able to retrieve the data I want.

Could someone advise where I am going wrong?

When I remove the namespaces from the root element I can find out the number of member elements in the XML (just for testing atm). I could write a regular expression to remove the namespaces from my XML, but that's just dodgy..

Here's a sample XML:

<enterprise xmlns="http://www.imsproject.org/xsd/imsep_rootv1p01" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:webct="http://www.webct.com/IMS">

<properties>...</properties>

<group>...</group>

<membership>

<member>...</member>

</membership>

</enterprise>

My implementation of the a NamespaceContext object:

public class IMSProjectNamespaceContext implements NamespaceContext {

public String getNamespaceURI(String prefix) {

if(prefix == null) throw new NullPointerException("Null prefix");

else if("xsi".equals(prefix)) return "http://www.w3.org/2001/XMLSchema-instance";

else if("webct".equals(prefix)) return "http://www.webct.com/IMS";

else if("xml".equals(prefix)) return XMLConstants.XML_NS_URI;

return XMLConstants.NULL_NS_URI;

}// end of overriding getNamespaceURI method

// This method is not necessary for XPath processing

public String getPrefix(String uri) {

throw new UnsupportedOperationException();

}// end of overriding getPrefix method

// This method is not necessary for XPath processing

public java.util.Iterator getPrefixes(String uri) {

throw new UnsupportedOperationException();

}// end of overriding getPrefixes method

}// end of IMSProjectNamespaceContext class

My method attempting to use XPath... but not successful (it should print out the number of member elements if XPath works correctly with my XML file, but only prints out 0 each time):

private void getSectionMembers(String dataXML) throws Exception {

try {

InputSource inputSource = new InputSource(new StringReader(dataXML));

XPath xPath = XPathFactory.newInstance().newXPath();

xPath.setNamespaceContext(new IMSProjectNamespaceContext());

XPathExpression xPathExpression = xPath.compile("/enterprise/membership/member");

NodeList nodes = (NodeList)xPathExpression.evaluate(inputSource, XPathConstants.NODESET);

System.out.println(nodes.getLength()); // Debugging

} catch(Exception ex) {

ex.printStackTrace();

throw new Exception("XPath Querying Failed.");

}

}// end of xPathQuery method

[3167 byte] By [Ziga] at [2007-11-27 8:16:09]
# 1

Develop an implementation class of the NamespaceContext interface with the provision to set more than one prefixes for a uri.

import javax.xml.namespace.NamespaceContext;

import java.util.Iterator;

import java.util.Map;

import java.util.HashMap;

import java.util.Set;

import java.util.ArrayList;

import java.util.List;

public class MyNamespaceContext implements NamespaceContext {

private Map map;

public MyNamespaceContext() {

map = new HashMap();

}

public void setNamespace(String prefix, String namespaceURI) {

map.put(prefix, namespaceURI);

}

public String getNamespaceURI(String prefix) {

return (String) map.get(prefix);

}

public String getPrefix(String namespaceURI) {

Set keys = map.keySet();

for (Iterator iterator = keys.iterator(); iterator.hasNext();)

{

String prefix = (String) iterator.next();

String uri = (String) map.get(prefix);

if (uri.equals(namespaceURI)) return prefix;

}

return null;

}

public Iterator getPrefixes(String namespaceURI) {

List prefixes = new ArrayList();

Set keys = map.keySet();

for (Iterator iterator = keys.iterator(); iterator.hasNext();)

{

String prefix = (String) iterator.next();

String uri = (String) map.get(prefix);

if (uri.equals(namespaceURI)) prefixes.add(prefix);

}

return prefixes.iterator();

}

}

dvohra09a at 2007-7-12 20:01:01 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 2

Dear dvohra09,

Thank you for your quick reply. Much appreciated. I have implemented a NamespaceContext object that is able to set more than one prefix for a URI as you suggested.

It works when I remove the first namespace that is generated in the IMSProject Enterprise XML:

xmlns="http://www.imsproject.org/xsd/imsep_rootv1p01"

When I hack the XML and set a prefix for this ('default') namespace it works... For example:

xmlns:ims="http://www.imsproject.org/xsd/imsep_rootv1p01"

Is the NamespaceContext class supposed to handle for so called "default" namespaces (without needing to touch the XML)?

My method is performing the XPath query is below:

private void getSectionMembers(String dataXML) throws Exception {

try {

InputSource inputSource = new InputSource(new StringReader(dataXML));

IMSProjectNamespaceContext imsproject = new IMSProjectNamespaceContext();

imsproject.setNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");

imsproject.setNamespace("webct", "http://www.webct.com/IMS");

imsproject.setNamespace("ims", "http://www.imsproject.org/xsd/imsep_rootv1p01lme");

XPath xPath = XPathFactory.newInstance().newXPath();

xPath.setNamespaceContext(imsproject);

XPathExpression xPathExpression = xPath.compile("//ims:enterprise/@xmlns/text()");

NodeList nodes = (NodeList)xPathExpression.evaluate(inputSource, XPathConstants.NODESET);

System.out.println(nodes.getLength()); // Debugging

System.out.println(nodes.item(0).getNodeValue());// Debugging

} catch(Exception ex) {

ex.printStackTrace();

throw new Exception("XPath Querying Failed.");

}

}// end of xPathQuery method

Regards

Zig

Ziga at 2007-7-12 20:01:01 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 3

Well when you remove the first namespace element you essentially remove the default namespace. I am not sure about sun java libraries that you are using but there is a similar issue with dom4j libraries. Read this to get more info on that and maybe this enlightens you about your problem.

http://www.edankert.com/defaultnamespaces.html#What_s_the_Problem_

Alternatively you may need to add localname() XPATH function to your Xpath query, this will work to return your element than. But you don't know than what namespace it belogns to but if there is only one than you are fine I guess.

Elementala at 2007-7-12 20:01:01 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...
# 4
Thanks Elemental,Sorry for the delay in reply. I have been away for a couple of days. I have got my XPath query working with namespaces now using JDOM. The resource link you provided helped significantly.Cheers.Zig
Ziga at 2007-7-12 20:01:01 > top of Java-index,Enterprise & Remote Computing,Enterprise Technologies...