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。
用户自定义函数需要继承自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; }
|
XalanTransformer类为扩展函数提供安装和卸载方法。
installExternalFunction在XalanTransformer的当前对象上安装扩展函数。
uninstallExternalFunction在XalanTransformer的当前对象上卸载扩展函数。
installExternalFunctionGlobal在全局作用域上安装扩展函数,
uninstallExternalFunctionGlobal在全局作用域上卸载扩展数据。
注意:如果扩展函数作为全局函数安装,那么扩展函数应该是线程安全的。因为多个线程可能同时调用同一个扩展函数。
以下代码片段安装一个名称为“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>
|
将含有单引号的字符串申明为变量,然后,在函数调用时,使用变量。例如,
使用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> |
Xalan-C_SQLExtLib库为Xalan-C下的一个数据库扩展函数库,其特性如下:
l 采用ADO连接数据源,具有执行SQL语句及存储过程的能力。
l 支持数据库事务
l 支持多数据源访问,支持同一个任务访问不同数据源的能力。
l 提供数据库连接池特性,按名访问数据库连接。
l 支持带参数的SQL语句。
l 提供缓存和非缓存两种模式。缓存模式下,一条查询语句返回的结果集能够多次使用,从而减少对数据库的访问,提供处理效率。
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放在同一个目录下。
http://Xalan-C_SQLExtLib.lijihongye.com
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语句 |
connect函数创建一个新连接,并连接到数据源。
参数:
参数1:ConnectName,连接名,字符串类型。
参数2:ConnectString,连接字符串,字符串类型。
返回值:
成功返回
失败返回
disconnect函数断开一个连接。
参数:
参数1:ConnectName,连接名,字符串类型。
返回值:
成功返回
失败返回
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> |
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> |
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> |
get-row-count-from-result-set返回结果集中记录的函数。
参数:
参数1:ResultsetName,结果集名。
返回值:
成功,返回
如果ResultsetName指定的结果集不存在,返回
get-col-count-from-result-set返回结果集中列的个数。
参数:
参数1:ResultsetName,结果集名。
返回值:
成功,返回
如果ResultsetName指定的结果集不存在,返回
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> |
execute函数执行一条SQL语句。
参数:
参数1:ConnectName,连接名,字符串类型。
参数2:SQL,SQL语句,参见query函数的参数3。
返回值:
成功返回;
若SQL语句中的参数和实参个数不一致,则返回
若SQL语句非法,则返回
其他错误,返回
略。
参考文献:
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