共用方式為


嗨,Scripting Guy!有名的遺言

Microsoft Scripting Guy

著名的希臘哲學家蘇格拉底 (歿於西元前 399 年,時值 Scripting Editor 誕生之前不久) 最家喻戶曉的名言或許是「未經審視的人生不值得一活」。很多人都聽過這句話。不過值得一提的是,Scripting Guy 最近發現蘇格拉底從未說過「未經審視的人生不值得一活」。其實是數百年前中世紀的抄寫者翻譯錯了。事實上蘇格拉底說的是:「未經審視的 XML 檔案不值得擁有」。

這麼一來,哲學名言總算有點道理了!XML 如今越來越普及;隨便搜尋一下某位 Scripting Guy 的測試電腦就可以找到 500 個以上由各式各樣系統或應用程式使用的 XML 檔案。

不用說,許多非常重要的資料也是以 XML 格式儲存。這麼做沒什麼問題,除非是這些資料未經審視。不幸的是,情況往往如此,只因為:很多人連怎麼審視 XML 檔案都不清楚。尤其是他們不知道如何查詢及搜尋 XML 檔案。

附註 如果您不太熟悉希臘哲學家蘇格拉底喜歡說多於做;事實上,他的妻子詹蒂碧曾說他是「無用的閒人」。因此也不難了解,Scripting Guy 為什麼對蘇格拉底心有戚戚焉。

當然,有些讀者可能會想:「等一等:Scripting Guy 不是已經在過去的專欄 (《對於追車 … 還有 XML 的迷戀》,網址是:technet.microsoft.com/magazine/cc162506) 中討論過如何搜尋 XML 檔案了?既然如此,為何現在要重炒這個話題;難道 Scripting Guy 真得這麼懶,想要一再重寫同樣的專欄?

信不信由您,我們比您想像的更懶。但這並非我們重寫此主題的首要原因。在之前的專欄中,我們所處理的 XML 檔案結構就像 [圖 1] 中的程式碼一樣,每個屬性值都代表檔案中的單一節點。

[圖 1] 只包含節點的 XML 檔案

<?xml version='1.0'?> 
  <INVENTORY> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Human Resources
      </department>
      <name>atl-ws-001</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows XP</os>
      <department>Finance</department>
      <name>atl-ws-002</name>
    </COMPUTER> 
    <COMPUTER>
      <os>Windows Vista</os>
      <department>Finance</department>
      <name>atl-ws-003</name>
    </COMPUTER> 
  </INVENTORY>

看起來還不錯,只不過很快就有人寫信來指出,這並非唯一一種建構 XML 檔案的方式。在上例中,檔案中的每個電腦都有自己的節點;而每個節點都有一些子節點 (作業系統、部門及名稱)。但是 XML 檔案也可以建構成不含任何子節點的個別節點;額外的屬性值則設成屬性,就像 [圖 2] 中的檔案一樣。

[圖 2] 包含屬性的 XML 檔案

<?xml version='1.0'?> 
  <HARDWARE> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-001</COMPUTER> 
      <COMPUTER os="Windows XP" department="Finance">atl-ws-002</COMPUTER> 
      <COMPUTER os="Windows Server 2003" department="IT">atl-fs-003</COMPUTER> 
      <COMPUTER os="Windows Vista" department="IT">atl-ws-004</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Human Resources">atl-ws-005</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Finance">atl-ws-006</COMPUTER> 
      <COMPUTER os="Windows XP" department="Sales">atl-ws-007</COMPUTER> 
      <COMPUTER os="Windows Server 2008" department="IT">atl-fs-008</COMPUTER> 
      <COMPUTER os="Windows XP" department="Human Resources">atl-ws-009</COMPUTER> 
      <COMPUTER os="Windows Vista" department="Sales">atl-ws-010</COMPUTER> 
  </HARDWARE>

那會造成問題嗎?事實上會的。在很久很久以前的專欄中,我們所示範的指令碼無法處理類似 [圖 2] 中的結構的 XML 檔案。無論如何也辦不到。這樣一定會產生問題,因為很多人都需要讀取這種類型的 XML 檔案。

