利用XSLT 实现更好的灵活性和方便

<script type="text/javascript"> google_ad_client = "pub-8800625213955058"; /* 336x280, 创建于 07-11-21 */ google_ad_slot = "0989131976"; google_ad_width = 336; google_ad_height = 280; // </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script> 利用 XSLT 实现更好的灵活性和方便

Benoit Marchalbmarchal@pineapplesoft.com
顾问,Pineapple Software
2001 年 8 月

在这篇详细的技巧说明中,Benoit Marchal 说明了当保存 XML 文件时, SAXTransformerFactory — 直接在 XSLT 处理器中提供 SAX 事件的类 — 如何带给您更好的灵活性。几段可重用 Java 代码样本演示了该技术,这些代码样本需要使用 TrAX。

我使用的大多数 Java 应用程序都将其数据保存成 XML 格式。我喜欢 XML 的原因之一就是使用 XSLT 后处理文件很容易。例如,我通常以 XML 格式保存原始数据,并使用一两个样式表来生成 HTML、PDF 和 SVG 格式的报表。

我过去常常使用单独的应用程序来后处理文件,但用户逐渐要求使用一个集成的解决方案来模拟他们熟悉的商业生产应用程序中的“导出...”或“另存为...”菜单命令。

在搜寻好的解决方案的过程中,我最终找到了 SAXTransformerFactory。对于本讨论的其余部分,我假设您已经了解 SAX 语法分析、TrAX(XSLT 处理器的标准 Java API)和 XSLT。如果需要了解一些背景知识,请参阅参考资料以获取参照。

SAX 和 XSLT 处理器
您知道,SAX 语法分析要求您编写处理语法分析器事件的 ContentHandlerSAXTransformerFactory 实质上是将 XSLT 处理器转换成 ContentHandler

您也许会问,为什么要那么麻烦?毕竟,TrAX 还支持 SAXSource 以便与 SAX 语法分析器结合。的确,如果使用成熟的 SAX 语法分析器,SAXTransformerFactory 就没什么用处了。但我发现,在没有语法分析器的情况下,从我的应用程序生成那些事件还是合算的。

请考虑图 1。其中发生了两件事:首先应用程序将数据写入 XML 文件,然后 XSLT 处理器(另一个应用程序)将数据整理成 HTML 格式。

图 1. 典型的 XML 和 XSLT 应用程序
后处理 XML 文件

如果将 XSLT 处理器集成到应用程序中,会发生什么?仍必须向它提供 XML 文档。为满足该目的,大多数应用程序都使用临时文件或字符串。我相信,ContentHandler 更具有吸引力。

的确,在第一种情况中,应用程序编写了 XSLT 处理器可以立即分析的 XML 文档(通常通过 SAX 语法分析器)。如图 2 所示,如果应用程序发出 SAX 事件本身,是否会更有效呢?在图 2 中,应用程序并没有写到文件中,而是直接调用处理器的 ContentHandler,有效地模拟了语法分析器。

第二种模式更有效,它使用较少的内存,并省去了创建和删除临时文件的烦恼。

图 2. 应用程序模拟语法分析器
应用程序模拟语法分析器

模拟语法分析器
此外,它使模拟语法分析器变得很简单。应用程序很可能已经拥有了写开始标记、结束标记和转义符的功能。它可以将它们替换成 SAX 等价物,分别替换成 startElement()endElement()characters()。换句话说,代码 writer.write("<ps:key>") 变成
contentHandler.startElement ("http://www.psol.com/2001/08/dw/tip","key","ps:key",attributes)。它也许会更长,但写起来不会很费劲。

asXML() 方法将 Properties 对象写成 XML 格式,但它没有写到文件中;而是使用 ContentHandler

如果没有名称空间,代码会很简单。经验显示发出 startPrefixMapping() 并将 xmlns 声明当作常规属性传送会更安全。

清单 1. asXML() 模拟 SAX 语法分析器


protected static void asXML(Properties properties,ContentHandler contentHandler)

   throws SAXException

