Trying to filter XML with XPath

I am trying to filter an XML file using XPath. The XPath that I use definitely filters the data that I want, but I'm just not sure how to filter the file as a whole.

Here is an example XML file:

<fields>
    <field name='F'>
        <field name='0'><value>F.0 stuff</value></field>
        <field name='1'><value>F.1 stuff</value></field>
        <field name='2'><value>F.2 stuff</value></field>
    </field>
    <field name='B'>
        <field name='0'><value>B.0 stuff</value></field>
        <field name='1'><value>B.1 stuff</value></field>
        <field name='2'><value>B.2 stuff</value></field>
        <field name='3'><value>B.3 stuff</value></field>
    </field>
</fields>

Here is the desired result:

<fields>
    <field name='F'>
        <field name='1'><value>F.1 stuff</value></field>
        <field name='2'><value>F.2 stuff</value></field>
    </field>
    <field name='B'>
        <field name='3'><value>B.3 stuff</value></field>
    </field>
</fields>

The solution does not have to be solved by XPath, but since it is a .NET application, the .NET API will be appreciated! The following code can be cut and pasted into LINQPad without any changes to see what I'm trying to do.

var doc = XDocument.Parse(@"
<fields>
    <field name='F'>
        <field name='0'><value>F.0 stuff</value></field>
        <field name='1'><value>F.1 stuff</value></field>
        <field name='2'><value>F.2 stuff</value></field>
    </field>
    <field name='B'>
        <field name='0'><value>B.0 stuff</value></field>
        <field name='1'><value>B.1 stuff</value></field>
        <field name='2'><value>B.2 stuff</value></field>
        <field name='3'><value>B.3 stuff</value></field>
    </field>
</fields>");
doc.Dump("Original XML");

var xpath = "//fields/field[@name='F']/field[@name='1' or @name='2'] | //fields/field[@name='B']/field[@name='3']";
doc.XPathSelectElements(xpath).Dump("XPath Combined");

var desired = XDocument.Parse(@"
<fields>
    <field name='F'>
        <field name='1'><value>F.1 stuff</value></field>
        <field name='2'><value>F.2 stuff</value></field>
    </field>
    <field name='B'>
        <field name='3'><value>B.3 stuff</value></field>
    </field>
</fields>");
desired.Dump("Desired Filtered XML");

EDIT: I completely skipped XML Transforms - thanks for the solution! Here is a solution you can insert into LINQPad to see how it works:

var filterString = @"@name=""F""]/field[@name=""0""]  | field[@name=""B""]/field[not(@name=""3"")";
var xslFmt = @"
<xsl:stylesheet version='1.0'
 xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
 <xsl:output omit-xml-declaration='yes' indent='yes'/>

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

 <xsl:template match=
  'field[{0}]
  '/>
</xsl:stylesheet>";
var xslMarkup = string.Format(xslFmt, filterString);

var xmlTree = XDocument.Parse(@"
<fields>
    <field name='F'>
        <field name='0'><value>F.0 stuff</value></field>
        <field name='1'><value>F.1 stuff</value></field>
        <field name='2'><value>F.2 stuff</value></field>
    </field>
    <field name='B'>
        <field name='0'><value>B.0 stuff</value></field>
        <field name='1'><value>B.1 stuff</value></field>
        <field name='2'><value>B.2 stuff</value></field>
        <field name='3'><value>B.3 stuff</value></field>
    </field>
</fields>");
xmlTree.Dump("Original XML");

// Code from MSDN: http://msdn.microsoft.com/en-us/library/bb675186.aspx
var newTree = new XDocument();
using (var writer = newTree.CreateWriter()) {
    // Load the style sheet.
    var xslt = new XslCompiledTransform();
    xslt.Load(XmlReader.Create(new StringReader(xslMarkup)));

    // Execute the transform and output the results to a writer.
    xslt.Transform(xmlTree.CreateReader(), writer);
}

newTree.Dump("Transformed XML");
+3
source share
1

XPath - , XML.

, , XSLT.

XDocument.CreateNavigator(), XslCompiledTransform.Transform() .

XSLT :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match=
  "field[@name='F']/field[@name='0']
  |
   field[@name='B']/field[not(@name='3')]
  "/>
</xsl:stylesheet>

XML-, , :

<fields>
    <field name="F">
        <field name="1">
            <value>F.1 stuff</value>
        </field>
        <field name="2">
            <value>F.2 stuff</value>
        </field>
    </field>
    <field name="B">
        <field name="3">
            <value>B.3 stuff</value>
        </field>
    </field>
</fields>
+2

Source: https://habr.com/ru/post/1767061/


All Articles