В настоящее время я генерирую NodeList
всех узлов документа (в порядке документов) вручную. Выражение XPath для получения этого NodeList
равно
//. | //@* | //namespace::*
Моя первая попытка обойти DOM вручную и собрать узлы (NodeSet
- это примитивная реализация NodeList
, делегирующая List
):
private static void walkRecursive(Node cur, NodeSet nodes) {
nodes.add(cur);
if (cur.hasAttributes()) {
NamedNodeMap attrs = cur.getAttributes();
for (int i=0; i < attrs.getLength(); i++) {
Node child = attrs.item(i);
walkRecursive(child, nodes);
}
}
int type = cur.getNodeType();
if (type == Node.ELEMENT_NODE || type == Node.DOCUMENT_NODE) {
NodeList children = cur.getChildNodes();
if (children == null)
return;
for (int i=0; i < children.getLength(); i++) {
Node child = children.item(i);
walkRecursive(child, list);
}
}
}
Я бы начал рекурсию с вызова walkRecursive(doc, nodes)
, где doc
- это org.w3c.Document
и nodes
a (пока пусто) NodeSet
.
Я проверил это, используя этот примитивный XML-документ:
<?xml version="1.0"?>
<myns:root xmlns:myns="http://www.my.ns/#">
<myns:element/>
</myns:root>
Если я, например, канонизирую свои созданные вручную NodeSet
и NodeList
, сгенерированные первоначально упомянутым выражением XPath, и сравниваю два байта для байта, то результат будет одинаковым и, кажется, будет работать нормально.
Но , если я переберу два NodeList
s и выведу отладочную информацию (typeString
просто генерирует строковое представление)
for (int i=0; i < nodes.getLength(); i++) {
Node child = nodes.item(i);
System.out.println("Type: " + typeString(child.getNodeType()) +
" Name:" + child.getNodeName() +
" Local name: " + child.getLocalName() +
" NS: " + child.getNamespaceURI());
}
затем я получаю этот вывод для сгенерированного XPath NodeList
:
Type: DocumentNode Name:#document Local name: null NS: null
Type: Element Name:myns:root Local name: root NS: http://www.my.ns/#
Type: Attribute Name:xmlns:myns Local name: myns NS: http://www.w3.org/2000/xmlns/
Type: Attribute Name:xmlns:xml Local name: xml NS: http://www.w3.org/2000/xmlns/
Type: Text Name:#text Local name: null NS: null
Type: Element Name:myns:element Local name: element NS: http://www.my.ns/#
Type: Text Name:#text Local name: null NS: null
и это для сгенерированного вручную NodeList
:
Type: DocumentNode Name:#document Local name: null NS: null
Type: Element Name:myns:root Local name: root NS: http://www.my.ns/#
Type: Attribute Name:xmlns:myns Local name: myns NS: http://www.w3.org/2000/xmlns/
Type: Text Name:#text Local name: null NS: null
Type: Element Name:myns:element Local name: element NS: http://www.my.ns/#
Type: Text Name:#text Local name: null NS: null
Итак, как вы можете видеть, в первом примере NodeList дополнительно содержит Node
для пространства имен XML:
Type: Attribute Name:xmlns:xml Local name: xml NS: http://www.w3.org/2000/xmlns/
Теперь мои вопросы:
a) Если я правильно интерпретирую xml-names11 , тогда мне не нужно объявление xmlns: xml:
Префикс xml по определению связан с именем пространства имен http://www.w3.org/XML/1998/namespace. Он МОЖЕТ, но не обязательно, быть объявленным и НЕ ДОЛЖЕН быть необъявленным или привязанным к любому другому имени пространства имен. Другие префиксы НЕ ДОЛЖНЫ быть связаны с этим именем пространства имен и НЕ ДОЛЖНЫ быть объявлены как пространство имен по умолчанию.
Я прав? (хотя бы с) намекает в этом направлении)
b) Но тогда, в любом случае, почему оценка XPath добавляет его - не должно ли оно просто включать то, что было в первую очередь, вместо автоматического добавления вещей?
c) Это может вызвать проблемы при канонизации XML , хотя не должно - объявления пространства имен xml
должны быть опущены во время канонизации. Кто-нибудь знает реализации (Java), которые ошибаются?
Редактировать:
Вот код, который я использовал для оценки выражения XPath, содержащего узел пространства имен «xml»:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
InputStream in = ...;
try {
Document doc = dbf.newDocumentBuilder().parse(in);
XPathFactory fac = XPathFactory.newInstance();
XPath xp = fac.newXPath();
XPathExpression exp = xp.compile("//. | //@* | //namespace::*");
NodeList nodes = (NodeList)exp.evaluate(doc, XPathConstants.NODESET);
} finally {
in.close();
}