Как использовать XPath на документах xml, имеющих пространство имен по умолчанию
Я хочу манипулировать документом xml, имеющим пространство имен по умолчанию, но без префикса. Есть ли способ использовать xpath без пространства имен uri, как будто нет пространства имен?
Я полагаю, что это будет возможно, если мы установим для свойства namespaceAware documentBuilderFactory значение 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>
3 ответа
Обработка XPath для документа, который использует пространство имен по умолчанию (без префикса), аналогична обработке XPath для документа, который использует префиксы:
Для документов, удовлетворяющих пространству имен, вы можете использовать NamespaceContext при выполнении XPath. Вам нужно будет поставить префикс фрагментов в 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;
}
}
}
Примечание: я также использовал исправленный 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();
}
}
}
Blaise Doughan прав, прикрепленный код верен.
Проблема была где-то еще. Я выполнял все свои тесты через Application Launcher в Eclipse IDE, и ничего не работало. Тогда я обнаружил, что проект Eclipse был причиной всего горя. Я запустил свой класс из командной строки, он работал. Создали новый проект Eclipse и вставили туда же код, он тоже работал. Спасибо всем, ребята, за ваше время и усилия.
Я написал простой NamespaceContext
реализация ( здесь), это может быть полезным. Требуется Map<String, String>
в качестве входных данных, где key
это префикс, а value
это пространство имен.
Он следует за спецификацией NamespaceContext, и вы можете увидеть, как он работает в модульных тестах.
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