Ответ 1
Это происходит потому, что когда вы выполняете parent.removeChild(node), родительский элемент необязательно является родителем node, потому что getElementsByTagName() выполняет рекурсивный поиск.
Я создаю script, который должен исправлять файлы XML, включая замену одного списка элементов на другой. Следующая функция применяет патч (включая возможно пустой список элементов с тем же именем) в родительский список элементов элементов с тем же именем (также возможно пустой список). (Это лишь малая часть логики исправления).
Почему, когда я запускаю код, могу ли я получить следующую ошибку?
org.w3c.dom.DOMException: NOT_FOUND_ERR: An attempt is made to reference a node in a context where it does not exist.
at com.sun.org.apache.xerces.internal.dom.ParentNode.internalRemoveChild(ParentNode.java:503)
at com.sun.org.apache.xerces.internal.dom.ParentNode.removeChild(ParentNode.java:484)
at CombineSweeps$PTReplaceNodeList.apply(CombineSweeps.java:514)
(строка 514 помечена ниже.) Насколько я понимаю, я только что проверил, что этот элемент существует (поскольку NodeList является живым, его первая запись всегда будет следующей совпадением или нулевым). Интересно, что это не всегда проблема.
private static class PTReplaceNodeList extends PTBase {
private final String name;
private final String nextElement;
private final List<Node> childList;
...
int apply(Document document, Node parent, Node node_unused) {
NodeList nodes;
// A marker for where to insert our nodes.
// We make a guess using nextElement (if null, means at end).
Node refNode = null;
if (parent instanceof Document) { // root element
Document parDoc = (Document) parent;
nodes = parDoc.getElementsByTagName(name);
if (nextElement != null) {
refNode = parDoc.getElementsByTagName(nextElement).item(0);
}
} else {
Element parElt = (Element) parent;
nodes = parElt.getElementsByTagName(name);
if (nextElement != null) {
refNode = parElt.getElementsByTagName(nextElement).item(0);
}
}
while (true) {
// iterate through the list of nodes
Node node = nodes.item(0);
if (node == null) {
break;
}
// Reliable guess: insert before node following last in list
refNode = node.getNextSibling();
parent.removeChild(node); // line 514
}
for (Node child : childList) {
Node imported = document.importNode(child, true);
parent.insertBefore(imported, refNode);
}
return childList.size();
}
}
Изменить: я использовал следующую функцию в качестве замены для getElementsByTagName()
(см. принятый ответ).
/** Returns all direct children of node with name name.
*
* Note: not the same as getElementsByTagName(), which finds all descendants. */
static List<Node> getChildNodes( Node node, String name ){
ArrayList<Node> r = new ArrayList<Node>();
NodeList children = node.getChildNodes();
int l = children.getLength();
for( int i = 0; i < l; ++i ){
if( name.equals( children.item(i).getNodeName() ) )
r.add( children.item(i) );
}
return r;
}
Это происходит потому, что когда вы выполняете parent.removeChild(node), родительский элемент необязательно является родителем node, потому что getElementsByTagName() выполняет рекурсивный поиск.
как насчет
nodeToBeRemoved.getParentNode().removeChild(nodeToBeRemoved);
parent.removeChild(node)
выбрасывает NOT_FOUND_ERR, потому что node
не является дочерним элементом parent
. Я вижу, что node
происходит от getElementsByTagName
, который не может быть непосредственным дочерним элементом parent
. Это может быть где угодно parent
.
Основываясь на диагнозе @Maurice и @fahd...
Не можете ли вы просто поставить условие до
parent.removeChild(node);
таких как
if (parent.isSameNode(node.getParentNode()))
Тогда он удалит только прямой дочерний элемент данного родителя.