我有一个看起来像这样的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
可以命名为CodeOut
或CodeIn
,但只能指定其中一个TimePeriod
上的日期必须匹配在xml上重复的唯一节点是Series
节点。换句话说,每Period
只有一个Series
,但有很多不同的Series
。
例如,将Period
或Codeout
值为CodeIn
或C41073
的所有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?)
答案 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代码中处理时间范围比较。