2012-01-26 1 views
0

J'utilise une fonction qui prend en charge deux éléments DOM - parent et enfant provenant de différents documents. J'importe l'élément enfant, le transforme, puis l'ajoute à l'élément parent. Mais, la dernière ligne du code suivant lève une exception dom: org.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: un noeud est utilisé dans un document différent de celui qui l'a créé.Transformation et ajout d'un noeud Dom

S'il vous plaît voir mon code ci-dessous:

public void attachNodeToParent (Element parent, Element child) throws Exception { 
     Document parent_doc = parent.getOwnerDocument(); 
     child = (Element)parent_doc.importNode(child, true); 
// Imported child Element is shown below: 
//  <node id="101"> 
//  <node id="102"> 
//   <node id="103" /> 
//  </node> 
//  <node id="104"> 
//   <node id="103" /> 
//  </node> 
//  </node> 

     // convert child Element into String 
     Source source = new DOMSource(child); 
     StringWriter stringWriter = new StringWriter(); 
     Result result = new StreamResult(stringWriter); 
     TransformerFactory factory = TransformerFactory.newInstance(); 
     Transformer transformer = factory.newTransformer(); 
     transformer.transform(source, result); 
     String childXml = stringWriter.getBuffer().toString(); 


     // Recursively modify the id attributes of every node 
     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 
     DocumentBuilder db = dbf.newDocumentBuilder(); 
     Document doc = db.parse(new InputSource(new StringReader(childXml))); 
     XPathFactory xpathFactory = XPathFactory.newInstance(); 
     XPath xpath = xpathFactory.newXPath(); 
     NodeList nodes = (NodeList) xpath.compile("//node[@id]").evaluate(doc, XPathConstants.NODESET); 
     for (int nodeNumber = 0; nodeNumber < nodes.getLength(); ++nodeNumber) { 
      final Element node = (Element) nodes.item(nodeNumber); 
      final String nodeId = node.getAttribute("id"); 
      final String newNodeId = "prefix/" + nodeId; 
      node.getAttributeNode("id").setValue(newNodeId); 
     } 


     final StringWriter writer = new StringWriter(); 
     transformer.transform(source, new StreamResult(writer)); 
     writer.flush(); 
     writer.close(); 
     String transformedChildXml = writer.toString(); 

     // Prase transformedChildXml String into XML 
     dbf.setNamespaceAware(true); 
     DocumentBuilder builder = dbf.newDocumentBuilder(); 
     Document document = builder.parse(new InputSource(new StringReader(transformedChildXml))); 
     document.setXmlStandalone(false); 
     child = document.getDocumentElement(); 

     // child Element is now transformed to: 
//  <node id="prefix/101"> 
//   <node id="prefix/102"> 
//   <node id="prefix/103" /> 
//   </node> 
//   <node id="prefix/104"> 
//   <node id="prefix/103" /> 
//   </node> 
//  </node> 

     // append transformed child Element to parent Element 
     // Throws o rg.w3c.dom.DOMException: WRONG_DOCUMENT_ERR: 
     // A node is used in a different document than the one that created it. 
     parent.appendChild(child); 
    } 

Répondre

0

La réponse courte est que l'élément enfant sur la ligne avant-dernière partie du document créé par la ligne

Document document = builder.parse(new InputSource(new StringReader(transformedChildXml))); 

et non le propriétaire du document de le parent. Vous devrez donc à nouveau utiliser importNode pour le transférer vers votre document cible.

Mais ne faites pas cela. Votre code a deux sérialiser à String et analyser les cycles de document, ce qui est très inefficace et aucun ne devrait être nécessaire. Une fois que vous avez appelé importNode au début, corrigez simplement les identifiants en place et ajoutez l'enfant au parent à la fin.

+0

Après avoir importé le noeud initialement, je ne suis pas sûr de savoir comment transformer les ID de manière récursive. Si le nœud a plusieurs sous-niveaux (2 sous-niveaux dans mon exemple) alors est-ce que je fais une boucle récursivement à travers chaque niveau? – sony

+0

Oui, vous pouvez parcourir l'arborescence DOM avec une récursivité, ou vous pouvez accéder à l'ensemble des nœuds avec des ID de la même manière que vous le faites actuellement avec un xpath. Changez simplement le premier paramètre de 'evaluate' en' child' et le xpath en '" .// node [@id] "' – Alohci

+0

Le xpath ".//node[@id]" sur l'élément cible retourne un NodeList de seulement les nœuds enfants. Dites que la cible est " Ensuite seulement les nœuds avec les identifiants 102 et 103 mais le nœud avec id =" 101 "ie la racine de l'élément cible lui-même n'est pas retournée, donc je modifie d'abord la racine de la cible, puis je modifie tous les nœuds enfants, ce qui n'est pas très joli, c'est pourquoi j'ai analysé xml en String. – sony