本项目起源于作者希望在 .NET 代码中使用 XSLT 2.0 和 XPath 2.0。然而,.NET 框架默认仅提供了 XSLT 1.0 和 XPath 1.0 的支持。据作者所知,Saxon 是唯一实现 XSLT 2.0 和 XPath 2.0 的工具。尽管如此,Saxon 在 .NET 社区中的普及程度并不高,这主要是因为 Saxon 的接口与 .NET 的 XML 接口存在显著差异。因此,本项目的目标是编写一个简单的接口适配器,使得 .NET 程序员能够以类似于使用 System.Xml 的方式来使用 Saxon。
读者应具备基本的 XML 知识。W3Schools 网站提供了关于 XSL、XPath 和 XQuery 的易于理解的教程。
XPath 2.0 比 XPath 1.0 更丰富、更灵活。在 XPath 2.0 中,一切都是序列,而不仅仅是“节点集”,并且支持条件逻辑和循环。例如,以下是一个有效的 XPath 2.0 语句,它返回一个序列:
for $x in /order/item return $x/price * $x/quantity
XML.com 上有一篇非常好的关于 XPath 2.0 的文章,该文章写于 2002 年初,这表明这项技术已经存在很长时间了。
XQuery 1.0 是 XPath 2.0 的扩展,是一个非常强大的查询语言。XQuery 可以用来生成基于 XML 的输出,代替 XSL。实际上,编写 XQuery 比编写 XSL 要容易得多。XQuery 的主要结构是 FLOWR 语句。 FLOWR 代表 "For, Let, Order by, Where, Return",与 LINQ 类似。
XSLT 2.0 使用 XPath 2.0,而 XSLT 1.0 使用 XPath 1.0。使用 XSLT 2.0 而不是 XSLT 1.0 有许多好处。在 XSLT 2.0 中,可以在 XSL 中编写一个函数,并在 XPath 中使用 xsl:function 元素访问该函数。这比之前在 .NET 代码中编写 XPath 函数,最终得到平台特定的 XSL 要好得多。此外,当自定义函数在 XSL 中编写时,开发周期会更快。
XSLT 2.0 还具有使用 xsl:for-each-group 元素进行分组的功能。以下代码来自 XML.com 的文章,是一个很好的示例:
<xsl:for-each-group select="cities/city" group-by="@country">
<tr>
<td>
<xsl:value-of select="@country"/>
</td>
<td>
<xsl:value-of select="current-group()/@name" separator=", "/>
</td>
<td>
<xsl:value-of select="sum(current-group()/@pop)"/>
</td>
</tr>
</xsl:for-each-group>
有关 XSLT 2.0 的详细信息可以在 XML.com 上找到。
如前所述,Saxon 的 API 与 .NET 的标准 XML API 有所不同。本项目通过适配器模式解决了这个问题。类 Xsl2Processor 和 XQueryProcessor 将 Saxon API 呈现给 .NET 程序员,类似于 System.Xml 中的 XslCompiledTransform 的呈现方式。主要区别在于 XslCompiledTransform 接受 IXPathNavigable 作为 XML 源,而包装器接受不太通用的 XmlNode。这是因为 Saxon API 使用 XmlNode。
代码将 Saxon 及其相关库作为一个名为 SaxonWrappers 的 Visual Studio 项目。有两种使用方式。第一种是将 SaxonWrappers 项目添加到解决方案中。这允许自定义包装器。或者,可以引用 zip 文件根目录中的所有程序集。确保将 saxon9api.netmodule 文件与 DLL 文件放在同一目录中。
以下代码对 books.xsl 进行 XSLT 2.0 转换,输入文件是 books.xml,并将结果输出到 books.html。下载中包含了输入文件。
using (StreamWriter streamWriter = new StreamWriter("books.html", false, Encoding.UTF8))
{
// Load the input doc
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("books.xml");
// Create the output XmlWriter
XmlTextWriter xmlWriter = new XmlTextWriter(streamWriter);
// Do the transformation
Xsl2Processor processor = new Xsl2Processor();
processor.Load("books.xsl");
processor.Transform(xmlDoc, xmlWriter);
}
以下代码对 books-to-html.xq 进行 XQuery 1.0 查询,输入文件是 books.xml,并将结果输出到 books_xq.html。下载中包含了输入文件。
using (StreamWriter streamWriter = new StreamWriter("books_xq.html", false, Encoding.UTF8))
{
// Load the input doc
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("books.xml");
// Create the output XmlWriter
XmlTextWriter xmlWriter = new XmlTextWriter(streamWriter);
// Do the transformation
XQueryProcessor xp = new XQueryProcessor();
xp.LoadFromFile("books-to-html.xq");
xp.RunQuery(xmlDoc, xmlWriter);
}
XQueryProcessor 类还有一个 Load 方法,用于从字符串加载 XQuery 语句。