.Net框架下的XSLT转换技术简介
作者:王凯明 本文选自:赛迪网 2003年05月08日
一.前言:
XSLT转换技术是XML中的一项重要技术,本文将向XML开发者介绍.Net框架下的一些不同的XSLT转换技术。同时,本文还将介绍如何运用各种不同的输入数据源以完成一个XSLT转换。在.Net框架下,System.Xml.Xsl.XslTransform类能根据一个XSLT样式表文件转换一个XML文档,它是XSLT转换中最重要的一个类,同时它能够支持W3C的XSLT 1.0的语法,其运用的命名空间是http://www.w3.org/1999/XSL/Transform。
二.与XSLT转换相关的输入数据源:
.Net框架下有许多类可以完成读取XML文档以实现XSLT转换的功能,其中最有用的当属System.Xml.XmlReader类了。它是一个虚基类,因此不能被直接运用,必须有一个类继承它。.Net框架下有三个类是从该类继承过来的:XmlTextReader类、XmlNodeReader类以及XmlValidatingReader类,它们都是包含在命名空间System.Xml中的。其中XmlTextReader类能从一个XML文档中读取字符流并检查该文档是否是具有良好结构(well-formed)的,但它并不运用DTD或是XML模式进行XML文档的验证工作。XmlNodeReader类允许数据从任何XML文档对象模型(DOM)的API中被读取,比如一个System.Xml.XmlNode对象,而且XmlNode对象并不必是一个完整的XML文档的根节点,它可以是其一个子节点。XmlValidatingReader类能保证一个XML文档是遵从一个DTD或是XML模式所确定的规则的。下面是一个XmlReader类作为XSLT转换的输入数据源的应用实例。
// 将一个XSLT文件的字符串导入到一个TextReader对象中
System.IO.TextReader tr = new System.IO.StreamReader("numbers.xsl");
// 将上面的TextReader对象作为XmlTextReader对象的数据源
System.Xml.XmlReader xr = new System.Xml.XmlTextReader(tr);
// 创建一个新的XslTransform对象
System.Xml.Xsl.XslTransform trans = new System.Xml.Xsl.XslTransform();
// 将XmlReader对象中的样式表导入到上面的XslTransform对象中
trans.Load(xr);
另一种能完成读取XML文档以实现XSLT转换功能的类是System.Xml.XPath.XPathNavigator类或是任何实现了System.Xml.XPath.IXPathNavigable接口的类,这些类包括了System.Xml.XPath.XPathDocument类,System.Xml.XmlDocument类以及System.Xml.XmlDataDocument类。System.Xml.XPath.XPathNavigator类是基于XPath数据模型的,并提供了能够对任何XML数据进行XPath查询的方法。
System.Xml.XPath.XPathDocument类是这些类中速度最快的一个类,因为它是只读的,在XSLT转换对速度的要求很高时应该使用这个类。System.Xml.XmlDocument类的效率仅次于System.Xml.XPath.XPathDocument类,而System.Xml.XmlDataDocumnet类并不适合XSLT转换,因为它的效率最低。System.Xml.XPath.IXPathNavigable接口可以在任何数据源上实现,同时它允许任何类型的数据作为XSLT转换的数据源。下面的示例代码可以被接在上面的例子后面。
// 创建一个新的XPathDocument对象,并从一个XML文件导入数据源
System.Xml.XPath.XPathDocument xp = new
System.Xml.XPath.XPathDocument("numbers.xml");
// 创建一个新的XPathNavigator对象
System.Xml.XPath.XPathNavigator xpn = xp.CreateNavigator();
XslTransform类要求XML或XSLT的数据源是一个XmlReader对象或是一个XPathNavigator对象。它的两个最重要的方法是Load()方法和Transform()方法,这两个方法并不提供重载的对Stream对象直接进行处理的方法,但是却可以间接地将Stream对象作为XSLT转换的数据源,而该Stream对象则是从XML文件或XSLT样式表中获取的。下面的代码创建了一个Stream对象,并显示了如何将它作为XSLT转换的数据源,不过该代码段仅仅是一个示例,并没有实际的数据存在。
// 创建一个新的Stream对象
System.IO.Stream st = new System.IO.MemoryStream();
// 在此运用一个XML文件填充上面的Stream对象并将Stream对象的位置置为0
st.Position = 0;
// 将Stream对象导入到一个XPathDocument对象中
System.Xml.XPath.XPathDocument xp = new System.Xml.XPath.XPathDocument(st);
// 对XSLT文档进行同样的操作
System.IO.Stream XSLTStream = new System.IO.MemoryStream();
// 在此运用一个XSLT文件填充上面的Stream对象并将Stream对象的位置置为0
st.Position = 0;
// 创建一个XmlReader对象,并将上面的Stream对象作为数据源
System.Xml.XmlReader xsltXR = new System.Xml.XmlTextReader(XSLTStream);
// 以后,该XmlReader对象就可以被传递到XslTransform类的Load()方法中去了
上面的代码中并没有实际的数据存在,我们需要将XML文件或XSLT样式表填充到Stream对象中才能进行实际的操作。另一种设置输入数据源的方法是以字符串的形式从一个URL获取XML文档或XSLT文档,同时还可能有一个可选择的System.Xml.XmlResolver对象,该方法是最直接也是最有效的方法。
三.与XSLT转换相关的输出数据源:
在介绍了XSLT转换的输入数据源之后,本文接着向大家介绍与之相关的输出数据源。根据所运用的不同类型的输出类,xsl:output元素有时可以被忽略掉。比如在运用XmlWriter类或XmlReader类作为输出类的时候,该元素就被忽略掉了。有关xsl:output元素的详细内容请参考MSDN的文章"Outputs from an XslTransform",这里限于篇幅就不多作介绍了。
综上所述,进行XSLT转换的必要条件包括了一个XML文档,一个XSLT文档以及一个能处理输出的有效对象。下面的代码运用了一个XPathDocument对象以及一个样式表文件进行XSLT转换,其输出的结果被直接显示在控制台中了。
// 创建一个新的XslTransform对象
System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform();
// 从XSL文件中导入样式表
xslt.Load("numbers.xsl");
// 创建一个新的XPathDocument对象,并导入一个XML文件
System.Xml.XPath.XPathDocument doc = new
System.Xml.XPath.XPathDocument("numbers.xml");
// 创建一个新的XmlTextWriter对象用于完成到控制台的数据输出
System.Xml.XmlTextWriter writer = new
System.Xml.XmlTextWriter(System.Console.Out);
// 进行实际的XSLT转换操作
xslt.Transform(doc, null, writer);
// 在完成操作后关闭XmlTextWriter对象
// 同时请注意一旦关闭该对象就不能往控制台中写任何额外信息了
writer.Close();
四.与XSLT转换相关的流对象:
.Net框架允许程序员将XSLT转换的结果直接输出到一个System.IO.Stream对象,下面的代码将使用一个用于输出的System.IO.MemoryStream对象,然后显示如何运用它进行与XSLT转换的输出相关的操作。
// 创建一个新的XslTransform对象
System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform();
// 从XSLT文件中导入样式表
xslt.Load("numbers.xsl");
// 导入一个XML文件
System.Xml.XPath.XPathDocument doc = new
System.Xml.XPath.XPathDocument("numbers.xml");
// 创建用于输出的Stream对象
System.IO.Stream str = new System.IO.MemoryStream();
// 进行实际的转换操作
xslt.Transform(doc, null, str);
// 对Stream对象进行Flush操作,并将其位置置为0
str.Flush();
str.Position = 0;
如果一个System.Xml.XmlDocument对象或一个System.Xml.XPath.XPathDocument对象已经被载入到主存中了,那么它们也可以被用作XSLT转换的输入数据源。因为这两个对象都实现了System.Xml.XPath.IXPathNavigable接口,所以都可以作为XslTransform对象的Transform()方法的参数被直接使用。
// 创建一个新的XslTransform对象
System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform();
// 从XSLT文件中导入样式表
xslt.Load("numbersXML.xsl");
// 将一个XML文档导入到一个XPathDocument对象中
System.Xml.XPath.XPathDocument doc = new
System.Xml.XPath.XPathDocument("numbers.xml");
// 创建用于输出的Stream对象
System.IO.Stream str = new System.IO.MemoryStream();
System.Xml.XmlWriter xw = new
System.Xml.XmlTextWriter(str,System.Text.Encoding.UTF8);
// 进行实际的转换操作
xslt.Transform(doc, null, xw);
// 对XmlWriter对象进行Flush操作,并将Stream对象的位置置为0
xw.Flush();
str.Position = 0;
五.与XSLT转换相关的嵌入式脚本以及代码:
.Net框架中的XSLT转换对象提供了对在XSLT文档中用脚本扩展元素来嵌入脚本语言的支持。脚本语言所提供的一组功能比纯XSLT所提供的功能要丰富,经常可以用来对文档内容进行更为复杂的操作,比如对其应用科学计算函数、访问外部信息源等。在.Net框架下,程序员运用msxsl:script元素可以标记XSLT样式表中的嵌入式脚本或代码。脚本扩展元素必须在urn:schemas-microsoft-com:xslt namespace命名空间中声明。.Net框架中的分析器能够支持C#、VB.Net、VBScript或JScript等代码编写的脚本。在脚本元素中,我们通过提供一个implements-prefix属性定义了函数所调用的命名空间,通过提供一个language属性定义了函数所使用的编程语言。同时,我们得注意一个样式表中可以嵌入多种语言的脚本代码,但是同一个命名空间下只能使用同一种语言。下面的实例显示了一个包含嵌入式C#函数的样式表。
<xsl:stylesheet version='1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:msxsl='urn:schemas-microsoft-com:xslt'
xmlns:theScript='urn:CustomScript'>
<xsl:output omit-xml-declaration='yes' method='text'
media-type='text/plain' indent='no' />
<xsl:variable name='displayme' />
<msxsl:script implements-prefix='theScript' language='C#'>
<![CDATA[
public string HelloName(string name)
{
return "Hello " + name;
}
]]>
</msxsl:script>
<xsl:template match='/'>
<xsl:text disable-output-escaping='yes'>Print Integers > 3</xsl:text>
<xsl:apply-templates select='Root/Numbers' />
Script Result: <xsl:value-of select='theScript:HelloName("Joe")' />
Done: <xsl:value-of select='$displayme' />
</xsl:template>
<xsl:template match='Numbers'>
Numbers:<xsl:apply-templates select='Integer[@value > 3]' />
</xsl:template>
<xsl:template match='Integer'>
Integer: <xsl:value-of select='@value' />
</xsl:template>
</xsl:stylesheet>
六.样式表参数以及扩展对象:
XSLT提供了一个在样式表中使用参数以及扩展对象的机制。在.Net框架下,程序员可以通过运用System.Xml.Xsl.XsltArgumentList类实现将参数或扩展对象传递到一个样式表。在样式表中使用参数的机制允许程序员声明一个全局变量,该变量被定义为xsl:variable类型的元素,它是作为一个xsl:stylesheet的子元素而存在的,但它并不包含在xsl:template之中。通过调用XsltArgumentList类的AddParam()方法程序员可以实现添加参数的功能,该方法中的三个参数依次为有效的名称、命名空间的URI以及参数值。如果参数值不是一个字符串、布尔值、数字、节点片断(Node Fragment)或节点集合(Node Set),那么它将被强制转化为双精度浮点值或是字符串。而一个扩展对象是任何一个能返回XSLT数据类型值的.Net类。通过调用XsltArgumentList类的AddExtensionObject()方法程序员可以实现添加扩展对象的功能,该方法中的两个参数分别为有效的名称以及命名空间的URI。下面的代码显示了如何运用XsltArgumentList类在样式表中使用参数以及扩展对象的方法。
// 创建一个新的XPathDocument对象,并导入一个XML文件
System.Xml.XPath.XPathDocument xp = new
System.Xml.XPath.XPathDocument("numbers.xml");
// 创建一个新的XslTransform对象
System.Xml.Xsl.XslTransform trans = new System.Xml.Xsl.XslTransform();
// 导入一个XSLT文件到上面的XslTransform对象中
trans.Load("numbersExtension.xsl");
// 创建一个新的XsltArgumentList对象
System.Xml.Xsl.XsltArgumentList xslArg = new
System.Xml.Xsl.XsltArgumentList();
// 调用XsltArgumentList对象的AddParam()方法以添加参数
xslArg.AddParam("displayme","","Is this fun?");
// 创建一个新的扩展对象
SayHello hi = new SayHello();
// 调用XsltArgumentList对象的AddExtensionObject()方法以添加扩展对象
xslArg.AddExtensionObject("urn:SayHello",hi);
// 创建一个新的System.IO.Stream对象以处理输出结果
System.IO.Stream str = new System.IO.MemoryStream();
// 进行实际的转换操作
trans.Transform(xp, xslArg, str);
// 对Stream对象进行Flush操作,并将其位置置为0
str.Flush();
str.Position = 0;
// 创建一个新的StreamReader对象以读取流并返回字符串
System.IO.StreamReader sr = new System.IO.StreamReader(str);
string xmlOut = sr.ReadToEnd();
// 关闭StreamReader 对象
sr.Close();
// 将结果写到控制台中
Console.Write(xmlOut);
// 扩展对象返回Hello Name
public class SayHello
{
public string HelloName(string name) {
return "Hello " + name;
}
}
通过上面的介绍,我们发现使用扩展对象和使用嵌入式脚本具有许多相同之处。下面我们分析一下使用扩展对象相对于使用嵌入式脚本的一些优缺点。
移植性:嵌入式脚本比扩展对象更容易移植,因为任何.Net平台都可以正确地用嵌入式脚本来转换一个样式表。而另一方面,使用扩展对象的样式表则只能用一段代码来进行转换,而且需在.Net平台或其他一些支持扩展对象的平台下进行,并要求这段代码能正确地实现一个带有效的名称和命名空间URI的扩展对象。
大小:一个依赖于扩展对象的样式表比包含嵌入式脚本的样式表要小,而且更容易维护。
灵活性:正如我们看到的那样,依赖于扩展对象的样式表可以按照修改约束它们的扩展对象的功能来修改它们的行为。而使用嵌入式脚本的样式表则永远只能按照嵌入的脚本语言指定的功能执行操作。
性能:因为扩展对象是预编译的,而不是即时编译的,因此使用扩展对象的脚本的性能在某种程度上要比嵌入式脚本高一些。不过具体情况下还要考虑装入样式表的频率以及嵌入式脚本的规模等因素。
可维护性:使用扩展对象的样式表在修改和纠错时可能比较困难,因为真正的代码从样式表和提供扩展对象实现的代码中分离出去了。而嵌入式脚本尽管稍微慢一些,却具有将所有代码都置于本地的优点。
通过考虑上述一些因素,我们可以决定在具体的情况下是使用扩展对象还是使用嵌入式脚本了。
七.总结:
本文向大家介绍了.Net框架下和XSLT转换相关的一些知识,XSLT转换技术是XML中的一项重要技术。.Net框架对XSLT转换提供了稳健的支持,它不仅完全支持W3C定义的XSLT规范,还有许多有用的扩展,比如在样式表中嵌入多种语言的脚本、使用扩展对象来扩展XSLT的功能等等,从而增强了.Net应用程序中样式表的易用性。最后,我希望大家能通过本文掌握.Net框架有关XSLT转换的一些基本技术,并了解每种技术的优缺点以更好地应用于实际的项目。