面向对象的 XSLT编程
内容:
样本应用程序的概述
样式表组件的设计
XSLT文档组装器的实现
实例
总结
参考资料
关于作者
在 XML & Web 服务专区还有:
教学
工具与产品
所有的文章

谢强 (richard_xieq@yahoo.com.cn)
西安交通大学系统工程所的硕士研究生
2004 年 3 月

现在,许多应用程序利用 XML 来格式化业务数据,而且这些数据可能分布在不同的地方。在实际运用时,常需要将这些分布的XML数据用不同的视图表示出来。而样式表提供了业务数据与表示层的分离的方法,作为一种XML数据转换的工具,通过它可以XML数据表示出来。一个样式表可将分布的XML数据以某种视图表现出来,而在本文所要解决的问题是如何产生多个样式表作用于分布的XML数据来产生不同的视图。本文介绍了基于   样式表组件的XSLT组装方法。它的核心是通过运用OO思想来构造一系列的XSLT的模板文件(XSLT文档组件),通过一个XSLT的组装器根据客户的请求来动态组装一个完整的XSLT文档,并将之运用到分布的XML文档上。从而,提供不同的用户以不同的表示层。

样本应用程序的概述

为了演示面向对象的XSLT编程,我将以产品目录系统为例,在产品目录系统中,不同的用户可以获得不同的表示层。在本例中,我们设想了三种用户,一般用户,高级用户及内部用户。各种用户具有不同的表示视图。例如,各等级的用户的折扣率不同,使得产品的零售价格不一,而内部用户可能需要得到产品供应商的信息。产品信息,供应商信息以及折扣率信息分别存储在不同的XML文档中。列表1是产品列表信息,列表2是供应商信息,列表3是折扣率信息。系统根据用户的类别动态的生成样式表。系统的原理就是首先生成多个XSLT文档组件,各个文档组件负责部分转换功能,就好比OOP中的各个类。同时,我们将生成一个XSLT文档组装器,它的核心实际上也是一个XSLT文档,但是它能接收应用程序传给它的参数并根据参数来将多个XSLT文档组装成一个完整的XSLT文档。

样式表组件的设计

XSLT实际是由一系列的匹配模板,命名模板,以及一组属性,参数和变量组成。它作用于XML数据源时,可根据定义的模板执行某些操作。从某种程度上讲,可视样式表为OO中的类。因此,某些OO的思想可以引入到样式表的设计中。下面分别介绍样式表组件的设计以在设计中可以引入的OO思想。

基本样式表组件设计

首先,我们设计基本的样式表组件,根据系统的要求,我们设计了三个基本的样式表,分别是产品样式表,供应商样式表及折扣率样式表。列表4是产品样式表,它是整个产品目录系统的主样式表,以后对样式表的扩展都是在它的基础上进行。它就好比系统的基类。供应商样式表功能是转换供应商的信息,由于所需信息位于不同的XML文档中,而系统的主样式表是产品样式表,产品列表XML文档是主源文档,所以,供应商样式表所使用的供应商XML文档作为辅助源文档,必需通过document()来加载。清单1显示了如果通过document函数来获得所需的节点集。列表5是供应商样式表的完整源代码。

清单1

 <xsl:template name="suplierShow"> <xsl:param name="sId"/> <xsl:variable name="supplier" select="document('suppliers.xml',.)//供应商[@id=$sId]"/> …… </xsl:template> 

折扣率样式表的功能是根据用户的等级来确定产品的零售价格。与供应商样式表一样,它也需要从折扣率XML文档中获得折扣率信息,并根据用户的等级计算出价格。清单2显示了零售价格的计算方法。列表6是折扣率样式的完整源代码。

清单2

 <xsl:template name="discount"> <xsl:param name="catalog"/> <xsl:param name="price"/> <xsl:param name="customerType"/> <xsl:variable name="discountPrice"> <xsl:choose > <xsl:when test="$customerType='一般用户'"> <xsl:variable name="discountRate" select="document('discounts.xml',.)//一般用户/折扣[@类别=$catalog]/@折扣率"/> <xsl:value-of select="$price*$discountRate"/> </xsl:when>    …… </xsl:template> 

