如何使用XPATH附加节点

时间:2013-10-14 18:45:17

标签: xml xpath

大家好我正在使用XPATH和XML.SelectNodes()从XML文件中提取圆顶数据,我希望这些数据按照一定的顺序,XML文件是这样的:

<?xml version='1.0' encoding='UTF-8'?>
    <ConvenioAladi>
       <Operaciones>
         <Operacion Prioridad='Alta' />
         <Operacion Prioridad='Media' />
         <Operacion Prioridad='Alta' />
         <Operacion Prioridad='Baja' />
         <Operacion Prioridad='Baja' />
         <Operacion Prioridad='Media' />
       </Operaciones>
    </ConvenioAladi>

希望获得这样的XML:

<?xml version='1.0' encoding='UTF-8'?>
    <ConvenioAladi>
       <Operaciones>
         <Operacion Prioridad='Alta' />
         <Operacion Prioridad='Alta' />
         <Operacion Prioridad='Media' />
         <Operacion Prioridad='Media' />
         <Operacion Prioridad='Baja' />
         <Operacion Prioridad='Baja' />
       </Operaciones>
    </ConvenioAladi>

我可以通过提供XPATH来随时获得Prioridad属性之一:

'/ ConvenioAladi / Operaciones / Operacion [@ Prioridad =' 阿尔塔 ']'

但如果我尝试这样的话: '/ ConvenioAladi / Operaciones / Operacion [@ Prioridad ='Alta'或@ Prioridad ='Media'或@ Prioridad ='Baja']'

或者: '/ ConvenioAladi / Operaciones / Operacion [@ Prioridad ='Alta'] | / ConvenioAladi / Operaciones / Operacion [@ Prioridad ='Media'] | / ConvenioAladi / Operaciones / Operacion [@ Prioridad ='Baja']'

我总是得到原始的XML,无论如何实现我之前提到过的东西?感谢

2 个答案:

答案 0 :(得分:2)

您使用的版本中的XPath按顺序匹配节点,您无法更改。

有几种方法可以实现它:

XSLT

一种简单的方法是执行XSL Transform

一旦你学习了XSLT,就可以很容易地做到这一点。如:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:template match="Operaciones">
        <xsl:copy>
            <xsl:apply-templates select="Operacion">
                <xsl:sort select="index-of(('Alta','Media','Baja'), @Prioridad)"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

然后您可以轻松查询所有Operaciones。如果您使用XSLT路由,则意味着您可以升级查询而无需重新编译软件。这可能是您希望利用的解决方案。

更新:Mathias指出我们并不是在寻找字母排序。我将保留以下解决方案以供参考,但现在我建议您使用上面正确的XSLT解决方案。

的XPathDocument

第二种方式更具编程性,它利用了微软自己的功能:

假设docXmlDocument,您可以执行以下操作将其转换为XPathDocument

XPathDocument xpathDoc = new XPathDocument(new XmlNodeReader(doc));

执行此操作后,您可以运行以下查询:

XPathNavigator nav = xpathDoc.CreateNavigator();
XPathExpression expression = XPathExpression.Compile(@"//Operacion");
expression.AddSort(@"@Prioridad", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Text);
XPathNodeIterator iterator = nav.Select(expression);
foreach (XPathNavigator operation in iterator) {
    Console.WriteLine("Found priority '{0}'",operation.GetAttribute("Prioridad",""));
}

的LINQ

第三种方式,使用Linq(你可能想在这里查看语法):

XDocument xDoc = XDocument.Load(new XmlNodeReader(doc));
var operations = xDoc.Descendants("Operacion").OrderBy(s=>(string)s.Attribute("Prioridad"));
foreach(var operation in operations) {
    Console.WriteLine("hey -> {0}", operation);
}

我建议您通过Linq进行,但如果您要转换数据,那么每次都要使用XSLT。

答案 1 :(得分:0)

如前所述,这里有一些选择。为了具体起见,这里有一个完整的XSLT(1.0)解决方案,到目前为止只是暗示过:

<?xml version='1.0' encoding='UTF-8'?>

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

  <xsl:template match="*">
    <xsl:variable name="n" select="name (.)"/>
    <xsl:element name="{$n}">
      <xsl:for-each select="@*">
        <xsl:copy-of select="."/>
      </xsl:for-each>
      <xsl:apply-templates select="*"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="/">
    <xsl:apply-templates select="ConvenioAladi"/>
  </xsl:template>

  <xsl:template match="Operaciones">
    <xsl:apply-templates select="Operacion[@Prioridad = &quot;Alta&quot;]"/>
    <xsl:apply-templates select="Operacion[@Prioridad = &quot;Media&quot;]"/>
    <xsl:apply-templates select="Operacion[@Prioridad = &quot;Baja&quot;]"/>
  </xsl:template>
</xsl:stylesheet>

您可以将此样式表应用于XML,并按OperacionAltaMedia的顺序对Baja个节点进行排序。它相当天真地进行:它只查找具有Operacion属性值Alta的任何Prioridad,并且(基本上)将其复制到输出。然后是Media,然后是Baja。即使没有Operacion个节点的Baja属性具有值Prioridad(并且其他两个值类似),它也会起作用。样式表确实隐含地假设这三个是Prioridad属性的唯一可能值(如果有这样的节点,它们会被静默忽略),所以如果你有Operacion个节点可能有其他节点Prioridad属性的值,需要调整样式表。