UML软件工程组织

面向对象的XML文档设计与样式表编程
谢强 (richard_xieq@yahoo.com.cn)
西安交通大学系统所硕士  转载 IBM

   样本应用程序的概述
   XML文档的设计
   XSLT的模块化设计
   XSLT的动态设计
   总结
   参考资料
   关于作者

XML作为一种以文本文件方式来存储的结构化的数据的机制。XML本身只说明数据的结构而并不关心数据是如何具体描述的、数据是否正确。但通过面向对象的建模技术和XML 模式所提供的语法约束和形式化的功能,就可以建立对象化的XML来描述现实世界中复杂的概念了。同时,XML主要是用于表示数据的,难以描述数据的显示格式。而样式表提供了一种对XML数据进行结构化转换的强大的工具,通过样式表可以使XML文档的显示多样化。本文介绍了在XML的设计与编程中运用面向对象的思想以及如何结合XML文档与XSLT样式表动态生成WEB网页。

样本应用程序的概述
为了演示面向对象的XML设计与样式表的编程,我将以课程目录系统为例,在课程目录系统中,学生或教师可以浏览各学期开设的课程以及相应的详细信息,例如授课教师的信息等。而所有这些信息都存储在XML文档中。现实世界中这些信息可能存储在不同的XML文档中,在这里为了简化问题,假设所有的信息都在一个XML文档中。系统根据用户的请求动态的提取有效的信息并生成响应HTML页面。在本文中,采用了一种基于样式表的编程模式,它的核心是根据用户请求的内容,将不同的样式表作用于XML文档,生成相应的响应HTML页面。整个过程中动态进行的。图1 显示了系统的工作原理。

图1 工作原理

XML文档的设计

1. 概念模型
首先,根据系统的需求,建立概念模型。系统的对象模型图2所示:

图2 对象模型

2. XML模式
根据UML图,我们设计了相应的XML模式

1.设计数据类型封装UML类图中各个类的属性

根据概念模型中的类图,将各个类转换为XML模式中的数据类型,类中的属性转换为数据类型中的元素或属性。清单1为转换的个人数据类型,清单2为转换的课程数据类型。完整的XML模式文档见列表1

清单1:

		<xs:complexType name="personType">
		<xs:sequence>
			<xs:element name="name" type="xs:string"/>
			<xs:element name="age" type="xs:integer"/>
			<xs:element name="email" type="xs:string"/>
		</xs:sequence>
		</xs:complexType>

清单2:

		<xs:complexType name="courseType">
		<xs:sequence>
			<xs:element name="courseNum" type="xs:string"/>
			<xs:element name="courseName" type="xs:string"/>
			<xs:element name="credit" type="xs:integer"/>
		</xs:sequence>
		</xs:complexType>

2.类图中类的关系在模式中实现

类图中的关系有关联,聚合和继承。根据图1所示的对象模型中,存在以下的关系:

  • 系与教师之间存在聚合关系
  • 课程目录与开设课程之间聚合关系
  • 课程与开设课程之间存在一对多的关联
  • 教师与个人之间存在继承关系

1. 聚合关系的实现

清单3是系与教师之间存在聚合关系的实现

清单3:

		<xs:complexType name="departmentType">
		……
		<xs:element ref="catalog:professor" maxOccurs="unbounded"/>  
		……
		</xs:complexType>

2. 关联关系

课程目录与开设课程之间关联关系,在样例中只保留开设课程到课程的导航关系.即在开设课程有到课程的关联联结.关联的实现是在课程中定义代表课程编号的主键,在开设课程中定义引用主键.。清单4是开设课程与课程关联的实现。

清单4:

		<xs:complexType name="courseType">
			<xs:element name="courseNum" type="xs:string"/> //课程编号主键
				……
		</xs:complexType>

		<xs:complexType name="courseOfferingType">
		……
		<xs:element name="courseRepresented" type="xs:string"/>  //对应的课程编号
		……
		</xs:complexType>
		
		<xs:key name="courseNumKey">
			<xs:selector xpath=".//catalog:course"/>
			<xs:field xpath="catalog:courseNum"/>
		</xs:key>
		
		<xs:keyref name="courseNumKeyRef" refer="catalog:courseNumKey">
			<xs:selector xpath=".//catalog:courseOffering"/>
			<xs:field xpath="catalog:courseRepresented"/>
		</xs:keyref>

