xmlvb.netlinq

Remove a particular XML element depending on a particular attribute value with LINQ


As source I have a XML document with the following content:

<?xml version="1.0"?>
<report>
    <feature tag="Config"/>
    <feature tag="Runtime">
        <feature tag="Output">
            <feature tag="Target">
                <property name="type" value="Html"/>
                <property name="driver" value="Telelogic.Html.Driver"/>
            </feature>
            <feature tag="Target">
                <property name="type" value="Word"/>
                <property name="driver" value="Telelogic.Word.Driver"/>
            </feature>
            <feature tag="Target">
                <property name="type" value="PDF"/>
                <property name="driver" value="Telelogic.Pdf.Driver"/>
            </feature>
        </feature>
    </feature>
</report>

If the value of the tag attribute within the feature element Target is not equal Word, I want to remove the complete feature element Target in order to look like the following result:

<?xml version="1.0"?>
<report>
    <feature tag="Config"/>
    <feature tag="Runtime">
        <feature tag="Output">
            <feature tag="Target">
                <property name="type" value="Word"/>
                <property name="driver" value="Telelogic.Word.Driver"/>
            </feature>
        </feature>
    </feature>
</report>

Therefore, I prepared the following vb.net function:

 Public Function removeElements(ByVal source As String) As Boolean
    Try
        Dim doc As New XDocument

        Dim output As String =
        "/report/feature[@tag='Runtime']/feature[@tag='Output']"

        Dim propertyType As String =
        "/report/feature[@tag='Runtime']/feature[@tag='Output']/feature[@tag='Target']/property[@name='type']"

        Dim path As String = "c:\temp\mySource.xml"

        doc = XDocument.Parse(source)
        doc.XPathSelectElements(output).Elements _
        .Where(Function(x) x.XPathSelectElement(propertyType).Attribute("value").Value <> "Word").Remove

        doc.Save(path)

        Return True

    Catch ex As Exception
        Return False
    End Try
End Function

The problem is, that my code removes all Target feature elements, so that the Output feature element is empty, as you can see in the result:

<?xml version="1.0" encoding="utf-8"?>
<report>
  <feature tag="Config"></feature>
  <feature tag="Runtime">
    <feature tag="Output" />
  </feature>
</report>

I found no solution with help of LINQ to remove every Target feature element, except that one, that holds the string "Word" as attribute value.


Solution

  • Here is a working solution implemented in both c# and VB.NET.

    c#

    void Main()
    {
        const string inputXML = @"e:\Temp\input.xml";
        const string outputXML = @"e:\Temp\output.xml";
    
        XDocument xdoc = XDocument.Load(inputXML);
    
        xdoc.XPathSelectElements("/report/feature[@tag='Runtime']/feature[@tag='Output']/feature[not(property/@value='Word')]")
            .ToList()
            .ForEach(x => x.Remove());
    
       xdoc.Save(outputXML);
    }
    

    VB.NET

    Sub Main
        Const inputXML As String = "e:\Temp\input.xml"
        Const outputXML As String = "e:\Temp\output.xml"
    
        Dim xdoc As XDocument = XDocument.Load(inputXML)
    
        xdoc.XPathSelectElements("/report/feature[@tag='Runtime']/feature[@tag='Output']/feature[not(property/@value='Word')]") _
            .ToList().ForEach(Sub(x) x.Remove())
    
        xdoc.Save(outputXML)
    End Sub