2013-02-19 15 views
5

Ho una domanda relativa alla rimozione di nodi specifici dal file xml.rimuovere il nodo genitore senza nodi childs

Ecco il mio esempio di XML:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <nodeA attribute="1"> 
    <nodeB attribute="table"> 
     <nodeC attribute="500"></nodeC> 
     <nodeC attribute="5"></nodeC> 
    </nodeB> 
    <nodeB attribute="3"> 
     <nodeC attribute="4"></nodeC> 
     <nodeC attribute="5"></nodeC> 
     <nodeC attribute="5"></nodeC> 
    </nodeB> 
    <nodeB attribute="placeHolder"> 
    <nodeB attribute="toRemove"> 
     <nodeB attribute="glass"></nodeB> 
     <nodeE attribute="7"></nodeE> 
     <nodeB attribute="glass"></nodeB> 
     <nodeB attribute="glass"></nodeB> 
    </nodeB> 
    </nodeB> 
    <nodeB attribute="3"> 
     <nodeC attribute="4"></nodeC> 
     <nodeC attribute="5"></nodeC> 
     <nodeC attribtue="5"></nodeC> 
    </nodeB> 
    <nodeB attribute="placeHolder"> 
    <nodeB attribute="toRemove"> 
     <nodeB attribute="glass"></nodeB> 
     <nodeE attribute="7"></nodeE> 
     <nodeB attribute="glass"></nodeB> 
     <nodeB attribute="glass"></nodeB> 
    </nodeB> 
    </nodeB> 
    </nodeA> 
</root> 

Vorrei rimuovere nodo nodeB="toRemove" senza rimuovere i bambini di questo nodo. Dopo di che ho bisogno di fare la stessa cosa con nodeB attribute="placeHolder". Parte del risultato ottenuto è come quello:

 <nodeB attribute="3"> 
     <nodeC attribute="4"></nodeC> 
     <nodeC attribute="5"></nodeC> 
     <nodeC attribtue="5"></nodeC> 
    </nodeB> 
    <nodeB attribute="glass"></nodeB> 
     <nodeE attribute="7"></nodeE> 
    <nodeB attribute="glass"></nodeB> 
    <nodeB attribute="glass"></nodeB> 

Ho cercato il codice come questo per achive che:

 XmlNodeList nodeList = doc.SelectNodes("//nodeB[@attribute=\"toRemove\"]"); 

     foreach (XmlNode node in nodeList) 
     { 
      foreach (XmlNode child in node.ChildNodes) 
      { 
       node.ParentNode.AppendChild(child); 
      } 
      node.ParentNode.RemoveChild(node); 
     } 
     doc.Save(XmlFilePathSource); 

sono in grado di localizzare nodo con attributo desiderato toremove o un segnaposto, comunque io non sono in grado di spostare i figli di questi nodi di un livello. Potresti aiutarmi in questo caso? Può essere una soluzione con Linq, XDocument, XmlReader ma preferisco lavorare con XmlDocument. Grazie per l'aiuto che potresti fornirmi in anticipo.

EDIT:

