XSLT用來描述從一個XML文檔到另一個文檔的轉換規則(邏輯),Xalan-C爲XSLT的一個基於C++語言的解析器,根據XSLT所描述的轉換規則執行轉換。這非常類似腳本語言和腳本解釋器,可以理解爲XSLT就是一種腳本語言,而Xalan-C則是對應的腳本解釋器。
將轉換邏輯從代碼中分離出來,通過XSLT來是實現,從而可以大大提高程序的可複用性、可擴展性和靈活性。創建全新的或者修改現有的轉換業務,只需要編輯轉換規則腳本即可,無需修改和構建主程序。
然而,有時,Xalan-C提供的轉換規則(功能或者函數)是有限的,需要對其進行擴展。例如,在文檔轉換時,需要將原文檔中的商品類別編號輸出爲對應的商品類別名稱,而商品類別編號和名稱的對應關係存在數據庫的一張代碼表中。因此,需要在XSLT中描述從數據庫中獲取數據的轉換規則,並在Xalan-C中實現這種擴展腳本的功能,這就是本文的主題——Xalan-C下數據庫擴展庫的實現。
Xalan-C支持函數擴展,創建和使用用戶自定義函數,但不支持元素擴展。Xalan-J(Xalan的Java版本)支持元素擴展,且Xalan-J已經實現了一個SQL擴展,見http://xml.apache.org/xalan-j/extensionslib.html#sql。
2. Xalan-C實現自定義函數
2.1. 創建用戶自定義函數
用戶自定義函數需要繼承自Function基類
重載execute()方法,在該方法內實現函數功能,並且通過XObjectFactory工廠返回一個XSLT數據類型
重載clone()方法,允許Xalan創建和保持函數的一個副本
如果需要,隱藏賦值=和等於運算符方法。
// Base header file. Must be first. #include <xalanc/Include/PlatformDefinitions.hpp>
#include <cmath> #include <ctime>
#include <xercesc/util/PlatformUtils.hpp> #include <xalanc/XalanTransformer/XalanTransformer.hpp> #include <xalanc/XPath/XObjectFactory.hpp>
XALAN_CPP_NAMESPACE_USE
// This class defines a function that will return the square root // of its argument. class FunctionSquareRoot : public Function { public:
/** * Execute an XPath function object. The function must return a valid * XObject. * * @param executionContext executing context * @param context current context node * @param opPos current op position * @param args vector of pointers to XObject arguments * @return pointer to the result XObject */ virtual XObjectPtr execute( XPathExecutionContext& executionContext, XalanNode* /* context */, const XObjectPtr arg, const Locator* /* locator */) const { if (args.size() != 1) { executionContext.error("The square-root() function takes one argument!", context); } assert(args[0] != 0); // Use the XObjectFactory createNumber() method to create an XObject // corresponding to the XSLT number data type. return executionContext.getXObjectFactory().createNumber( sqrt(args[0]->num())); }
/** * Implement clone() so Xalan can copy the square-root function into * its own function table. * * @return pointer to the new object */ // For compilers that do not support covariant return types, // clone() must be declared to return the base type. #if defined(XALAN_NO_COVARIANT_RETURN_TYPE) virtual Function* #else virtual FunctionSquareRoot* #endif clone() const { return new FunctionSquareRoot(*this); }
private: // The assignment and equality operators are not implemented... FunctionSquareRoot& operator=(const FunctionSquareRoot&); bool operator==(const FunctionSquareRoot&) const; }
|
2.2. 安裝、卸載用戶自定義函數
XalanTransformer類爲擴展函數提供安裝和卸載方法。
installExternalFunction在XalanTransformer的當前對象上安裝擴展函數。
uninstallExternalFunction在XalanTransformer的當前對象上卸載擴展函數。
installExternalFunctionGlobal在全局作用域上安裝擴展函數,
uninstallExternalFunctionGlobal在全局作用域上卸載擴展數據。
注意:如果擴展函數作爲全局函數安裝,那麼擴展函數應該是線程安全的。因爲多個線程可能同時調用同一個擴展函數。
2.3. 訪問用戶自定義函數
以下代碼片段安裝一個名稱爲“square-root”的本地函數FunctionSquareRoot(求平方根),並且綁定到命名空間http://MyExternalFunction.mycompany.org下。
#include <xalanc/Include/PlatformDefinitions.hpp> #include <xercesc/util/PlatformUtils.hpp> #include <xalanc/XalanTransformer/XalanTransformer.hpp> // You have created a header file for FunctionSquareRoot. #include <MyFunctions/FunctionSquareRoot.hpp> // The namespace... const XalanDOMString theNamespace("http://MyExternalFunction.mycompany.org");
theXalanTransformer.installExternalFunction(theNamespace, XalanDOMString("square-root"), FunctionSquareRoot()); |
以下是使用擴展函數
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:external="http://ExternalFunction.xalan-c.xml.apache.org" exclude-result-prefixes="external"> <xsl:template match="//area"> <out> The area of the square is <xsl:value-of select="@value"/> square units. The length of each side is <xsl:value-of select="external:square-root(@value)"/> units </out> </xsl:template> </xsl:stylesheet>
|
2.4. 其他
2.4.1. 字符串參數中帶有單引號
將含有單引號的字符串申明爲變量,然後,在函數調用時,使用變量。例如,
使用xpath的concat函數連接字符創“a’c’b”和“123”。
錯誤的語法如下:
<xsl:value-of select="concat('a'c'b','123')"></xsl:value-of> |
正確的語法如下:
<xsl:variable name="var1">a'c'b</xsl:variable> <xsl:value-of select="concat($var1,'123')"></xsl:value-of> |
3. Xalan-C_SQLExtLib
Xalan-C_SQLExtLib庫爲Xalan-C下的一個數據庫擴展函數庫,其特性如下:
l 採用ADO連接數據源,具有執行SQL語句及存儲過程的能力。
l 支持數據庫事務
l 支持多數據源訪問,支持同一個任務訪問不同數據源的能力。
l 提供數據庫連接池特性,按名訪問數據庫連接。
l 支持帶參數的SQL語句。
l 提供緩存和非緩存兩種模式。緩存模式下,一條查詢語句返回的結果集能夠多次使用,從而減少對數據庫的訪問,提供處理效率。
3.1. 安裝、卸載Xalan-C_SQLExtLib
Xalan-C_SQLExtLib.dll導出2個外部函數:
XalanSQLExtLib_Install:安裝SQL擴展庫
XalanSQLExtLib_UnInstall:卸載SQL擴展庫
宿主進程加載Xalan-C_SQLExtLib.dll,調用XalanSQLExtLib_Install安裝SQL擴展庫後即可使用SQL擴展庫提供的擴展函數。當不再需要使用時,調用XalanSQLExtLib_UnInstall卸載SQL擴展庫。
注意,部署時,將Xalan-C_SQLExtLib.dll與Xalan-C_1_10D.dll放在同一個目錄下。
3.2. 命名空間
http://Xalan-C_SQLExtLib.lijihongye.com
3.3. 擴展函數清單
Xalan-C_SQLExtLib擴展函數包括連接相關函數、查詢相關函數、執行SQL語句函數等。
註冊函數名 |
內部函數名 |
描述 |
連接相關函數 |
||
connect |
XalanSQLExFunctionConnect |
連接數據庫 |
disconnect |
XalanSQLExFunctionDisconnect |
關閉連接 |
查詢相關函數 |
||
query |
XalanSQLExFunctionQuery |
查詢 |
open-result-set |
XalanSQLExFunctionOpenResultset |
執行查詢,將結果集緩存 |
get-from-result-set |
XalanSQLExFunctionGetFromResultset |
從結果集中獲取數據 |
get-row-count-from-result-set |
XalanSQLExFunctionGetRowCountFromResultset |
返回結果集中記錄函數 |
get-col-count-from-result-set |
XalanSQLExFunctionGetColCountFromResultset |
返回結果集中列數 |
close-result-set |
XalanSQLExFunctionCloseResultset |
關閉結果集 |
事務相關函數 |
|
|
begin-transaction |
XalanSQLExFunctionBeginTransaction |
打開事務 |
commit-transaction |
XalanSQLExFunctionCommitTransaction |
提交事務 |
rollback-transaction |
XalanSQLExFunctionRollbackTransaction |
回滾事務 |
執行SQL語句函數 |
||
execute |
XalanSQLExFunctionExecute |
執行非查詢SQL語句 |
3.4. 連接相關函數
3.4.1. connect
connect函數創建一個新連接,並連接到數據源。
參數:
參數1:ConnectName,連接名,字符串類型。
參數2:ConnectString,連接字符串,字符串類型。
返回值:
成功返回
失敗返回
3.4.2. disconnect
disconnect函數斷開一個連接。
參數:
參數1:ConnectName,連接名,字符串類型。
返回值:
成功返回
失敗返回
3.5. 查詢相關函數
3.5.1. query
query函數執行一條查詢語句,返回查詢結構集中第一行指定列的值。
參數:
參數1:QeuryColumnName,需查詢的列名,字符串類型。
參數2:ConnectName,連接名,字符串類型。
參數3:SQL,查詢SQL語句,字符串類型。
SQL參數能夠接受帶變量的SQL語句,變量沒有名字,沒有類型(都視爲字符串),只有位置(順序號),即第幾個(字符串)變量。變量用符號“?”表示,選擇“?”,是因爲該符號在SQL語句中不常出現。若SQL語句中本身含有“?”符號,不需要進行變量替換,則用“??”雙波浪號代替。在包含有變量的SQL語句調用中,變量的取值被跟隨在參數3後的參數給出,第i個變量取值爲第i+3個參數。原則上,有多少個變量,參數3後就應該有多少個參數。
返回值:
如果查詢結果集爲空,返回“”空字符串;
如果查詢結果集函數大於等於1行,則返回首行QeuryColumnName指定的列的值;
如果SQL語句中的參數和實參個數不一致,則返回
如果查詢語句非法,則返回
如果查詢失敗,返回NF,。
其他錯誤,返回
舉例:
不帶變量的query調用:在“laton”連接上執行“select maxobjectid from tb_0001”語句,並返回“maxobjectid”字段的值。
<out> <xsl:value-of select="sqlextlib:query('code','laton',' select maxobjectid from tb_0001"/> </out> |
帶變量的query調用:在“laton”連接上執行“select code from tb_0002 where name=’?’”語句,其中where子句中“?”由XSLT參數$name替換並返回“code”列的值。
<out> <xsl:param name="name" select="'test'"/> <xsl:value-of select="sqlextlib:query('code','laton','select code from tb_0002 where name='?'',$name)"/> </out> |
3.5.2. open-result-set
query函數一次只能從查詢結果集中返回第一行中一個字段的值,若需要獲取同一條SQL語句中多個字段的值,則需要對同一SQL語句執行多次,效率低下。open-result-set函數執行一條查詢語句,並能緩存結果集,能用於get-from-result-set函數從結果集中多次獲取數據,直到close-result-set函數關閉結果集。
參數:
參數1:ResultsetName,結果集名,注意在同一個XSLT文件中不能定義2個相同的結果集。
參數2:ConnectName,連接名,字符串類型。
參數3:SQL,查詢SQL語句,同query函數的參數3。
返回值:
如果函數執行成功,返回
如果SQL語句中的參數和實參個數不一致,則返回
如果查詢語句非法,則返回
如果查詢失敗,返回NF,。
其他錯誤,返回
open-result-set的實現需要考慮結果集的多實例問題。多實例問題可能出現在以下兩種場景中。第一種情況是同一個Xalan-C實例同時解析多個不同的XSLT文件,而這些文件中使用open-result-set函數打開了相同結果集,即ResultsetName參數相同;另一種情況則是同一個Xalan-C實例同時執行同一個包含有open-result-set函數的XSLT文件的多個轉換任務,例如同時將a.xml和b.xml通過同一個XSLT文件t.xsl轉換爲a.out和b.out。
如果不考慮結果集多實例問題,則有可能導致SQL擴展庫混淆不同實例的結果集,張冠李戴,a.xml的結果集可能會被用於b.xml文件。
解決這一問題的一個思路是結果集增加實例標示符屬性。實例標示符可以採用線程ID來進行唯一標示。該方案的一起缺陷即要求一個轉換任務需要在同一個線程中完成。
舉例:在“laton”連接上執行“select code,name from tb_0002”語句,並將查詢結果集緩存爲“CodeTable”。
<out> <xsl:param name="RS_CodeTable" select="sqlextlib:open-result-set('RS_CodeTable','laton',' select code,name from tb_0002')"/> </out> |
3.5.3. get-from-result-set
get-from-result-set從open-result-set打開的結果集中獲取數據。
參數:
參數1:ResultsetName,結果集名。
參數2:Row,行號
參數3:Col,列號
返回值:
成功,返回
如果ResultsetName指定的結果集不存在,返回
如果Row行號越界,則返回
如果Col列號越界,則返回
舉例:
<out> <xsl:param name="RS_CodeTable" select="sqlextlib:open-result-set('RS_CodeTable','laton',' select code,name from tb_0002')"/> <xsl:value-of select="sqlextlib:get-from-result-set('RS_CodeTable',0,1)"/> </out> |
3.5.4. get-row-count-from-result-set
get-row-count-from-result-set返回結果集中記錄的函數。
參數:
參數1:ResultsetName,結果集名。
返回值:
成功,返回
如果ResultsetName指定的結果集不存在,返回
3.5.5. get-col-count-from-result-set
get-col-count-from-result-set返回結果集中列的個數。
參數:
參數1:ResultsetName,結果集名。
返回值:
成功,返回
如果ResultsetName指定的結果集不存在,返回
3.5.6. close-result-set
close-result-set關閉open-result-set打開的結果集。
參數:
參數1:ResultsetName,結果集名。
返回值:
成功,返回
如果ResultsetName指定的結果集不存在,返回
舉例:
<out> <xsl:param name="RS_CodeTable" select="sqlextlib:open-result-set('RS_CodeTable','laton',' select code,name from tb_0002')"/> <xsl:value-of select="sqlextlib:get-from-result-set('RS_CodeTable',0,1)"/> <xsl:param name=" RS_CodeTable" select="sqlextlib:close-result-set('RS_CodeTable')"/> </out> |
3.6. 執行SQL語句函數
3.6.1. execute
execute函數執行一條SQL語句。
參數:
參數1:ConnectName,連接名,字符串類型。
參數2:SQL,SQL語句,參見query函數的參數3。
返回值:
成功返回;
若SQL語句中的參數和實參個數不一致,則返回
若SQL語句非法,則返回
其他錯誤,返回
4. 應用舉例
略。
參考文獻:
http://xml.apache.org/xalan-c/extensions.html
http://xml.apache.org/xalan-c/extensionslib.html
http://xml.apache.org/xalan-j/extensionslib.html#sql