只要您開口,我們就為您解決問題。呃,除此之外,還要等一年半,我們才會找出解決這個問題的辦法。

深入剖析之前,先來仔細看一下我們的 XML 檔案。在本例中,我們有一個檔案包含名為 HARDWARE 的主要節點;每個個別電腦都以 HARDWARE 的子節點的身份存在。此外,這些節點分別都有一對屬性:作業系統 (用來儲存電腦上安裝的作業系統名稱) 與部門 (用來儲存擁有電腦的部門名稱)。

舉例來說,假設我們想要取得所有執行 Windows XP 的電腦清單。可以使用指令碼來辦到嗎?當然沒問題:

Set xmlDoc = _
  CreateObject("Microsoft.XMLDOM")

xmlDoc.Async = "False"
xmlDoc.Load("HARDWARE.xml")

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

我們來看一下這段程式碼。一開始,我們建立 Microsoft.XMLDOM 物件的執行個體;顧名思義,這是讓我們使用 XML 檔案的物件。建立好物件之後,接下來將 Async 屬性設為 False;這樣指令碼就知道我們要同步 (而不是非同步) 載入文件。指令碼為何會在意這點?老實說,它根本不在意;畢竟所有指令碼 (就像 Scripting Guy 一樣) 都是無生命的物件。

不過您應該在意。如果我們非同步載入文件,即使文件尚未完全載入,指令碼仍會繼續執行。這樣可不妙:想像一下,如果您嘗試在還不存在的文件上執行作業,那有多糟糕。同步載入 XML 檔案可確保在指令碼繼續執行之前,已經完全載入檔案。

巧的是,我們現在剛好要載入 XML 檔案了。方法是呼叫 Load 方法並開啟檔案 C:\Scripts\HARDWARE.xml,也就是使用以下這行程式碼:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER[@os='Windows XP']")

我們在此是使用 SelectNodes 方法來查詢 XML 檔案,並告訴指令碼要擷取哪個 XML 節點。要注意的是,這裡的語法的確有點與眾不同。

一開始,我們需要指定 XML 檔案內的路徑。我們的 XML 檔案有一個最上層節點,稱為 HARDWARE,其下是一系列的第二層節點,稱為 COMPUTER。這些第二層節點分別代表 XML 資料檔案中的單筆記錄。因此我們的 selectNodes 查詢開頭就像這樣:

//HARDWARE/COMPUTER

這時我們遇到此建構函式:

[@os='Windows XP']

這個查詢部分類似標準 SQL 查詢中的 WHERE 子句。在 SQL 中,我們可能使用與此類似的資料庫查詢來擷取所有執行 Windows XP 作業系統的電腦清單:

SELECT Name FROM Hardware 
    WHERE OS = 'Windows XP'

我們使用 SelectNodes 方法來進行相似的動作:我們要求取得 os 屬性 (@os) 等於 Windows XP 的所有電腦清單。為了指定這是 WHERE 子句,我們用方括弧括住整個子句。

附註 這種查詢類別稱為 XPath 查詢。若要進一步了解 XPath,請參閱 msdn.microsoft.com/library/ms256115.aspx

我們說過,它看起來有些怪異,但的確有效。如果我們想要取得屬於財務部門的所有電腦清單呢?這沒什麼大不了的;我們對 selectNodes 的呼叫看起來像這樣:

Set colNodes=xmlDoc.selectNodes _
  ("//HARDWARE/COMPUTER" & _
   "[@department='Finance']")

同樣地,WHERE 子句也是以方括弧夾住,我們在屬性名稱 (department) 前面加上符號 (@),接著指定所要的值。

很簡單吧!

呼叫 selectNodes 方法後,接下來我們可以重複檢查傳回值集合,並回應 Text 屬性,藉此來回應電腦名稱,如下所示:

For Each objNode in colNodes
  Wscript.Echo objNode.Text 
Next

如此一來,我們可望取得什麼樣的資訊?說實話,我們完全能預期取得如下的資訊:

