2010-09-15 74 views
15

Je construis un script qui doit corriger des fichiers XML, y compris en remplaçant une liste d'éléments par une autre. La fonction suivante applique un patch (impliquant une liste éventuellement vide d'éléments portant le même nom) sur la liste d'éléments d'un élément parent du même nom (éventuellement une liste vide). (Ceci est seulement une petite partie de la logique de correction). Pourquoi, lorsque j'exécute le code, j'obtiens l'erreur suivante?Pourquoi ne puis-je pas supprimer un élément enfant que je viens de trouver? NOT_FOUND_ERR

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) 

(ligne 514 est étiqueté ci-dessous.) Pour autant que je le comprends, je viens vérifié que l'élément existe (car NodeList est en direct, sa première entrée sera toujours le prochain match ou nul). Fait intéressant, ce n'est pas toujours un problème.

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(); 
    } 
} 

Edit: J'utilisé la fonction suivante en remplacement de getElementsByTagName() (voir réponse acceptée).

/** 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; 
} 

Répondre

12

En effet, lorsque vous faites parent.removeChild (noeud), parent est pas nécessairement le parent du nœud parce getElementsByTagName() fait une recherche récursive.

+0

Merci tous les deux. Y at-il une version non récursive - 'getChildNodes()' et implémenter ma propre recherche par nom peut-être? Plus j'apprends sur la bibliothèque XML de Java, moins je trouve qu'elle fait ce que je m'attendais. – dhardy

+0

Je suppose que vous devrez mettre en œuvre votre propre recherche –

+0

Cela semblait être la meilleure solution. J'ai implémenté une fonction retournant 'List ' puisque dans mon cas je ne veux pas non plus du comportement" live "de' NodeList' (ajouté à la fin de ma question puisque je ne peux pas poster de blocs de code ici). – dhardy

4

parent.removeChild(node) génère un NOT_FOUND_ERR car node n'est pas un enfant de parent. Je vois que node vient de getElementsByTagName qui pourrait ne pas être un enfant immédiat de parent. Il pourrait être n'importe où sous parent.

0

bâtiment sur le diagnostic par @Maurice et @fahd ...

ne peut pas vous mettre juste une condition avant

parent.removeChild(node); 

tels que

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

alors il ne supprimer un enfant direct du parent donné.

+0

Je suppose que cela fonctionnerait - d'une manière plutôt inefficace malheureusement. – dhardy

11

que diriez-vous

nodeToBeRemoved.getParentNode().removeChild(nodeToBeRemoved); 
+0

Ceci ne fournit pas de réponse à la question. Pour critiquer ou demander des éclaircissements à un auteur, laissez un commentaire sous son article. – NT3RP

+0

il le fait, cette ligne doit être à 514 - vous trouvez un noeud en utilisant getElementsByTagName, vous le retirez de son parent en utilisant l'API getParentNode –