现在,我们已经设计好了样式表基本的组件了,下面就引入OO的概念到样式表的设计中。

样式表中的继承

在XSLT有两种重用样式表的方法:导入(import)和包含(include)。这两种方法的区别在于:包含只是简单地将样式表添加到主样式表中包含它们的位置。包含的样式表将插入主文档中,就好象被包含的文件直接从数据流中读取一样。而导入与包含的不同之处在于只有在调用文档中不具有相同的命名参数或模板时,才使用导入模板。包含方法虽然编译度快,但它不能显式重载模板。所以,在本样例中,采用的导入方法。在本例中,内部用户的产品样式表在主产品样式表上,扩展了供应商的信息。内部用户产品样式表就好比继承了主产品样式表,并扩展了自己的功能。清单3显示了如向利用导入机制来扩展样式表。列表7是内部用户产品样式表的完整源代码,它是通过XSLT文档组装器生成的。

清单3

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- 导入样式表--> <xsl:import href="supplier.xslt"/> …… <!-- 显示重载模板 --> <xsl:template match="供应商"> <xsl:apply-imports/> </xsl:template> </xsl:stylesheet> 

样式表中的重载

在样式表中的<xsl:template>与<xsl:apply-temples>指令元素具有模式(mode)属性。拥有mode属性的<xsl:apply-temples>元素将寻找拥有同样mode的一个<xsl:template>元素,并处理它的模板内容,忽略没有匹配模式的模板规则。利用模板的模式属性,我们可为相同的XML文档元素定义多个模板,然后,在运行时,根据模式属性决定到底调用那个模板。清单4显示了模式属性的使用方法。同样,<xsl:apply-imports>指令元素也可用于样式表的重载,由于导入的样式表中的模板比被导入的样式表中的相同模板的优先级低,甚至导入的模板具在比样式表中原有的模板具有更高的优先级属性(priority)也如此。而<xsl:apply-imports>元素具有类似OOP中在super()方法的相似的功能,它可以通知XSLT处理程序在被导入文档中运用导入模板。同时,被调用的导入模板要匹配当前的环境节点。清单5显示如何运用导入模板。在清单5中有一个问题是导入的模板(discount.xslt)具有mode属性,而主模板却没有mode属性,因此,在主模板并不能正确的导入相应的匹配模板。所以需要利用XSLT文档组装器来完成给主模板增加相应的mode属性。在下一部分中将解决这个问题。

清单4

 <xsl:template match="价格" mode="normal" > <br/> 折扣价格: <xsl:call-template name="discount"> <xsl:with-param name="catalog" select="../类别"/> <xsl:with-param name="price" select="."/> <xsl:with-param name="customerType" select="'一般用户'"/> </xsl:call-template> </xsl:template> <!-- 具有模式属性的模板,它与<apply-templates>的模式属性协同工作--> <xsl:template match="价格" mode="advanced" > <br/> 折扣价格: <xsl:call-template name="discount"> <xsl:with-param name="catalog" select="../类别"/> <xsl:with-param name="price" select="."/> <xsl:with-param name="customerType" select="'高级用户'"/> </xsl:call-template> </xsl:template> 

清单5

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!--导入的样式表组件--> <xsl:import href="discount.xslt"/> …… <!--原有的价格匹配模板--> <xsl:template match="价格"> <br/> 价格: <xsl:value-of select="."/> <!-- 导入的价格匹配模板--> <xsl:apply-imports />         </xsl:template> </xsl:stylesheet> 

XSLT文档组装器的实现

XSLT文档组装器实际上也是一个样式表,它根据要求组装不同的样式表。在XSLT组件设计中所提到的OO思想是通过文档组装器动态生成的。文档组装样式表的思想就是根据客户的请求通过样式表导入来扩展系统的主样式表(产品列表样式表),同时,还要对主样式表进行适当的修改来满足客户的需求。清单6显示了如向根据请求的参数来组装样式表。

