2013-02-19 17 views
5

Mam pytanie dotyczące usuwania określonych węzłów z pliku xml.usuń węzeł nadrzędny bez węzłów potomnych

Oto moja próbka 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> 

Chciałbym usunąć bez usuwania węzła nodeB="toRemove" Dziecięce tego węzła. Potem muszę zrobić to samo z nodeB attribute="placeHolder". Część wyniku będzie wyglądać tak:

 <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> 

staram kod tak, aby achive że:

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

jestem w stanie zlokalizować węzeł z pożądaną cechą toRemove lub symbol zastępczy, jednak nie jestem w stanie przenieść dzieci z tych węzłów o jeden poziom. Czy możesz mi pomóc w tej sprawie? Może to być rozwiązanie z Linq, XDocument, XmlReader, ale wolę pracować z XmlDocument. Dziękuję za wszelką pomoc, jaką możesz mi zapewnić z góry.

EDIT:

W tym przypadku użyłem kodu nieznacznie zmodyfikowany (aby zachować porządek), że Chuck Savage napisał poniżej. Raz, aby usunąć

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

a następnie zrób to samo z

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

Oto nieco zmodyfikowany kod

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

podejście XSLT dostarczone przez @MiMo jest bardzo przydatna również w tym przypadku.

+0

Wiele elementów 'nodeC' nie ma znaczników zamykających. Czy możesz zaktualizować swoje pytanie za pomocą prawidłowego, dobrze sformułowanego xml? –

+0

Zaktualizowałem mój uproszczony plik xml. Dzięki za podpowiedź, teraz łatwiej jest czytać innym. – wariacik

Odpowiedz

3

Używanie LINQ-XML i swój 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); 

Uwaga: XPath jest dostępny w System.Xml.XPath

UWAGA2: można konwertować do/z XmlDocument przy użyciu these extensions, ponieważ wolisz XmlDocument.

+0

Jedną z wad jest to, że zachowane dzieci zostaną dodane na końcu węzła zawierającego, zamiast pozostać w części dokumentu, gdzie są. Pytający nie powiedział, że zachowanie ich lokalizacji jest wymogiem, ale łatwo może być. – JLRishe

+0

@JLRishe Jeśli spojrzysz na kod OPs, robi on zasadniczo to samo, ale podoba mi się twój punkt widzenia. –

+0

Bardzo podoba mi się to podejście, jednak w tym przypadku jest to wymaganie, aby zachować lokalizację węzłów podrzędnych. Czy istnieje sposób na utrzymanie węzłów potomnych w części dokumentu tam, gdzie są? – wariacik

4

Problem polega na tym, że nie można modyfikować węzłów dokumentu podczas wyliczania ich dzieci - powinieneś utworzyć nowe węzły zamiast próbować modyfikować istniejące, a to staje się nieco trudne przy użyciu XmlDocument.

Najłatwiej zrobić tego rodzaju transformacji używa XSLT, czyli zastosowaniu tej 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> 

do wejścia złożyć wyjścia:

<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> 

Kod zastosować XSLT to po prostu:

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

Jeśli nie jest możliwe (lub pożądane) użycie zewnętrznego pliku dla X SLT można odczytać z ciągiem:

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

dzięki za odpowiedź. Użyję tego rodzaju podejścia innym razem, kiedy będę mógł załadować dodatkowe pliki. Jednak w tym konkretnym przypadku nie mogę używać plików zewnętrznych. Tak więc ładowanie pliku xslt nie jest w moim przypadku opcją. – wariacik

+0

@wariacik: nadal możesz używać XSLT nawet bez zewnętrznego pliku - rozszerzyłem swoją odpowiedź. Problem z XSLT polega na tym, że są one trudne w użyciu, jeśli już ich nie znasz - ale jeśli wykonujesz wiele procesów przetwarzania XML, to jest to dobra inwestycja. – MiMo

+0

Dziękuję. Nie wiedziałem, że mogę załadować xslt jako ciąg znaków. To będzie bardzo przydatne w moich projektach. – wariacik

3

Znam to stare pytanie, ale napisałem to bezpośrednio za pomocą XmlDocument.

Dodanie go, jeśli ktoś woli, aby zrobić to w ten sposób:

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

to jest to :-), mam nadzieję, że to pomoże nikomu.

Powiązane problemy