熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

XPath查詢XML文檔的注意事項

2013-11-13 10:29:49  來源: .NET編程 
老鼠與人最好的排列計劃

  本文的靈感來自一個諸事不順的周末我的那一位決定和同事去拉斯維加斯進行一次慶祝旅行恰好我計劃去宜家家居挑一個書櫃這樣我在搬到雷得蒙幾個月之後終於可以將我的書松綁了在宜家家居逛了兩個小時之後我發現一個陳列的書櫃與我房間的色調很相配只是一些必要的配件缺貨最終我訂購了這種書櫃空著手先回家了然而我已經把書都解開了它們散落在房間的各個角落這成為整理我日益增長的書庫的最好機會當然我選擇使用 XML 實現此任務(本文包含一些指向英文站點的鏈接

  不僅是表面那麼簡單

  我正在建造的 XML 分類的主要目的是建立一個結構來集中存儲我所擁有的書的信息它應該足夠靈活可以進行查詢和各種演示同時也便於攜帶下面是該文檔的初稿的摘錄

  Booksxml

  <?xml version= encoding=UTF ?>
<bk:books xmlns:bk=urn:xmlns:hoursadaycom:mybookshelf onloan=yes
<bk:book publisher=IDG books onloan=Sanjay
<bk:title>XML Bible</bk:title>
<bk:author>Elliotte Rusty Harold</bk:author>
</bk:book>
<bk:book publisher=QUE
<bk:title>XML By Example</bk:title>
<bk:author>Benoit Marchal</bk:author>
</bk:book>
</bk:books>

    我希望能夠使用 onloan 屬性來跟蹤我是否將書借了出去根元素上的 onloan 屬性指定至少一本書被借了出去然後將每個 book 元素上相同的屬性指定借書人現在看來這可能不是最好的設計因為它導致在根元素和子元素之間不必要的聯系但是請諸位諒解這畢竟只是我的初稿

  設計好簡單的格式後我決定在上面運行一些實際的查詢來看看這種格式是否滿足我的需要我嘗試在 SystemXmlXmlNode 類中使用 SelectSingleNode Method 的第一個查詢如下

  //*[position() = ]/@onloan

  我本來的意思是選擇文檔中的所有節點然後給我第一個節點的 onloan 屬性查詢返回以下內容

  onloan=yes

  因此對問題我是否有書借出?的答案是 yes然而當我模擬如果一本書被借出之後我沒有更新根元素上的 onloan 值會出現什麼情況時發生了一件有趣的事情我從根元素中刪除 onloan 屬性再次運行查詢結果如下

  onloan=Sanjay

    結果是根元素的某個子元素的值我懷疑有錯所以又在 MSXML 上嘗試查詢還是得到相似的結果進一步研究使我與小組內的一些 XPath 專家進行了很有啟發意義的討論並進一步閱讀了 XPath 建議我發現與多方共同設計的重要語言一樣有一些古怪富有個性及不一致的情況(即在處理 XPath 時需要避免的陷阱)

  縮寫及它們的真實意思

  XPath 建議列出了一些軸這些軸包含了一些與當前選擇節點(也稱為上下文節點)相關的節點為避免冗長指定了某些常用軸的一些縮寫下表顯示這些縮寫和它們等效的軸

   縮寫 軸 self::node() parent::node() // /descendentorself::node()/ @ attribute::

  另一個事實是在每個位置步驟或路徑表達式上使用的默認軸是 child:: 軸因此/bk:books/bk:book 實際等效於 /child::bk:book/child::bk:book 這比直接鍵入要容易得多

  節點測試用來選擇當前軸的主節點類型的所有節點* 是節點測試不是步驟的縮寫最後帶有數字的謂詞等效於檢查上下文節點的位置是否與該數字相同這意味著查詢 /bk:book[] 等效於 /bk:book[position()=]

  有了以上信息我們可以返回原來出問題的查詢看看為什麼會得到意外的結果//*[position() = ]/@onloan 實際是 /descendentorself::node()/child::*[position() = ]/@onloan 的縮寫它選擇文檔中的每個節點檢索每個選定節點的第一個子節點的 onloan 屬性明智地使用圓括號可以迅速解決此問題(//*)[position() = ]/@onloan(它是 (/descendentorself::node()/child::*)[position() = ]/@onloan 的縮寫) 是我實際想要的

  有趣的是在問題解決之後不久我意識到實現我要求的更簡單和更有效的查詢本來可以是

  /*/@onloan

  這是一個更好的解決方案因為它只需要查看文檔中的第一個節點我將保留多個示例強調為什麼一個人應該考慮在某些情況下縮寫所表示的內容以避免令人迷惑不解的結果

   縮寫 完整查詢 查詢結果 //*[] /descendentorself::node()/child::*[position()=] 選擇文檔中每個節點的第一個子節點 (//*)[] (/descendentorself::node()/child::*)[position()=] 選擇文檔中第一個節點

  提高我們的數學技能

  涉及關系或算術運算符和字符串的查詢通常導致與直覺不相符的結果XPath 將涉及關系或算術運算符的表達式中的所有操作數轉換為數字不完全是數字值的字符串將轉換為 NaN(不是一個數)下表顯示某些 XPath 表達式表達式隱式轉換成的內容以及表達式的結果

   表達式 隱式轉換 結果 + + + + + a + NaN NaN True True b < NaN False ab NaN < NaN False ab NaN > NaN False

  必須注意到比較運算符(<<=>=)不執行字符串值的字典式比較功能

  另一個有趣的算術定義是雖然定義了一元減號(例如 是有效的 XPath 表達式)但是卻未定義一元加號(+ 不是有效的 XPath 表達式)更令人吃驚的是多重否定可以堆疊在一起卻仍然有效因此 是有效的 XPath 表達式等效於值

  XPath 缺乏對科學/指數記數法的支持將使用戶犯錯因為支持它的既有流行的查詢語言(如 SQL)也有流行的編程語言(如 C++)

  在節點集合上結合算術和關系運算的表達式還可能導致令人吃驚的結果節點集合上的算術運算將集合中第一個節點的值轉換為數字而關系運算符將判斷節點集合中的任意節點是否滿足條件下面是一個 XML 文檔用來顯示算術運算和關系運算符如何導致不 Associative(結合)的表達式

  NumbersXML

  <Root>
<Numbers>
<Integer value= />
<Integer value= />
<Integer value= />
</Numbers>
<Numbers>
<Integer value= />
<Integer value= />
<Integer value= />
</Numbers>
</Root>

  下表顯示缺乏結合性的算術運算

表達式 結果 解釋 Root/Numbers[Integer/@value > ] <Numbers>

  <Integer value= />

  <Integer value= />

  <Integer value= />

  </Numbers>

  <Numbers>

  <Integer value= />

  <Integer value= />

  <Integer value= />

  </Numbers>

選擇文檔中的所有 <Numbers> 元素其中至少一個<Integer> 元素具有值大於 的 value 屬性 Root/Numbers[ + Integer/@value > ] <Numbers>

  <Integer value= />

  <Integer value= />

  <Integer value= />

  </Numbers>

選擇文檔中的所有 <Numbers> 元素其中 加上具有值大於 的 value 屬性的第一個<Integer> 元素

  如果 XPath 是代數結合的則兩種查詢將返回同樣的結果

  何時集合不是一個集合?

  雖然節點集合是無序的集合就象數學(或您喜歡的編程語言)中的集合一樣但是處理它們通常與處理數學意義上的集合不同XPath 中的某些操作在處理節點集合時使用第一語義而其他操作使用任意語義第一語義意味著該操作的節點集合的值從集合中的第一個節點獲得任意語義則意味著節點集合中的操作取決於集合中的任何節點是否滿足該條件標題為提高數學技能的小節將介紹使用任意第一語義的情況

  XPath 節點集合與數學集合不同的另一個特征是 XPath 不直接提供機制以執行集合操作(如子集交集或對稱差集)Michael Kay(XSLT Programmers Reference nd edition 的作者)最早發現如何使用 count() 函數和聯合運算符 | 來模擬缺少的集合運算符下面列出了對上面一節中的 XML 文檔執行集合操作的 XSLT 樣式表及其輸出

  樣式表

  <xsl:stylesheet xmlns:xsl= version=
<xsl:output method=text />
<xsl:variable name=a select=/Root/Numbers[]/Integer/@value/>
<xsl:variable name=b select=/Root/Numbers[]/Integer/@value[]/>
<xsl:variable name=c select=/Root/Numbers[]/Integer/@value[ = ]/>
<xsl:template match=/
SET A: { <xsl:foreach select=$a> <xsl:valueof select= /> </xsl:foreach> }
SET B: { <xsl:foreach select=$b> <xsl:valueof select= /> </xsl:foreach> }
SET C: { <xsl:foreach select=$c> <xsl:valueof select= /> </xsl:foreach> }
a UNION b: { <xsl:foreach select=$a | $b> <xsl:valueof select=
/> </xsl:foreach> }
b UNION c: { <xsl:foreach select=$b | $c> <xsl:valueof select=
/> </xsl:foreach> }
a INTERSECTION b: { <xsl:foreach select=$a[count(|$b) = count($b)]
<xsl:valueof select= /> </xsl:foreach> }
a INTERSECTION c: { <xsl:foreach select=$a[count(|$c) = count($c)]
<xsl:valueof select= /> </xsl:foreach> }
a DIFFERENCE b: { <xsl:foreach select=$a[count(|$b) != count($b)] |
$b[count(|$a) != count($a)]> <xsl:valueof select= /> </xsl:foreach> }
a DIFFERENCE c: { <xsl:foreach select=$a[count(|$c) != count($c)] |
$c[count(|$a) != count($a)]> <xsl:valueof select= /> </xsl:foreach> }
a SUBSET OF b: { <xsl:valueof select=count($b | $a) = count($b)/> }
b SUBSET OF a: { <xsl:valueof select=count($b | $a) = count($a)/> }
</xsl:template>
</xsl:stylesheet> 

  輸出

  SET A: { }
SET B: { }
SET C: { }
a UNION b: { }
b UNION c: { }
a INTERSECTION b: { }
a INTERSECTION c: { }
a DIFFERENCE b: { }
a DIFFERENCE c: { }
a SUBSET OF b: { false }
b SUBSET OF a: { true }  

  節點集合和數學集合之間差異的最後一點是節點集合通常是有序的WC XPath 建議將它們描繪為無序的但是 XSLT 確實指定了節點集合的順序

  標識危機

  在 XPath 中沒有直接確定節點標識或不同節點集合中的等效節點的構造不直接支持比較例如由 /bk:books 返回的節點是否與由 /bk:books/bk:book[]/parent::* 返回的節點相同在節點集合上使用 = 運算符的比較不將節點集合作為一個整體進行比較而是使用任意語義從 WC XPath 建議

  如果要比較的兩個對象都是節點集合則當且僅當第一個節點集合中有一個節點且第二個節點集合中有一個節點時該比較才為 true這樣在兩個節點的字符串值上進行比較的結果才為 true

  為了解釋清楚這一點以下是顯示從簡介的 XML 分類格式中執行有關節點集合比較操作結果的表格請注意這些初看起來就象相互矛盾的結果

表達式 結果 解釋 //bk:book = /bk:books/bk:book[] TRUE 是否 //bk:book 中至少有一個節點與 /bk:books/bk:book[] 中的另一個節點具有同樣的字符串值? //bk:book != /bk:books/bk:book[] TRUE 是否 //bk:book 中至少有一個節點與 /bk:books/bk:book[] 中的另一個節點具有不同的字符串值? not(//bk:book = /bk:books/bk:book[]) FALSE 問題是否//bk:book 中至少有一個節點與 /bk:books/bk:book[] 中的另一個節點具有同樣的字符串值?的相反答案

  可以使用 XPath count() 函數模仿節點標識判斷相同長度的兩個節點集合的相交部分是否是任意節點集合的同樣長度或者在單一元素節點集合的情況下是否等於 例如以下查詢在這種情況下返回 TRUE因為兩個節點都是相同的

  count(/bk:books | /bk:books/bk:book[]/parent::*) =

  也可以使用 XSLT 中的 generateid() 函數模仿節點標識XSLT FAQ 提供使用 generateid() 的示例

  我是故我在

  雖然沒有測試節點存在的明確機制但是它確實在涉及節點集合的一些表達式中隱式發生了不存在的節點集合表示為空節點集合在分別涉及字符串和數值操作的情況下空節點集合隱式轉換為空的字符串或 NaN如果執行查詢時沒有查看實例文檔從而未確定空節點集合導致發生了(以及沒有發生)哪些實例系列隱式轉換可能導致令人混淆的結果下面是查詢的一些示例涉及空節點集合以及這些隱式轉換如何影響它們

   表達式 結果 /NonExistentNode + NaN /NonExistentNode = False /NonExistentNode != False concat(/NonExistentNode hello) hello /Root[@nonExistentAttribute] 不返回結果 /Root[@nonExistentAttribute < ] 不返回結果 /Root[@nonExistentAttribute > ] 不返回結果

  因為節點可能包含空的字符串通常最好使用 boolean() 函數而不是通過檢查節點的字符串值來測試節點的存在例如以下查詢(返回 FALSE)是肯定地告訴您文檔中沒有 NonExistentNode 的最好方法   boolean(/NonExistentNode)

  命名空間和 XPath Redux

  在 XPath 中處理命名空間時的缺陷這個缺陷涉及到即使文檔使用默認的命名空間也必須在表達式中映射前綴和命名空間名稱

  有趣的是對於一個文檔總有至少一個命名空間節點可以使用XML 命名空間 例如看看以下查詢

  /bk:books/namespace::*

  該查詢將返回以下內容

  urn:xmlns:hoursadaycom:mybookshelf

  

  返回的項是 booksxml 文檔根處提供的命名空間節點

  未涉及的內容

  XML 文檔中的某些信息是透明的或者在某些情況下對 XPath 是不可見的XML 文檔頂部的 XML 聲明就是一種 XPath 看不到的 XML 構造這意味著不能通過 XPath 來查詢 XML 文檔的版本編碼或獨立狀態

  用於引入在分析 XML 文檔的過程中替換的文本的語法構造(例如 CDATA 節和分析的實體)對 XPath 同樣是透明的XPath 將替換文本作為常規文本節點進行處理


From:http://tw.wingwit.com/Article/program/net/201311/13811.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.