3.继承关系

清单5是教师类继承了个人关系的实现.

清单5:

		<xs:complexType name="professorType">
		……
			<xs:extension base="catalog:personType">
      ……
	</xs:complexType>

通过上面的步骤,我就得到了面向对象的XML模式(列表1),根据XML模式我就能得到结构良好得XML文档,接下来讨论XSLT的模块化设计。

XSLT的模块化设计
XSLT样式表是由一系列的模板以及基于XPath表达式的指令构成。XSLT处理模型包含两种方式:声明式与命令式。声明式类型(也称为拉模型)中,我们告诉处理程序,当它在输入文档中遇到特定的XML元素时,对它处理。而命令式模型(也称为推模型)中,我们明确告诉处理程序如何在输入文档中进行搜索并处理之中的元素。

XSLT的模块化设计目标是设计可重用的,可扩展的XSLT组件,它包括四个重要的思想: 中间状态;功能模块化,外部资源与扩展。在这里主要讨论中间状态和功能模块化。中间状态就是利用变量与参数创建临时的XML结构。功能模块化就是利用命名模板和模式匹配模板,有效的封装XSLT的转化例程,以便重用。文献1中有更多的关XSLT模块化编程的介绍。

根据系统的需求设计,我们设计了三个XSLT文档:目录XSLT,课程XSLT,教师XSLT

1.目录XSLT 目录XSLT用来作用于XML文档,输出开设课程目录。在目录XSLT中接受三个参数,一个代表学期的参数;由于目录可能具有大量条目,需要分页设计,所以另两个参数用来代表每页要显示的条目以及当前要显示的分页。根据这些参数,XML转化后输出相应学期的开设课程的目录。在目录XSLT中有效使用了命名模板,变量,节点集来建立高效的模块化的样式表。它通过XML树分枝过滤节点得到所需的节点集并赋予变量,而命名模板就象函数模板,它接受节点集参数实现转换。

在本样例中使用Apache xalan-j的处理器。它支持节点集的生成。清单6显示了如何在xalan处理器中生成XML树分枝(XML Tree Fragment)以及节点集,同时显示了如何从XML文档中过滤生成节点集。清单7显示如何定义命名模板并将节点集作为参数传给模板。

清单6:

<xsl:variable name="pagedItemstf">
     <xsl:call-template name="page.filter">
       <xsl:with-param name="dataset" select="$sortedItems"/>
         <xsl:with-param name="page" select="$page"/>
         <xsl:with-param name="pageSize" select="$pageSize"/>
     </xsl:call-template>
</xsl:variable>            
<xsl:variable name="pagedItems" select="xalan:nodeset($pagedItemstf)"/>
<xsl:template name="page.filter">
    <xsl:param name="dataset"/>
    <xsl:param name="page"/>
    <xsl:param name="pageSize"/>
    <xsl:for-each select="$dataset/catalog:courseOffering[(position()>($page - 1) * $pageSize) and (position() <= $page * $pageSize)]">
          <xsl:copy-of select="."/>
     </xsl:for-each>
 </xsl:template>
<!-如何过滤XML文档生成节点集-->

清单7:

<xsl:template name="items.display">
		<xsl:param name="pagedItems"/>
	<! - 命名模板的参数-->
			<table border="2">
			<tr>
				<th>offeringNum</th>
				<th>Room</th>
				<th>instructor</th>
				<th>courseNum</th>
			</tr>
			<xsl:for-each select="$pagedItems/*">
			<!-迭代-->
				<tr>
					<td>	<xsl:value-of select="catalog:offeringNum"/></td>
					<td>	<xsl:value-of select="catalog:room"/>	</td>
					<td>	<xsl:value-of select="catalog:instructor"/></td>
					<td>	<xsl:value-of select="catalog:courseRepresented"/>	</td>
				</tr>
			</xsl:for-each>
		</table>
	</xsl:template>

整个目录XSLT文档见列表2

2.课程XSLT与教师XSLT

用户在课程目录网页点击查看课程信息后,课程XSL作用于XML文档并输出相应的课程详细资料的网页。由于它要显示相应课程的信息,因此它需要一个代表课程号的参数。当用户在课程目录网页点击了查看教师信息后,教师XSL作用于XML文档并输出相应的教师详细资料的网页.由于它要显示相应教师的信息,因此它需要一个代表教师号的参数。这两个XSLT文档是基于拉模型的XSLT。常见的拉模型的样式表是基于模板匹配。清单8显示了匹配模板实现。完整的课程XSLT见列表3;完整的教师XSLT见列表4

