首页 文章

XPath选择匹配的节点

提问于
浏览
1

我有一个看起来像这样的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 节点 . 换句话说,每个 Series 只有一个 Period ,但是有很多不同的 Series .

例如,将 CodeoutCodeIn 值的所有 Period 节点设为 C41073B85028 ,日期为 2013-07-18 .

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

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

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

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

我'm using .Net, if that matters, and I' m将在 .SelectNodes() 函数上使用此xPath .


EDIT:

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

这个: 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 回答

  • 1

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

    /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 值,你可以这样做:

    /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代码中处理该时间范围比较 .

相关问题