我想操作具有默认命名空间但没有前缀的xml doc。有没有办法在没有命名空间uri的情况下使用xpath,就好像没有命名空间一样? 我相信如果我们将documentBuilderFactory的namespaceAware属性设置为false应该是可能的。但就我而言,它无法正常工作 我的理解是不正确的还是我在代码中犯了一些错误?
这是我的代码:
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(false);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("//author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
这是我的xml:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.mydomain.com/schema">
<author>
<book title="t1"/>
<book title="t2"/>
</author>
</root>
答案 0 :(得分:22)
使用默认命名空间(无前缀)的文档的XPath处理与使用前缀的文档的XPath处理相同:
对于命名空间限定文档,您可以在执行XPath时使用NamespaceContext。您需要在XPath中为片段添加前缀以匹配NamespaceContext。您使用的前缀不需要与文档中使用的前缀匹配。
以下是您的代码的外观:
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext());
NodeList nl = (NodeList) xPath.evaluate("/ns:root/ns:author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class MyNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if("ns".equals(prefix)) {
return "http://www.mydomain.com/schema";
}
return null;
}
public String getPrefix(String namespaceURI) {
return null;
}
public Iterator getPrefixes(String namespaceURI) {
return null;
}
}
}
注意: 我还使用了Dennis建议的更正后的XPath。
以下似乎也有效,并且更接近您原来的问题:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
public class Demo {
public static void main(String[] args) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document dDoc = builder.parse("E:/test.xml");
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList) xPath.evaluate("/root/author", dDoc, XPathConstants.NODESET);
System.out.println(nl.getLength());
} catch (Exception e) {
e.printStackTrace();
}
}
}
答案 1 :(得分:1)
Blaise Doughan是对的,附加代码是正确的 问题在某处。我在Eclipse IDE中通过Application启动程序运行了所有测试,但没有任何工作。然后我发现Eclipse项目是所有悲伤的原因。我从命令提示符运行我的类,它工作。创建了一个新的eclipse项目并在那里粘贴了相同的代码,它也在那里工作。 感谢所有人的时间和努力。
答案 2 :(得分:0)
我写了一个简单的NamespaceContext
实现(here),这可能会有所帮助。它需要Map<String, String>
作为输入,key
是前缀,value
是名称空间。
遵循NamespaceContext规定,您可以在unit tests中看到它是如何运作的。
Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "http://foo");
mappings.put("foo2", "http://foo");
mappings.put("bar", "http://bar");
context = new SimpleNamespaceContext(mappings);
context.getNamespaceURI("foo"); // "http://foo"
context.getPrefix("http://foo"); // "foo" or "foo2"
context.getPrefixes("http://foo"); // ["foo", "foo2"]
请注意,它依赖于Google Guava