2010-09-28 101 views
3

我正在嘗試使用XPath篩選XML文件。我使用的XPath絕對是對我想要的數據進行過濾,但我不確定如何過濾整個文件。嘗試使用XPath篩選XML

這裏的示例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轉換 - 謝謝你的解決方案!這裏是你可以粘貼到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

什麼是標準從你的原始XML獲取「已過濾」的一個?你想看哪個XML節點,你想「過濾」出哪個? – 2010-09-28 20:28:57

+0

XSLT在這裏是正確的答案嗎? – 2010-09-28 20:30:12

+1

非常詳細的問題,但它仍然不是很清楚如何從輸入到輸出。什麼標準適用? – 2010-09-28 20:31:38

回答

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>