2013-02-19 42 views
5

Tôi có câu hỏi liên quan đến việc xóa các nút cụ thể khỏi tệp xml.xóa nút cha không có nút con

Đây là mẫu của tôi về 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> 

Tôi muốn loại bỏ nút nodeB="toRemove" mà không cần tháo trẻ em của nút này. Sau đó tôi cần phải làm điều tương tự với nodeB attribute="placeHolder". Một phần của kết quả sẽ trông như thế:

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

Tôi đã cố gắng mã như thế này để đạt được điều đó:

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

tôi có thể xác định vị trí nút với thuộc tính mong muốn toRemove hoặc giữ chỗ, tuy nhiên tôi không có thể di chuyển trẻ em của các nút này lên một cấp. Bạn có thể giúp tôi trong trường hợp này? Nó có thể là giải pháp với LINQ, XDocument, XmlReader nhưng tôi thích làm việc với XmlDocument hơn. Cảm ơn bạn đã giúp đỡ bạn có thể cung cấp cho tôi trước.

EDIT:

Trong trường hợp này tôi đã sử dụng mã sửa đổi một chút (để giữ gìn trật tự) mà Chuck Savage viết dưới đây. Một lần để loại bỏ

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

và sau đó làm tương tự với

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

Dưới đây là hơi đổi mã

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

cách tiếp cận XSLT cung cấp bởi @MiMo là rất hữu ích cũng như trong trường hợp này.

+0

Nhiều phần tử 'nodeC' của bạn thiếu thẻ đóng. Bạn có thể cập nhật câu hỏi của mình với xml hợp lệ, được tạo đúng không? –

+0

Tôi đã cập nhật tệp xml được đơn giản hóa của mình. Cảm ơn cho gợi ý, Bây giờ nó dễ đọc hơn cho người khác. – wariacik

Trả lời

3

Sử dụng LINQ-to-XML và XPath của bạn,

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

Lưu ý: XPath có sẵn trong System.Xml.XPath

Note2: Bạn có thể chuyển đổi sang/từ XmlDocument sử dụng these extensions vì bạn thích XmlDocument.

+0

Một nhược điểm ở đây là các trẻ được bảo tồn sẽ được thêm vào cuối của nút chứa thay vì bị bỏ lại trong phần của tài liệu mà chúng đang ở đó. Người hỏi đã không nói rằng bảo quản vị trí của họ là một yêu cầu, nhưng nó có thể dễ dàng. – JLRishe

+0

@JLRishe Nếu bạn nhìn vào mã OPs, anh ấy đang làm cơ bản giống nhau, nhưng tôi thích quan điểm của bạn. –

+0

Tôi thực sự thích cách tiếp cận này, tuy nhiên trong trường hợp này nó là một reaquirment để bảo vệ vị trí của các nút con. Có cách nào để giữ các nút con trong phần tài liệu mà chúng ở đâu không? – wariacik

4

Vấn đề là bạn không thể sửa đổi các nút tài liệu khi liệt kê trên con của họ - bạn nên tạo nút mới thay vì cố sửa đổi các nút hiện có và điều này trở nên phức tạp một chút khi sử dụng XmlDocument.

Cách dễ nhất để làm điều này loại chuyển đổi được sử dụng XSLT, tức là áp dụng XSLT này:

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

đầu vào tập tin đầu ra là:

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

Mã áp dụng XSLT chỉ đơn giản là:

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

Nếu không thể (hoặc mong muốn) sử dụng tệp bên ngoài cho X SLT nó có thể được đọc từ một chuỗi:

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

cảm ơn câu trả lời. Tôi sẽ sử dụng kiểu tiếp cận này một lần nữa khi tôi có thể tải các tệp bổ sung. Tuy nhiên trong trường hợp cụ thể này, tôi không thể sử dụng các tệp bên ngoài. Vì vậy, tải tập tin xslt không phải là một lựa chọn trong trường hợp của tôi. – wariacik

+0

@wariacik: bạn vẫn có thể sử dụng XSLT ngay cả khi không có tệp bên ngoài - tôi đã mở rộng câu trả lời của mình. Vấn đề với XSLT là chúng rất khó sử dụng nếu bạn chưa quen với chúng - nhưng nếu bạn thực hiện rất nhiều việc xử lý XML, chúng là một đầu tư tốt. – MiMo

+0

Cảm ơn bạn. Tôi không biết rằng tôi có thể tải xslt như một chuỗi. Điều này sẽ rất hữu ích trong các dự án của tôi. – wariacik

3

Tôi biết câu hỏi cũ của nó, nhưng tôi đã viết trực tiếp bằng cách sử dụng XmlDocument.

Thêm nó nếu có ai thích làm theo cách này:

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

Vậy đó :-), hy vọng nó sẽ giúp bất cứ ai.

Các vấn đề liên quan