尝试使用XPath过滤XML

时间:2010-09-28 20:26:00

标签: c# linq xslt xpath linq-to-xml

我正在尝试使用XPath过滤XML文件。我正在使用的XPath肯定是过滤我想要的数据,但我只是不确定如何整体过滤文件。

以下是示例XML文件:

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

这是所需的输出:

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

解决方案不一定必须由XPath解决,但由于这是一个.NET应用程序,.NET API将不胜感激!以下代码可以剪切并粘贴到LINQPad中,无需编辑即可查看我正在尝试执行的操作。

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");
编辑:我完全错过了XML Transforms - 感谢您的解决方案!这是您可以粘贴到LINQPad中的解决方案,看它是否正常工作:

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

1 个答案:

答案 0 :(得分:2)

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>