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

Я создаю скрипт, который должен исправлять 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;
}

person dhardy    schedule 15.09.2010    source источник


Ответы (4)


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

person Maurice Perry    schedule 15.09.2010
comment
Спасибо вам обоим. Есть ли нерекурсивная версия — getChildNodes() и, может быть, реализовать свой собственный поиск по имени? Чем больше я узнаю о XML-библиотеке Java, тем меньше я нахожу, что она делает то, что я ожидал. - person dhardy; 15.09.2010
comment
Я думаю, вам придется реализовать свой собственный поиск - person Maurice Perry; 15.09.2010
comment
Это казалось лучшим решением. Я реализовал функцию, возвращающую List<Node>, поскольку в моем случае мне также не нужно живое поведение NodeList (добавлено в конце моего вопроса, поскольку я не могу размещать здесь блоки кода). - person dhardy; 16.09.2010

как насчет

nodeToBeRemoved.getParentNode().removeChild(nodeToBeRemoved);
person Kalpesh Soni    schedule 25.09.2013
comment
Это не дает ответа на вопрос. Чтобы подвергнуть критике или запросить разъяснения у автора, оставьте комментарий под его публикацией. - person NT3RP; 26.09.2013
comment
это так, эта строка должна быть на уровне 514 — вы находите узел с помощью getElementsByTagName, вы удаляете его из родительского с помощью API getParentNode - person Kalpesh Soni; 26.09.2013

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

person dogbane    schedule 15.09.2010

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

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

parent.removeChild(node);

такие как

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

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

person LarsH    schedule 15.09.2010
comment
Думаю, это сработает — к сожалению, довольно неэффективно. - person dhardy; 16.09.2010