今天在一个拥有命名空间设定的xml文件上用xpath查节点,发现怎么写xpath语句都查不到对应的东西,总是返回null.仔细google一下,发现原来是命名空间的问题。
对于一个没有命名空间的xml文件,比如:
no namespace
<?xml version="1.0"?>
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>project</artifactId>
<name>example</name>
<url>http://maven.apache.org</url>
<version>1.1.0</version>
</project>
如下的xpath语句可以顺利执行,并得到预想的结果。
no namespace xpath
doc.selectSingleNode("version") // 获得verion节点
root.selectSingleNode("//version") // 获得version 节点
然而,加入命名空间后,这两个xpath的返回值都变成null。
namespace xml
<?xml version="1.0"?>
<project
xmlns="url1" xmlns:xsi="url2">
<xsi:body>1234567</xsi:body>
<modelVersion>4.0.0</modelVersion>
<artifactId>project</artifactId>
<name>example</name>
<url>http://maven.apache.org</url>
<version>1.1.0</version>
</project>
仔细分析,发现新的xml文件中,有两组命名空间,分别是前缀为 ”xsi”的空间(url=http://www.w3.org/2001/XMLSchema-instance)和无前缀的默认空间(url=http://maven.apache.org/POM/4.0.0)。 因此,当调用xpath语句时,候选节点自动根据命名空间进行了过滤,即:
当xpath中未指定命名空间,如”//version” |
搜索范围是所有不属于任何命名空间的节点。但是在该例中由于存在默认命名空间,所以这里什么都找不到。 |
当xpath中指定命名空间前缀时,如”xsi” |
搜索范围是所有隶属于xsi命名空间的节点,也就是以xsi: 开头的节点及其子节点。 |
因此,为了顺利查找得到隶属于默认无前缀命名空间的version节点,我们需要:
- 将默认无前缀命名空间改为有前缀的命名空间。
- 将新的前缀名加入到查找所用到的xpath语句中。
代码如下:
java
private String url;
private String PREFIX = "ns";
private HashMap<String, String> nsMap;
//main part
//input : xpathString
Document doc = reader.read(file);
root = doc.getRootElement();
url = root.getNamespaceURI(); // 获得默认空间的url
nsMap = new HashMap<String, String>();
nsMap.put(PREFIX, url);
} else {
nsMap = null;
}
XPath xpath = root.createXPath(getXPathString(xpathString));
xpath.setNamespaceURIs(nsMap);
Element result = (Element) xpath.selectSingleNode(base);
// referred method
protected String getXPathString(final String xpathString) {
if (url.equals("")) {
return xpathString;
} else {
StringBuffer buf = new StringBuffer(xpathString);
int length = 0;
while (buf.charAt(length) == '/') {
length++;
}
buf.insert(length, PREFIX + ":");
return buf.toString();
}
没有评论:
发表评论