清单8:

<xsl:template match="catalog:course">
		<xsl:if test="catalog:courseNum[contains(.,$param)]">
		<form >
			……
		</form>
		</xsl:if>
	</xsl:template>

XSLT的动态设计
XSLT的动态设计目标是运行时动态调用XSLT文档。也就是在运行时根据不同的用户请求,将多个不同的XSL文档,作用于XML文档后,产生不同的输出。XSLT动态设计的重点采用前端控制器模式与命令模式完成XSLT的动态调用。

1.控制器的设计

控制器是一个Servlet类,它是用户请求的集中控制点和服务的起始点, 它委托请求助手来分析用户请求并动态生成网页以显示正确的响应信息.它采用J2EE模式中的前端控制器模式以及命令和控制器策略。文献3中有更多的关于J2EE模式介绍。在控制器中主要完成一些初始化工作以及任务的委派。它的时序图如图3所示。清单9显示了控制器的工作流程。完整的代码见列表5

图3 控制器的时序图

清单9:

public class XMLServlet2 extends HttpServlet {
	static Document document=null;
	// 完成一些初始化工作
	public void init(ServletConfig config) throws ServletException{
		super.init(config);
					…..
			document = builder.parse( xmlSystemid);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
			ServletContext context=getServletContext();
			RequestHelper helper=new RequestHelper(req,context);
			Command command=helper.getCommand();
			command.execute(resp,document);
	}
}

2.请求助手的设计

RequestHelper类根据请求创建具体的转化命令。它实现了工厂方法模式.清单10是请求助手工厂方法的实现。完整的代码见列表6

清单10:

public Command getCommand(){
		String op=(String)request.getParameter("operation");
		String param=(String)request.getParameter("param");
		if(op.equals("viewcatalog")){
			try {
				URL url=context.getResource("/WEB-INF/xmlfiles/test.xsl");
				String pagesize=(String)request.getParameter("pageSize");
				return new CatalogTransformer(url,param,pagesize);
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
			
		}
		if(op.equals("viewProf")){
			try {
				URL url=context.getResource("/WEB-INF/xmlfiles/profDetail.xsl");
				return new TransformerCommand(url,param);
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
		}
		if(op.equals("viewCourse")){
			……
		}
		return null;
	}

3.转换命令的设计

各个具体的转换类的实现采用了命令模式。Command接口定义了转换方法. 各个转换命令实现了command接口。清单11显示了Command接口的定义,清单12显示了一个具体的转换类(CatalogTransformer类)的实现。更多的的模式在文献4中可找到。

清单11:

public interface Command {
	public void execute(HttpServletResponse resp,Document doc) throws IOException;
}

清单12:

public class CatalogTransformer implements Command { 
	public void execute(HttpServletResponse resp, Document doc)
		throws IOException {
			String xslSystemid=xlsURL.toExternalForm();
		try {
			TransformerFactory transFactory=TransformerFactory.newInstance();
			Templates sytlesheet=transFactory.newTemplates( new StreamSource(xslSystemid));
			Transformer transformer=sytlesheet.newTransformer();
			PrintWriter out=resp.getWriter();
			StreamResult result=new StreamResult(out);
			transformer.setParameter("param",param);
			transformer.setParameter("pageSize",pageSize);
			transformer.transform( new DOMSource(doc),result);
		} catch (TransformerConfigurationException e) {			
		} catch (TransformerFactoryConfigurationError e) {
		} catch (IOException e) {
		} catch (TransformerException e) {
		}
	}
}

整个样例程序都是Websphere Studio Application Developer 5.0 中开发,整个的WEB模块可在此下载

总结
通过面向对象的原理,设计可重用的XML文档,并结合XML文档与样式表动态生成HTML网页。

参考资料

 

关于作者

谢强谢强: 西安交通大学系统所硕士,主要从事Linux的集群技术的研究,并对XML,J2EE技术有着浓厚的兴趣。喜欢钻研技术,目前的目标是成为IBM认证的企业应用开发者。您可以通过richard_xieq@yahoo.com.cn和他联系。

 

版权所有:UML软件工程组织