atl-ws-001
atl-ws-002
atl-ws-007
atl-ws-009

換句話說,它會傳回仍然執行 Windows XP 的所有電腦名稱。

附帶一提,您並不只限於回報電腦名稱;由於電腦名稱是每個節點的「預設」值,如果我們參考 Text 屬性,就會直接取得電腦名稱。

或者,您可以明確指定所要傳回的屬性值。例如,看一下這個修改過的 For Each 迴圈:

For Each objNode in colNodes
    Wscript.Echo objNode.Text 
    Wscript.Echo objNode.Attributes. _
      getNamedItem("department").Text
    Wscript.Echo
Next

如您所見,在此迴圈中,我們依舊回應 Text 屬性的值;然而,我們也附加了這一行程式碼:

Wscript.Echo objNode.Attributes. _
  getNamedItem("department").Text

在本例中,我們使用 getNamedItem 方法來擷取 department 屬性的值;接著回應該屬性的 Text 屬性。我們就是透過這種方法來指定要 (和不要) 回應到螢幕的屬性值 (另外也請注意,我們加入 Wscript.Echo 命令以便在各筆記錄間插入空白行)。跑過這個指令碼後,會得到下列結果:

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

沒錯,您可以選擇撰寫更複雜的查詢。舉例來說,假設您想要取得所有執行 Windows XP 或 Windows Vista 的電腦清單。在 SQL 中,您撰寫 OR 查詢來達到這個目的。您猜怎麼著?查詢 XML 檔案的作法也如出一轍:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' or " & _
     "@os='Windows Vista']")

看出來我們怎麼辦到的嗎?我們在單獨一組方括弧中提供兩個條件:@os='Windows XP' 或 @os='Windows Vista'。您只需要這麼做,就能取得如 [圖 3] 所示的報告。

[圖 3] OR 查詢結果

atl-ws-001
Human Resources

atl-ws-002
Finance

atl-ws-004
IT

atl-ws-005
Human Resources

atl-ws-006
Finance

atl-ws-007
Sales

atl-ws-009
Human Resources

atl-ws-010
Sales

如果您仔細查看 XML 檔案,將會看見執行 Windows XP 或 Windows Vista 的電腦都包含在輸出中;執行 Windows Server 2003 或 Windows Server 2008 的電腦則不在輸出中,而且它們不應該出現在輸出中。

您有疑問嗎?您想撰寫限制條件更嚴格的查詢,只傳回執行 Windows XP 且屬於財務部門的電腦?您可能已經猜到了,這個工作很簡單;您只需要撰寫如下的 AND 查詢就行了:

Set colNodes=xmlDoc.selectNodes _
    ("//HARDWARE/COMPUTER" & _
     "[@os='Windows XP' and " & _
     "@department='Finance']")

這樣會傳回下列資料集:

atl-ws-002
Finance

為什麼報告中只有一個項目?說得對:因為財務部門只有一台執行 Windows XP 的電腦。

希望我們提供的資訊能協助大家處理這種特殊的 XML 檔案類型。如果暗地裡還有其他 XML 檔案類型,那就自求多福囉。

既然本月專欄是以一句名言作為開場白,那麼專欄最後同樣也該以名言收尾。因此我們要以 Scripting Guy 心愛的威廉大帝的名言作為總結:「朕於此宣布永久放棄普魯士國王及與之相連的德皇王位」。

Dr. Scripto 的指令碼謎題 (Scripting Perplexer)

這個每月一次的挑戰不僅測試您的解謎功力,更要測試您的指令碼技巧。

2008 年 10 月VBScript 填填看

為了解決這道謎題,請在每一列中填入 VBScript 函式名稱。完成時,組合藍色方格中的字母將出現另一個函式名稱。

fig10.gif

答案是:

Dr. Scripto 的指令碼謎題 (Scripting Perplexer)

答案是:2008 年 10 月VBScript 填填看

puzzle_answer.gif

The Scripting Guy (Greg Stemp 與 Jean Ross) 為 Microsoft 做事。