Почему я не могу удалить дочерний элемент, который я только что нашел? NOT_FOUND_ERR

Я создаю 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;
}

Ответы

Ответ 1

Это происходит потому, что когда вы выполняете parent.removeChild(node), родительский элемент необязательно является родителем node, потому что getElementsByTagName() выполняет рекурсивный поиск.

Ответ 2

как насчет

nodeToBeRemoved.getParentNode().removeChild(nodeToBeRemoved);

Ответ 3

parent.removeChild(node) выбрасывает NOT_FOUND_ERR, потому что node не является дочерним элементом parent. Я вижу, что node происходит от getElementsByTagName, который не может быть непосредственным дочерним элементом parent. Это может быть где угодно parent.

Ответ 4

Основываясь на диагнозе @Maurice и @fahd...

Не можете ли вы просто поставить условие до

parent.removeChild(node);

таких как

if (parent.isSameNode(node.getParentNode()))

Тогда он удалит только прямой дочерний элемент данного родителя.