清单6

 <xsl:param name="customerType" />    <!--传入的请求参数--> <xsl:template match="xsl:stylesheet " > <xsl:copy > <xsl:apply-templates select="@*"/> <xsl:choose > <!-根据请求参数来正确导入样式表组件 --> <xsl:when test="$customerType='一般用户' or $customerType='高级用户'"> <xsl:element name="xsl:import"> <xsl:attribute name="href" > <xsl:text >discount.xslt</xsl:text> </xsl:attribute> </xsl:element> </xsl:when> <xsl:when test="$customerType='内部用户'"> <xsl:element name="xsl:import"> <xsl:attribute name="href" > <xsl:text >supplier.xslt</xsl:text> </xsl:attribute> </xsl:element> </xsl:when> </xsl:choose > <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:template> <xsl:template match="xsl:template"> …… <!-- 在匹配模板中运用导入的模板-' <xsl:if test="$customerType='内部用户'"> <xsl:if test="@match='供应商'"> <xsl:element name="xsl:apply-imports"/> </xsl:if> </xsl:if> ……. </xsl:template> 

清单6显示了如何组装样式表文档,但正如我们在上一部分讨论的,我们必须解决模板模式(mode)属性匹配的问题。清单7显示了如何解决mode属性匹配。在主样式表模板中动态增加了模式(mode)属性后,就能正确调用导入样式表中的匹配模板。

清单7

 <xsl:template match="xsl:template"> <xsl:copy> <xsl:apply-templates select="@*"/> <!-- 根据请求参数来增加mode属性 --> <xsl:if test="$customerType='一般用户' or $customerType='高级用户' "> <xsl:if test="not(@match='/')"> <xsl:attribute name="mode"> <xsl:if test="$customerType='一般用户'"> <xsl:text >normal</xsl:text> </xsl:if> <xsl:if test="$customerType='高级用户'"> <xsl:text >advanced</xsl:text> </xsl:if> </xsl:attribute> </xsl:if> <!-- 调用导入样式表中的匹配模板,由于主样式表中模板增加了相应的模式(mode)属性,它可以正确的调用导入样式表的相应模板 --> <xsl:if test="@match='价格'"> <xsl:element name="xsl:apply-imports"/> </xsl:if> </xsl:if> <xsl:if test="$customerType='内部用户'"> <xsl:if test="@match='供应商'"> <xsl:element name="xsl:apply-imports"/> </xsl:if> </xsl:if> <xsl:apply-templates select="xsl:param"/> <xsl:apply-templates select="node()[not(name()='xsl:param')]"/> </xsl:copy> </xsl:template> 

至此,我们已完成了样式表的设计,我们设计了一个简单的来验证系统。清单8是测试工具的源代码。列表7是内部用户产品样式表的完整源代码, 列表8是一般用户产品样式表的完整源代码, 列表9是高级用户产品样式表的完整源代码。它们都是通过XSLT文档组装器生成的。

