2013-07-18 72 views
1

我有一個XML看起來像這樣的節點:XPath來選擇匹配

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

我需要返回所有的Period節點這些條件匹配的XPath:

  • 節點CodeOut/CodeIn具有我在一個數組中的任何值的值
  • 此節點CodeOut可以被命名爲CodeOutCodeIn,但只有一個SE
  • TimePeriod的日期必須符合

是repeates在XML中的唯一節點是Series節點。換句話說,每個Series只有一個Period,但是有很多不同Series

例如,獲得其中有他CodeoutCodeIn值全部Period節點是C41073B85028以及日期爲2013-07-18

我想,以匹配多個名稱,使用類似:

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

,但我認爲這將是更好的,如果只匹配正確的節點,以防某個節點具有相同的值,ISN是嗎?

我正在尋找使用類似「contains」的東西,但它以不同的方式工作。

我使用.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

TimePeriod元素上'v'屬性的值看起來不是標準的xml數據類型值。我認爲這是兩個用斜線分隔的日期/時間值(開始時間和結束時間)? –

+0

是的。我所做的是使用'/'分割。我不知道它是否可能與xPath,即時通訊正在尋找它。 – SysDragon

+0

您是否需要使用XPath?爲什麼不使用Linq到XML? –

回答

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會有點問題,因爲它似乎是一個非標準的時間範圍值。如果開始和結束時間存儲在單獨的節點中,則會更容易。

如果你需要做的是匹配的精確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代碼中處理時間範圍比較可能是最好的。

+0

所以,實際上,最好的選擇是把每個代碼兩次? (用於'CodeOut'和'CodeIn')?而且我認爲沒有'contains'這樣的東西來檢查很多值,而不是用'OR'來檢查每個值。 – SysDragon

+0

我更新了我的答案,以演示如何使用'contains'函數。老實說,雖然這有點古怪,但並不是更加優雅,所以,我個人只是堅持第一個例子。 –

+0

由於您正在搜索兩個不同的元素名稱,所以我無法想到避免兩次列出項目的方法。也許有比我更多的XPath專業知識的人可以想到一個很好的方法來做到這一點。如果是這樣,我會很好奇看到它:) –