2

我有一个如下所示的 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可以命名为CodeOutor CodeIn,但只能命名为其中之一
  • 日期TimePeriod必须匹配

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

例如,获取PeriodCodeoutCodeIn值为 beC41073B85028日期为 be 的所有节点2013-07-18

我尝试使用以下方法匹配多个名称:

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

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

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

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


编辑:

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

This:doc.SelectNodes("/*")(0).Name 正在返回RootName
This:doc.SelectNodes("/*/*").Count 正在返回912
This: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")

ns:在 xPath 序列中的每个元素节点名称之前添加。(有关它的更多信息,请参阅此内容:是否可以在 xpath 表达式中仅指定一次命名空间前缀?

4

1 回答 1

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, '/')and分割字符上的字符串substring-after(TimePeriod, '/'),但除非您使用 XPath 2.0,否则您无法比较字符串以查看它们是大于还是小于。如果您使用的是 2.0,则可以使用该compare函数将这些子字符串中的每一个与搜索值进行比较,但它仍然很混乱。最好在 .NET 代码中处理时间范围比较。

于 2013-07-18T12:41:46.190 回答