清单8

 import javax.xml.transform.*; import java.io.*; import javax.xml.transform.stream.*; public class test{ public static void main(String[] args){ try{ StringWriter sw=new StringWriter(); StringWriter sw2=new StringWriter(); TransformerFactory tf=TransformerFactory.newInstance(); // XSLTCreator.xslt是样式表组装器样式表// Transformer trans=tf.newTransformer(new StreamSource(new File("XSLTCreator.xslt"))); //应用程序传递参数给组装器样式表,以生成所需的样式表// trans.setParameter("customerType","一般用户"); //product_skeleton.xml是系统的主样式表// trans.transform(new StreamSource(new File("product_skeleton.xml")),new StreamResult(sw)); Transformer trans2=tf.newTransformer(new StreamSource(new StringReader(sw.toString()))); trans2.transform(new StreamSource(new File("products.xml")),new StreamResult(sw2)); FileOutputStream fo=new FileOutputStream(new File("test")); System.out.println(sw2.toString()); fo.write(sw.toString().getBytes()); fo.close(); }catch(Exception e){ e.printStackTrace(); } } } 

实例

为了说明XSLT编程中的面向对象思想,特提供如下的实例以供参考。在实例是一个简单的基于WEB的内容分布系统。它根据客户的请求动态生成样式表,进而提供相应的响应页面。本实例只是简单的实现了面向对象的XSLT编程的思想。系统的本身并没有进行很好的功能的优化。图一显示了系统的时序图


图一 系统的时序图

XSLTServlet类是用户请求的集中控制点和服务的起始点,XSLTCreator类是XSLT组装器的核心。清单9是XSLTCreator的实现的主要部分,它根据用户请求动态调用或生成样式表来生成相应的响应页面。完整的代码见列表10.

清单9

 public void xsltCreator(HttpServletRequest req,HttpServletResponse resp) throws IOException, SAXException{ StringWriter sw=new StringWriter(); StringWriter sw2=new StringWriter(); String userType; try{ TransformerFactory tf=TransformerFactory.newInstance(); tf.setURIResolver(new URIResolver(){ public Source resolve(String href,String base) { StringBuffer path = new StringBuffer(base_path); path.append(File.separator).append(href); File file = new File(path.toString()); if(file.exists()) return new StreamSource(file); return null; } }); Transformer trans= tf.newTransformer(new StreamSource (locator.getCachFile("XSLTCreator.xslt"))); HttpSession session=req.getSession(); if(req.getParameter( "User")!=null) session.setAttribute( "userType",req.getParameter("User" )); userType=(String)session.getAttribute( "userType"); trans.setParameter("customerType",userType); trans.transform(new StreamSource (locator.getCachFile("product_skeleton.xml")),new StreamResult(sw)); Transformer trans2= tf.newTransformer(new StreamSource (locator.getCachFile("productFilter.xslt"))) ; String type=(String)req.getParameter("productType"); trans2.setParameter("type",type); trans2.transform(new StreamSource (locator.getCachFile("products.xml")),new StreamResult(sw2) ); Transformer trans3= tf.newTransformer(new StreamSource( new StringReader(sw.toString()))); trans3.transform( new StreamSource(new StringReader(sw2.toString())) new StreamResult(resp.getWriter())); } catch (TransformerConfigurationException e) { PrintWriter pw=resp.getWriter() ; pw.println("<html><body><h2>tansformer error<h2><pre>"); e.printStackTrace( pw); pw.println("</pre></body></html>" ); } catch (TransformerFactoryConfigurationError e) { … } catch (TransformerException e) { … } } 

系统的入口如下图二所示


图二 实例的系统入口页面

系统根据用户的请求来动态生成不同的页面。图三就是以高级用户登录后所显示的内容。


图三 动态生成的产品列表

总结

本文通过设计多个XSLT文档组件来减少样式表设计的复杂程度,同时,利用XSLT文档组装器来完成复杂,动态的样式表的生成。

参考资料

  • 下载本文中所使用的源代码及如何使用。
  • J.R.Gardner等人著作的XSLT and Xpath: A guid to XML transformations
  • 在 developerWorks XML 专区查找更多关于 XML 的参考资料。

关于作者

谢强,西安交通大学系统工程所的硕士研究生,主要从事Linux的集群技术的研究,并对XML,J2EE技术有着浓厚的兴趣,喜欢钻研技术。您可以通过richard_xieq@yahoo.com.cn和他联系。

转载于:https://www.cnblogs.com/sunsonbaby/archive/2005/01/17/92909.html

面向对象的 XSLT编程相关推荐

  1. (转)面向对象的 JavaScript 编程:dojo.declare 详解

    >>>>>http://www.ibm.com/developerworks/cn/<<<<< JavaScript 早在发明初期时,仅用来 ...

  2. 面向对象的 JavaScript 编程及其 Scope 处理

    为什么80%的码农都做不了架构师?>>>    在面向对象的 JavaScript 编程中,我们常常会将一些数据结构和操作封装成对象以达到继承和重用的目的.然而层层封装和继承再加上 ...

  3. 面向对象的JavaScript编程

    面向对象的JavaScript编程     Javascript对于做过Web程序的人不应该是陌生,初期是用来做一些简单的FORM验证,基本上是在玩弄一些技巧性的东西.IE 4.0引入了DHTML,同 ...

  4. 《Java8实战》笔记(15):面向对象和函数式编程的混合-Java 8和Scala的比较

    面向对象和函数式编程的混合:Java 8和Scala的比较 Scala是一种混合了面向对象和函数式编程的语言.它常常被看作Java的一种替代语言,程序员们希望在运行于JVM上的静态类型语言中使用函数式 ...

  5. Reactor事件驱动的两种设计实现:面向对象 VS 函数式编程

    内容目录: Reactor实现架构对比 面向对象的Reactor方案设计 函数式编程的Reactor设计 示例对比 两者的时序图对比 结论 Reactor事件驱动的两种设计实现:面向对象 VS 函数式 ...

  6. 面向对象的Python编程,你需要知道这些!

    摘要:Python 没有像 java 中的"private"这样的访问说明符.除了强封装外,它支持大多数与"面向对象"编程语言相关的术语.因此它不是完全面向对象 ...

  7. python递归 及 面向对象初识及编程思想

    递归 及 面向对象初识及编程思想 一.递归 1.定义: 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. (1)递归就是在过程或函数里调用自身: (2)在使用递归策 ...

  8. python中的递归思想_〖Python〗-- 递归、面向对象初识及编程思想

    [递归.面向对象初识及编程思想] 一.递归 1.定义: 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. (1)递归就是在过程或函数里调用自身: (2)在使用递归策 ...

  9. 【2023】Kotlin教程 第二篇 面向对象与函数式编程 第15章 数据容器——数组和集合 15.2 集合概述

    [2023]Kotlin教程 文章目录 [2023]Kotlin教程 第二篇 面向对象与函数式编程 第15章 数据容器--数组和集合 15.2 集合概述 第二篇 面向对象与函数式编程 第15章 数据容 ...

最新文章

  1. Mybatis-增删改查模糊查询分页注解(普通类型参数、引用类型参数、Map类型参数)
  2. 软件开发工程师证书有用吗_bim工程师证书有用吗 含金量怎么样?
  3. 向maven中央仓库提交jar
  4. mysql nosql 同步_使用canal和canal_mysql_nosql_sync同步mysql数据
  5. 千万级的mysql数据库与sql优化方法
  6. RabbitMQ—AMQP协议重要概念介绍
  7. 来自后端的突袭? --开包即食的教程带你浅尝最新开源的C# Web引擎 Blazor
  8. 2019年最流行的10个前端框架
  9. LeetCode--34.在排序数组中查找元素第一个和最后一个位置(二分法)
  10. Oracle学习 第18天 .NET连接Oracle
  11. 投放Facebook广告,跑到爆品之后怎样扩量效果更好?
  12. html div画圆有什么用,圆形div栏,用于评级html5 js(Circle div bar for rating html5 js)
  13. http ,servlet
  14. ROS☞rosbag/rostopic消息记录、回放、转.txt
  15. 沃德天,Python竟然还能做实时翻译
  16. C语言代码绘制,利用数组输出 0-2Π之间的 sin 函数图像和 cos 函数图像,实验报告及代码。
  17. php 正则车架号,js 正则校验车架号VIN
  18. 做带团长的社区团购吗?必死那种。
  19. 【ChatGPT调教】调教ChatGPT案例
  20. mongodb mysql 知乎_为什么 MongoDB 索引选择B-树,而 Mysql 索引选择B+树(精干总结)...

热门文章

  1. 快到年底了,小伙伴开始关心年终奖话题了
  2. 想多赚点钱,一个最简单的办法,找到适合自身的坑
  3. 社群模式:什么是普通人赚钱的核心秘密
  4. sql字符处理函数concat()、concat_ws()
  5. Qt4_实现Edit菜单
  6. 解决: Windows下启动Redis失败
  7. sql server运算符_SQL Server执行计划中SELECT运算符的主要概念
  8. oracle 事务快照_了解事务和快照复制的分布规模
  9. 在Azure ML中使用Logistic回归预测客户的贷款资格
  10. 严格单调递增与非严格之间的转换