2009年4月29日

XPath運算子與特殊字元

XPath 運算式是使用下列表格所顯示的運算子和特殊字元建構的。
/子運算子;選取左側集合的子系。當這個路徑運算子出現在模式開頭時,就表示應該從根節點選取子系。
//遞迴下降;搜尋任何深度的指定項目。當這個路徑運算子出現在模式開頭時,就表示從根節點遞迴下降。
.指示目前內容。
..目前內容節點的父代。
*萬用字元;選取所有項目,不論項目名稱為何。
@屬性;屬性名稱的前置詞。
@*屬性萬用字元;選取所有屬性,不管名稱為何。
:命名空間分隔符號;分隔命名空間前置詞與項目或屬性名稱。
()用來明確建立優先順序的群組運算。
[]套用篩選條件模式。
[]註標運算子;做為集合內的索引。
+執行加法。
-執行減法。
div根據IEEE754執行浮點數除法。
*執行乘法。
mod傳回捨去除法的餘數。

依下列表格的指示來定義優先順序 (從最高優先順序到最低優先順序)。
優先順序字元內容
1()群組
2[]篩選常式
3///路徑運算

路徑運算子
您可以使用路徑運算子 (/ and //) 來決定特定型別的項目集合。這些運算子會將其引數當做「左側」集合 (執行選取),並當做「右側」集合 (指示要選取的項目)。子系運算子 (/) 會從左側集合的立即子系中選取,子代運算子 (//) 則從左側集合的任意子代中選取。事實上,您可將 // 當做階層的一或多個層級之替代項。
請注意,執行查詢時,路徑運算子會變更內容。使用者可將路徑運算子字串放在一起,以周遊文件樹狀目錄。
範例
運算式表示
author/first-name目前內容節點的<author>項目內部的所有<first-name>項目。
bookstore//title位於<bookstore>項目一或多層深的所有<title>項目(任意子代)。請注意,其不同於下列模式bookstore/*/title。
bookstore/*/title所有當做<bookstore>項目孫代的<title>項目。
bookstore//book/excerpt//emph位於<book>項目的<excerpt>子系之任何位置,以及<bookstore>項目的任何位置之所有<emph>項目:
.//title位於目前內容中一或多層深的所有<title>項目。請注意,唯有在此情況下,才需要使用句點標記法。


萬用字元
藉由替代萬用字元 ( * )集合,即可參考項目而不需使用項目的名稱。不論標籤名稱為何, * 集合都會參考目前內容子系的所有項目。
範例
運算式表示
 author/*  <author> 項目的所有項目子系。
 book/*/last-name 所有當做 <book> 項目孫代的 <last-name> 項目。
 */* 目前內容的所有孫代項目。
 my:book  my 命名空間的 <book> 項目。
 my:*  my 命名空間的所有項目。

請注意,系統不支援模式 *:book 。

屬性
XPath 以 @ 符號表示屬性名稱。系統會平等看待屬性和子項目,並且平等處理兩個類型之間的功能 (可能的話)。
屬性不含子項目,所以將路徑運算子套用到屬性時,就會發生語法錯誤。此外,您無法將索引套用到屬性,因為依照定義,不會定義屬性的順序。
範例
運算式表示
 @style 目前項目內容的 style 屬性。
 price/@exchange 目前內容內部的 <price> 項目之 exchange 屬性。
book/@style所有 <book> 項目的 style 屬性。

請注意,下列範例是無效的,因為屬性不能有任何子系:price/@exchange/total

尋找多個屬性
您可以使用 @* 傳回項目的所有屬性。這對於將屬性看做記錄欄位的應用程式很有用
範例
運算式表示
 @* 目前內容節點的所有屬性。
 @my:*  my 命名空間的所有屬性。其不含 my 命名空間項目的不合格屬性。

請注意,系統不支援模式 @*:title 。

篩選條件子句
藉由將篩選條件子句 [pattern] 新增至集合,可將限制和分支套用至任何集合。篩選條件類似於 SQL WHERE 子句。篩選條件內含模式,稱為「篩選條件模式」。會將篩選條件模式評估為布林值,並且會針對集合的每個項目進行測試。結果集合將略過無法通過篩選條件模式測試的集合之任何項目。
為了方便起見,若將集合放在篩選條件內部,則當集合包含任何成員時,就會產生布林 TRUE;若集合為空,則產生 FALSE。如果存在含子項目 項目,運算式 (例如 author/degree) 就會隱含執行評估為 TRUE 的「集合至布林值」轉換函式。
請注意,運算式的指定層級可出現任意數量的篩選條件。不允許空的篩選條件。
系統必定會根據內容評估篩選條件。換言之,運算式 book[author] 會測試每個找到的 項目是否有 子項目。同樣地,book[author = 'Bob'] 會測試每個找到的 項目是否具有值 Bob 的 子項目。使用句點 (.) 字元也可以檢查內容的值。例如,book[. = 'Trenton'] 會測試在目前內容中找到的每本書籍值是否為 Trenton。
範例
運算式表示
 book[excerpt] 至少包含一個 <excerpt> 項目的所有 <book> 項目。
 book[excerpt]/title 至少包含一個 <excerpt> 項目的 <book> 項目內部的所有 <title> 項目。
 book[excerpt]/author[degree] 至少包含一個 <degree> 項目,並且位於 <book> 項目(至少包含一個 <excerpt> 項目)內部的所有 <author> 項目。
 book[author/degree] 至少包含一個 <author> 項目(內含至少一個 <degree> 子項目)的所有 <book> 項目。
 book[excerpt][title] 至少包含一個 <excerpt> 項目和至少一個 <title> 項目的所有 <book> 項目。

2009年4月28日

XPath詳解

XPath路徑運算式
在本篇下面的內容中你將可以學習到:
路徑運算式語法
相對/絕對路徑
運算式上下文
謂詞(篩選運算式)及軸的概念
運算符及特殊字元
常用運算式實例
函數及說明
這裡給出一個Xml範例。下面的說明及範例都是基於此XML檔:

<?xml version="1.0" encoding="UTF-8"?>
<!-- edited with XMLSpy v2008 rel. 2 sp2 (http://www.altova.com) by Administrator -->
<?xml-stylesheet type="text/xsl" href="messages.xsl"?>
<messages>
 <message id="1">
  <sender>gukaitong@gmail.com</sender>
    <to>anonymous@gmail.com
      <group name="IT">
        <address>111@gmail.com</address>
        <address>222@gmail.com</address>
        <address>aaa@gmail.com</address>
        <address>bbb@gmail.com</address>
        <address>ccc@gmail.com</address>
      </group>
    </to>
  <subject>This is a sample</subject>
  <datetime date="2008-12-11" time="12:00:00" formatted="12/11/2008 12:00AM">
    2008-12-11T12:00:00Z
  </datetime>
                
    Are you interested in? 
    <attachments>
      <attachment id="1">
        <message id="0">
          <sender>anonymous@gmail.com</sender>
          <to>gukaitong@gmail.com</to>
          <body>
            We strongly recommend the following books
            <books xmlns:amazon="http://www.amazon.com/books/schema">
              <amazon:book>
                <name>Professional C# 2008 </name>
                <country>USA</country>
                <price>37.79</price>
                <year>2007</year>
              </amazon:book>
              <amazon:book>
                <name>Microsoft Visual C# 2008 Step by Step </name>
                <country>USA</country>
                <price>26.39 </price>
                <year>2008</year>
              </amazon:book>
              <amazon:book>
                <name>C# in Depth</name>
                <country>USA</country>
                <price>29.69 </price>
                <year>2006</year>
              </amazon:book>
              <amazon:book>
                <name>Thinking in Java</name>
                <country>USA</country>
                <price>23.69 </price>
                <year>2004</year>
              </amazon:book>
            </books>
          </body>
        </message>
      </attachment>
    </attachments>
   </body>
  </message>
  <message id="2">
    <sender>333@gmail.com</sender>
    <to>444@gmail.com</to>
    <subject>No title</subject>
    <body/>
  </message>
</messages>


路徑運算子:
請看一個典型的XPath查詢運算式:/messages/message//child::node()[@id=0],其中/messages/message是路徑(絕對路徑以"/"開始),child::是表示在子節點下選擇,node()是節點測試表示選擇所有的節點。[@id=0]表示選擇所有有屬性id並且值為0的節點。

相對路徑與絕對路徑:
如果"/"在XPath運算式開頭則表示為根節點,(運算式中間作為分隔符號用以分割每一個層級)如:/messages/message/subject是一種絕對路徑表示法,它表明是從根開始查詢節點。假設目前節點是在第一個message節,如:/messages/message[1],則路徑為subject(路徑前沒有"/")這種表示法稱為相對路徑,表明從目前節點開始查詢。

XPath位置路徑運算子:

運算子
範例
說明
./
./sender表示選擇目前節點下的sender節點集合
../
../sender表示選擇目前節點的父節點下的sender節點集合
/
/messages表示選擇從根節點下的messages節點集合
*
/*這裏的*是代表所有節點,/*的結果和/messages的結果一樣都是messages節點
 /*/*/sender表示所有位於第三層的sender
//
/messages//sender <sender>gkt1980@gmail.com</sender>
<sender>111@gmail.com</sender>
<sender>333@gmail.com</sender>
從目前節點開始遞迴搜索其下的所有子節點找到滿足條件的節點集合

什麼是XSLT,什麼是XPath

1. 什麼是XSLT
  
大家可能聽說過XSL(eXtensible Stylesheet Language),XSL和我們這裏說的XSLT從狹義上理解是一樣的,而按照W3C的標準,XSLT的說法更嚴格些,因此我們在文章中統一使用XSLT的稱法。XSLT的英文標準名稱為eXtensible Stylesheet Language Transformation。根據W3C的規範說明書(http://www.w3.org/TR/xslt),最早設計XSLT的用意是幫助XML文件(document)轉換為其他文件。但是隨著發展,XSLT已不僅僅用於將XML轉換為HTML或其他文件格式,更全面的定義應該是: XSLT是一種用來轉換XML文件結構的語言。

  
2. 為什麼要用XSLT
  
我們已經知道,XML是一種電腦程式間交換原始資料的簡單而標準的方法。它的成功並不在於它容易被人們書寫和閱讀,更重要的是,它從根本上解決了應用系統間的資訊交換。因為XML滿足了兩個基本的需求:
(1).將資料和UI分離。就象天氣預報的資訊可以顯示在不同的設備上,電視,手機或者其他。
(2).在不同的應用之間傳輸資料。電子商務資料交換的與日俱增使得這種需求越來越緊迫。
  為了使資料便於人們的閱讀理解,我們需要將資訊顯示出來或者列印出來,例如將資料變成一個HTML檔,一個PDF檔,甚至是一段聲音;同樣,為了使資料適合不同的應用程式,我們必須有能夠將一種資料格式轉換為另一種資料格式,比如需求格式可能是一個文字檔案,一個SQL語句,一個HTTP資訊,一定順序的資料調用等。而XSLT就是我們用來實現這種轉換功能的語言。將XML轉換為HTML,是目前XSLT最主要的功能。


3. 什麼是XPath
  
XPath是XSLT的重要組成部分。那麼XPath是什麼呢?我們首先來瞭解一下XSL系列的"家族"關係。

  XSL在轉換XML文件時分為明顯的兩個過程,第一轉換文件結構;其次將文件格式化輸出。這兩步可以分離開來並單獨處理,因此XSL在發展過程中逐漸分裂為XSLT(結構轉換)和XSL-FO(formatting objects)(格式化輸出)兩種分支語言,其中XSL-FO的作用就類似CSS在HTML中的作用。而我們這裏重點討論的是第一步的轉換過程,也就是XSLT。
  另外,在學習XML時我們已經知道XML是一個完整的樹結構文件。在轉換XML文件時可能需要處理其中的一部分(節點)資料,那麼如何查詢跟定位XML文件中的資訊呢,XPath就是一種專門用來在XML文件中查詢資訊的語言。XPath隸屬XSLT,因此我們通常會將XSLT語法和XPath語法混在一起使用。
  用一種比較好理解的解釋:如果將XML文件看作一個資料庫,XPath就是SQL查詢語言;如果將XML文件看成DOS目錄結構,XPath就是cd,dir等目錄操作命令的集合。

  

2009年4月15日

ASP產生Excel方法

報表是各種管理系統中一個重要的,而輸出的格式中Excel就佔有一席之地。
這裡舉一個例子。使用ASP輸出Excel,並能夠直接列印。
1.Excel報表模板的制作
首先根據給定的報表格式,製作一個Excel template,當然需要從DB中動態統計生成的資料留著空白。這個報表先在Excel中畫好,以此例子儲存路徑為\test\testTemplate1.xlt。

2.Excel報表的生成與打印
這裡採用了Excel的Application module,也就是必須安裝Excel到系統中。


(1) 建立Excel.Application
set objExcel=CreateObject("Excel.Application")

(2) 打開Excel template
objExcel.Workbooks.Open(server.mappath("\test")&"\testTemplate1.xlt") '打開Excel template
objExcel.Sheets(1).select '選定工作頁
set sheetActive=objExcel.ActiveWorkbook.ActiveSheet

(3) 加入資料到指定儲存格
例如sheetActive.range("g4").value=date() ‘這裡塞的是時間,當然也可以是任何資料

(4) 加入DB中的資料
這裡假設已有一個dataset名稱為adoRset。
num=7 '從Excel的第七行開始
do until adoRset.EOF
strRange="d"&num&":f"&num '設定要填寫內容的區域
sheetActive.range(strRange).font.size=10 ‘設定字體大小
sheetActive.range(strRange).WrapText=false ‘設定文字自動換行
sheetActive.range(strRange).ShrinkToFit=true ‘設定是否自動調整表格大小
sheetActive.range(strRange).value=array(adoRset("aa"),adoRset("bb"),adoRset("cc")) ‘把Dataset中的Data填寫到相應的儲存格中
num=num+1
adoRset.MoveNext
loop

(5) Excel臨時報表文件的保存及處理
實際執行中應該注意每次一個user進行報表列印時都建立一個臨時的Excel文件,而不是硬性規定名稱,因為如果用固定的名稱的話,只有第一次生成是成功的,後面的操作都會因為已存在同名文件而導致失敗。所以我們需要每次都產生一個臨時的而且不重復的名稱,這裡可以採用自己撰寫的getTemporaryFile()函數由來生成,然後存放在filename變數中,用filepos變數表示這些臨時檔案的路徑。
此外如果這些臨時檔案不處理的話久而久之會成為垃圾檔案,因此在每個用戶在Excel報表打印請求時要先刪除臨時目錄下所有原先產生的臨時檔案。
臨時文件的處理函數如下:

function getTemporaryFile(myFileSystem)

dim tempFile,dotPos
tempFile=myFileSystem.getTempName
dotPos=instr(1,tempFile,".")
getTemporaryFile=mid(tempFile,1,dotPos)&"xls"

end function

set myFs=createObject("scripting.FileSystemObject")
filePos=server.mappath("\test") & "\tmp\" '要存放臨時文件的臨時目錄
fileName=getTemporaryFile(myFs) '取得一個臨時文件名稱
myFs.DeleteFile filePos&"*.xls" '刪除該目錄下所有原先產生的臨時檔案
set myFs=nothing
objExcel.ActiveWorkbook.saveas filePos&filename '儲存臨時檔案

'關閉Excel.Application
objExcel.quit
set objExcel=Nothing



3.系統設定與注意事項
雖然以上程式很簡單,但實際應用不當經常會出現錯誤,所以下面要講到的系統配置和注意事項非常關鍵。

(1) 千萬要保證以上程式輸入的正確性,否則一旦發生錯誤,Excel佔據記憶體,導致下一次呼叫時速度狂慢,並產生記憶體區段不可讀寫的Windows錯誤。

(2) 一定要設置好負責列印功能的asp文件的權限。方法是:在IIS管理中,選擇該asp文件,右鍵然後選“屬性”->“文件安全性”-> "匿名訪問和驗證控制“,在這裡IIS預設是匿名登入,應該選擇驗證登入(這裡基本驗證和Windows驗證兩種方式均可,但前者不夠安全),這一點無比重要,否則應用當中會出錯的。

(3) 有的時候報表分為多頁,而且我們希望每一頁有相同的表頭,要求表頭每頁都自動列印,可以在Excel template中進行設置。方法如下:選擇工具列"文件"->"頁面設置"->"工作表",然後在"頂端標題行"輸入你表頭的行數(如:表頭為1-3行即填入:$1:$3)。