XPath选择匹配的节点

时间:2013-07-18 11:54:37

标签: c# .net xml vb.net xpath

我有一个看起来像这样的XML:

<?xml version="1.0"?>
<RootName>
  <RandomNode v="someValue"/>
  <Series>
    <Idendity v="C16"/>
    <CodeOut v="C41073"/>
    <Period>
      <TimePeriod v="2013-07-18T22:00Z/2013-07-19T22:00Z"/>
      <Resolution v="PT60M"/>
      <Interval>
        <Pos v="1"/>
        <Qty v="14.1"/>
      </Interval>
      <Interval>
        <Pos v="2"/>
        <Qty v="20.7"/>
      </Interval>

我需要一个xPath来返回符合这些条件的所有Period个节点:

  • 节点CodeOut / CodeIn具有数组中任何值的值
  • 此节点CodeOut可以命名为CodeOutCodeIn,但只能指定其中一个
  • TimePeriod上的日期必须匹配

在xml上重复的唯一节点是Series节点。换句话说,每Period只有一个Series,但有很多不同的Series

例如,将PeriodCodeout值为CodeInC41073的所有B85028个节点都设为2013-07-18,日期为{{1} }。

我尝试使用以下内容匹配多个名称:

//*[@v="C41073"] | //*[@v="B85028"] | ...

但我认为如果仅匹配正确的节点会更好,以防其他节点具有相同的值,不是吗?

我正在寻找使用类似“包含”的东西,但它以不同的方式工作。

我正在使用.Net,如果这很重要,我将在.SelectNodes()函数上使用此xPath。


修改

发生了一些奇怪的事情。也许语法不正确。看看这个测试:

这:doc.SelectNodes("/*")(0).Name正在返回RootName
这:doc.SelectNodes("/*/*").Count正在返回912
这:doc.SelectNodes("/*/*")(11).Name正在返回Series

但是:doc.SelectNodes("/RootName").Count正在返回0
这:doc.SelectNodes("/*/Series").Count正在返回0
而且:doc.SelectNodes("/*/RootName").Count正在返回0

使答案中建议的所有其他xPath序列不起作用。

编辑:

好的,这是命名空间,我这样做了:

Dim xmlnsManager As Xml.XmlNamespaceManager = New System.Xml.XmlNamespaceManager(doc.NameTable)
xmlnsManager.AddNamespace("ns", "http://example")

在xPath序列中的每个元素节点名称之前添加ns:。 (有关详细信息,请参阅此处:Is it possible to specify the namespace prefix just once in a xpath expression?

1 个答案:

答案 0 :(得分:1)

要选择仅受Period / CodeIn列表限制的所有CodeOut元素,您可以执行以下操作:

/RootName/Series[(CodeOut/@v = 'C41073') or (CodeOut/@v = 'B85028') or (CodeIn/@v = 'C41073') or (CodeIn/@v = 'B85028')]/Period

如果您不想将列表中的每个项目列为单独的条件,您可以将它们全部连接到一个分隔列表中,然后使用contains函数,如下所示:

/RootName/Series[(CodeOut/@v and contains('|C41073|B85028|', concat('|', CodeOut/@v, '|'))) or (CodeIn/@v and contains('|C41073|B85028|', concat('|', CodeIn/@v, '|')))]/Period

注意,为了避免像C4这样匹配整个值的子字符串(例如C41073)的问题,您需要在属性值之前和之后连接分隔符。此外,您需要确保分隔符存在于分隔的值列表的开头和结尾处。此外,您选择的任何分隔符必须是无效字符,该字符在列表中的任何值中都不会出现。

然而,由TimePeriod限制它将会有点问题,因为它似乎是一个非标准的时间范围值。如果开始和结束时间存储在单独的节点中,那将更容易。

例如,如果您只需匹配精确的TimePeriod值,则可以执行以下操作:

/RootName/Series[(CodeOut/@v = 'C41073') or (CodeOut/@v = 'B85028') or (CodeIn/@v = 'C41073') or (CodeIn/@v = 'B85028')]/Period[TimePeriod/@v = '2013-07-18T22:00Z/2013-07-19T22:00Z']

您可以使用/substring-before(TimePeriod, '/')拆分substring-after(TimePeriod, '/')字符上的字符串,但除非使用XPath 2.0,否则无法比较字符串以查看它们是否更大或者不到。如果您使用的是2.0,则可以使用compare函数将每个子字符串与搜索值进行比较,但它仍然很混乱。最好在.NET代码中处理时间范围比较。