In questo caso ho usato il codice leggermente modificato (per preservare l'ordine) che Chuck Savage ha scritto sotto. Una volta per rimuovere

<nodeB attribute="toRemove"> </nodeB> 

e poi fare lo stesso con

<nodeB attribute="placeHolder"></nodeB> 

Qui è leggermente modificata codice

XElement root = XElement.Load(XmlFilePathSource); 
    var removes = root.XPathSelectElements("//nodeB[@attribute=\"toRemove\"]"); 
    foreach (XElement node in removes.ToArray()) 
    { 
    node.Parent.AddAfterSelf(node.Elements()); 
    node.Remove(); 
    } 
    root.Save(XmlFilePathSource); 

approccio XSLT fornito da @MiMo è molto utile anche in questo caso.

+0

Molti degli elementi 'nodeC' mancano i loro tag di chiusura. Puoi aggiornare la tua domanda con xml valido e ben formato? –

+0

Ho aggiornato il mio file XML semplificato. Grazie per il suggerimento, è più facile da leggere per gli altri ora. – wariacik

risposta

3

utilizzando LINQ-to-XML e la vostra XPath,

XElement root = XElement.Load(XmlFilePathSource); // or .Parse(string) 
var removes = root.XPathSelectElements("//nodeB[@attribute=\"toRemove\"]"); 
foreach (XElement node in removes.ToArray()) 
{ 
    node.AddBeforeSelf(node.Elements()); 
    node.Remove(); 
} 
root.Save(XmlFilePathSource); 

Nota: XPath è disponibile in System.Xml.XPath

Nota 2: È possibile convertire in/da XmlDocument utilizzando these extensions poiché si preferisce XmlDocument.

+0

Uno svantaggio qui è che i figli conservati verrebbero aggiunti alla fine del nodo contenente anziché essere lasciati nella parte del documento in cui si trovano. Il richiedente non ha detto che preservare la loro posizione è un requisito, ma potrebbe facilmente esserlo. – JLRishe

+0

@JLRishe Se si guarda il codice OP, sta facendo praticamente la stessa cosa, ma mi piace il tuo punto. –

+0

Mi piace molto questo approccio, tuttavia in questo caso è un riacquisto per preservare la posizione dei nodi figli. C'è un modo per mantenere i nodi figli in una parte del documento dove sono stati collocati? – wariacik

4

Il problema è che non è possibile modificare i nodi del documento durante l'enumerazione sui propri figli: è necessario creare nuovi nodi anziché tentare di modificare quelli esistenti e questo diventa un po 'complicato utilizzando XmlDocument.

Il modo più semplice per fare questo tipo di trasformazione utilizza XSLT, cioè applicando questo XSLT:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="nodeB[@attribute='toRemove' or @attribute='placeHolder']"> 
    <xsl:apply-templates/> 
    </xsl:template> 

    <xsl:template match="text()"> 
    </xsl:template> 

    <xsl:template match="@* | *"> 
    <xsl:copy> 
     <xsl:apply-templates select="@* | node()"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

all'ingresso depositare l'output è:

<root> 
    <nodeA attribute="1"> 
    <nodeB attribute="table"> 
     <nodeC attribute="500" /> 
     <nodeC attribute="5" /> 
    </nodeB> 
    <nodeB attribute="3"> 
     <nodeC attribute="4" /> 
     <nodeC attribute="5" /> 
     <nodeC attribute="5" /> 
    </nodeB> 
    <nodeB attribute="glass" /> 
    <nodeE attribute="7" /> 
    <nodeB attribute="glass" /> 
    <nodeB attribute="glass" /> 
    <nodeB attribute="3"> 
     <nodeC attribute="4" /> 
     <nodeC attribute="5" /> 
     <nodeC attribtue="5" /> 
    </nodeB> 
    <nodeB attribute="glass" /> 
    <nodeE attribute="7" /> 
    <nodeB attribute="glass" /> 
    <nodeB attribute="glass" /> 
    </nodeA> 
</root> 

Il codice di applicare il XSLT è semplicemente:

XslCompiledTransform transform = new XslCompiledTransform(); 
    transform.Load(@"c:\temp\nodes.xslt"); 
    transform.Transform(@"c:\temp\nodes.xml", @"c:\temp\nodes-cleaned.xml"); 

Se non è possibile (o desiderabile) utilizzare un file esterno per X SLT può essere letto da una stringa:

string xsltString = 
    @"<xsl:stylesheet 
     version='1.0' 
     xmlns:xsl='http://www.w3.org/1999/XSL/Transform'> 

     <xsl:output method=""xml"" indent=""yes""/> 

     <xsl:template match=""nodeB[@attribute='toRemove' or @attribute='placeHolder']""> 
     <xsl:apply-templates/> 
     </xsl:template> 

     <xsl:template match=""text()""> 
     </xsl:template> 

     <xsl:template match=""@* | *""> 
     <xsl:copy> 
      <xsl:apply-templates select=""@* | node()""/> 
     </xsl:copy> 
     </xsl:template> 

    </xsl:stylesheet>"; 
    XslCompiledTransform transform = new XslCompiledTransform(); 
    using (StringReader stringReader = new StringReader(xsltString)) 
    using (XmlReader reader = XmlReader.Create(stringReader)) { 
    transform.Load(reader); 
    } 
    transform.Transform(@"c:\temp\nodes.xml", @"c:\temp\nodes-cleaned.xml");  
+0

grazie per la risposta. Userò questo tipo di approccio un'altra volta quando sarò in grado di caricare file aggiuntivi. Tuttavia, in questo caso specifico, non posso usare file esterni. Quindi caricare il file xslt non è un'opzione nel mio caso. – wariacik

+0

@wariacik: puoi ancora usare un XSLT anche senza un file esterno - ho ampliato la mia risposta. Il problema con XSLT è che sono difficili da usare se non si ha già familiarità con loro - ma se si fa un sacco di elaborazione XML impararli è un buon investimento. – MiMo

+0

Grazie. Non sapevo di poter caricare xslt come stringa. Questo sarà molto utile nei miei progetti. – wariacik

3

So che è una vecchia domanda, ma l'ho scritta usando XmlDocument direttamente.

Aggiunta se qualcuno preferisce farlo in questo modo:

XmlNode child_to_remove = parent.ChildNodes[i]; // get the child to remove 

// move all the children of "child_to_remove" to be the child of their grandfather (== parent) 
while(child_to_remove.HasChildNodes) 
    parent.InsertBefore(child_to_remove.ChildNodes[0], child_to_remove); 

parent.RemoveChild(child_to_remove); 

Questo è tutto :-), spero che ti aiuta nessuno.

Problemi correlati