{

   AttributesImpl attributes = new AttributesImpl();

   contentHandler.startDocument();

   contentHandler.startPrefixMapping("ps",

	   "http://www.psol.com/2001/08/dw/tip");

   attributes.addAttribute("","ps","xmlns:ps","CDATA",

	   "http://www.psol.com/2001/08/dw/tip");

   contentHandler.startElement("http://www.psol.com/2001/08/dw/tip",

	   "properties","ps:properties",attributes);

   attributes.clear();

   Enumeration enumeration = properties.propertyNames();

   while(enumeration.hasMoreElements())

   {

      String name = (String)enumeration.nextElement(),

             value = properties.getProperty(name);

      contentHandler.startElement("http://www.psol.com/2001/08/dw/tip",

		   "property","ps:property",new AttributesImpl());

      contentHandler.startElement("http://www.psol.com/2001/08/dw/tip",

		   "key","ps:key",attributes);

      contentHandler.characters(name.toCharArray(),0,name.length());

      contentHandler.endElement("http://www.psol.com/2001/08/dw/tip",

		   "key","ps:key");

      contentHandler.startElement("http://www.psol.com/2001/08/dw/tip",

		   "value","ps:value",attributes);

      contentHandler.characters(value.toCharArray(),0,value.length());

      contentHandler.endElement("http://www.psol.com/2001/08/dw/tip",

		   "value","ps:value");

      contentHandler.endElement("http://www.psol.com/2001/08/dw/tip",

		   "property","ps:property");

   }

   contentHandler.endElement("http://www.psol.com/2001/08/dw/tip",

	   "properties","ps:properties");

   contentHandler.endPrefixMapping("ps");

   contentHandler.endDocument();

}

调用 XSLT 处理器
要对 asXML() 的结果应用样式表,请使用清单 2 中的代码。首先,就象常规 TrAX 一样,创建 TransformerFactory。接着,通过调用 getFeature() 确保它与 SAXTransformerFactory 兼容,如果成功,将它强制类型转换成 TransformerFactory

最后,使用 newTransformerHandler() 来请求 TransformerHandler 对象。该方法使用样式表的 URI 作为参数。TransformerHandler 实现了 ContentHandler,并且可以作为参数传递给 asXML()

如果熟悉 TrAX,您也许想要知道在哪里调用 transform()。答案是不必这样做。TransformerHandler 在接受 SAX 事件时会应用样式表。

HTML 样式表
TransformerHandler 是常规 XSLT 处理器;它对样式表没有限制,如“清单 3”中的 table.xsl。

清单 3. table.xsl


<?xml version="1.0"?>

<xsl:stylesheet

   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

   xmlns:ps="http://www.psol.com/2001/08/dw/tip"

   version="1.0">



<xsl:output method="html"/>



<xsl:template match="/">

<html>

   <head>

      <title>Properties</title>

   </head>

   <body>

      <table>

         <tr><td>Key</td><td>Value</td></tr>

         <xsl:for-each select="ps:properties/ps:property">

            <tr>

               <td><xsl:value-of select="ps:key"/></td>

               <td><xsl:value-of select="ps:value"/></td>

            </tr>

         </xsl:for-each>

      </table>

   </body>

</html>

</xsl:template>



</xsl:stylesheet>

整理成原始 XML
如果不想要 HTML,而更喜欢原始 XML 文档,会发生什么?最简单的解决方案是提供一个额外的样式表,该样式表不修改输入,如“清单 4”中的 identity.xsl。

清单 4. identity.xsl


<?xml version="1.0"?>

<xsl:stylesheet version="1.0"

                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">



<xsl:output method="xml"/>



<xsl:template match="@*|node()">

   <xsl:copy>

      <xsl:apply-templates select="@*|node()"/>

   </xsl:copy>

</xsl:template>



</xsl:stylesheet>

提高性能的序列化程序
如果您关心性能,那么会首选使用序列化程序。序列化程序是 ContentHandler,它将其输入写入 XML 文档。如果您的 XSLT 处理器是 Xalan,请使用 org.apache.xalan.serialize.Serializer。由于它是 ContentHandler,它会直接使用 asXML(),如清单 5 所示。

SAXTransformerFactory 的其它应用程序
如果分几个阶段来处理 XML 文档,SAXTransformerFactory 也会有用。例如,我最近在所谓的“管道”中实现了它,该“管道”通过 XSLT 样式表和定制过滤器的组合来转换输入文档。

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值