2013-10-14 132 views
0

大家好,我正在使用XPATH和XML.SelectNodes()从XML文件中提取球顶数据,我希望这个数据按照一定的顺序,XML文件是这样的:如何使用XPATH添加节点

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

“/ ConvenioA:

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

我能够获得Prioridad之一通过给XPATH随时属性拉迪/ Operaciones/Operacion [@ Prioridad = '阿尔塔'] '

但如果我尝试这样的事: '/ ConvenioAladi/Operaciones/Operacion [@ Prioridad =' 阿尔塔' 或@ Prioridad ='媒体'或@ Prioridad =' 巴哈 ']'

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

我总是得到原始的XML回来,有没有实现我之前提到的?谢谢

+0

为什么不寻找具有相同属性值和更大位置的下一个兄弟姐妹? –

+0

似乎你正在使用XPath 1.这是不可能的。它只返回一组节点,而不是一个序列 – BeniBela

+0

Ignacio Vazquez-Abrams我该怎么做,你能举个例子吗? –

回答

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

我知道这是一个旧的答案,但您的XSLT代码直到现在才可见,并且不完全正确。标准排序是按字母顺序排列的,在这种情况下没有用。你必须单独应用模板:http://xsltransform.net/nc4NzQD。 –

+0

谢谢@MathiasMüller指出这些问题,非常感谢!我更习惯性地更新了XSLT - 仍然使用单一排序。 –

+1

加1,但请注意,这只会在XSLT 2.0中起作用,因为'index-of()'是一个2.0函数:http://xsltransform.net/pPqsHTF。 –

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和它的顺序Alta排序Operacion节点, MediaBaja。它只是天真地进行:它仅查找具有Prioridad属性值Alta的任何Operacion,并且(基本上)将其复制到输出。然后Media,然后Baja。即使没有Operacion节点的Prioridad属性值Baja(对于其他两个值也是如此),它仍然可以工作。样式表确实假设这三个属性是Prioridad属性的唯一可能值(如果存在这样的节点,它们会被忽略),所以如果您有Operacion节点可能具有Prioridad属性的其他值,则样式表将需要调整。

+0

请停止发布不良的XSLT答案。你的代码可以工作,但是它有很多不必要的结构 - 输出中的元素可以被复制到输出中,不需要生成新的元素。不需要为来自输入的已知属性列表设置“for-each”。如果您将它们用作实际引号,则无需在XSLT中转义引号。 –

+0

这就是说,XSLT代码(直到最近才显示)在这个问题的接受答案中是错误的 - 标准的按字母顺序排序,在这种情况下是无用的。所以,每个人都会从http://xsltransform.net/nc4NzQD的行中获得有用的XSLT答案。 –