2010-09-15 9 views
15

Sto costruendo uno script che deve applicare patch ai file XML, inclusa la sostituzione di un elenco di elementi con un altro. La seguente funzione applica una patch (che comprende una lista eventualmente vuota di elementi con lo stesso nome) sull'elenco di elementi dell'elemento genitore con lo stesso nome (probabilmente anche una lista vuota). (Questa è solo una piccola parte della logica di patching).Perché non riesco a rimuovere un elemento figlio che ho appena trovato? NOT_FOUND_ERR

Perché, quando eseguo il codice, ottengo il seguente errore?

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) 

(linea 514 è etichettato sotto.) Per quanto riguarda ho capito, ho appena verificato che l'elemento esiste (perché NodeList è vivo, il suo primo ingresso sarà sempre la prossima partita o null). È interessante notare che questo non è sempre un problema.

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: ho usato la seguente funzione in sostituzione del getElementsByTagName() (vedi risposta accettata).

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

risposta

12

Questo perché quando si sta facendo parent.removeChild (nodo), genitore non è necessariamente il genitore del nodo perché getElementsByTagName() sta facendo una ricerca ricorsiva.

+0

Grazie entrambi. Esiste una versione non ricorsiva - 'getChildNodes()' e implementa la mia ricerca per nome forse? Più sto imparando sulla libreria XML di java, meno riesco a trovare ciò che mi aspetto. – dhardy

+0

Immagino che dovrai implementare la tua ricerca –

+0

Questa sembra essere la soluzione migliore. Ho implementato una funzione restituendo 'Lista ' poiché nel mio caso anch'io non voglio il comportamento "live" di 'NodeList' (aggiunto alla fine della mia domanda dato che qui non posso inserire blocchi di codice). – dhardy

4

parent.removeChild(node) sta lanciando un NOT_FOUND_ERR perché node non è un figlio di parent. Vedo che node proviene da getElementsByTagName che potrebbe non essere un figlio immediato di parent. Potrebbe essere ovunque sotto parent.

0

Costruire sulla diagnosi da @Maurice e @fahd ...

Non si può semplicemente mettere una condizione prima di

parent.removeChild(node); 

come

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

Poi è solo sarebbe rimuovere un figlio diretto del genitore dato.

+0

Immagino che funzionerebbe - in un modo piuttosto inefficiente, sfortunatamente. – dhardy

11

come su

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

Questo non fornisce una risposta alla domanda. Per criticare o richiedere chiarimenti da un autore, lascia un commento sotto il loro post. – NT3RP

+0

lo fa, questa linea deve essere a 514 - si trova un nodo usando getElementsByTagName, lo si rimuove dal suo genitore usando l'API getParentNode –