Попытка отфильтровать XML с помощью XPath - PullRequest
3 голосов
/ 29 сентября 2010

Я пытаюсь отфильтровать файл XML с помощью XPath. Используемый мной 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, API .NET приветствуются! Следующий код можно вырезать и вставить в 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 Ответ

2 голосов
/ 29 сентября 2010

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...