分享
 
 
 

XML应用-利用XML 与XSL 开发一个易于修改和扩充的用户手册

王朝c#·作者佚名  2006-12-17
窄屏简体版  字體: |||超大  

版权声明: 本文可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息。

原文出处: http://www.aiview.com/notes/xml_xsl_manual.htm

作者: 张洋 Alex_doesAThotmail.com

最后更新: 2001-12-15

目录

用户的需求 对应的技术实现 定义DTD 如何准备手册内容 最关键的部分: 开发XSL 将各部分粘合起来 参考资源

本文将介绍利用XML与XSL技术内容与表示相分离的特点,创建一个易于扩充,结构灵活的用户手册文档。当然,本文的重点在于阐述一种利用XML的方法,并不仅限于用户手册文档,您可以利用在任何您认为需要的地方。

本文介绍的内容基于XML、XSL与DTD技术规范,您可以使用IE5.0及其以上版本进行测试。

用户的需求从用户的视角,我们要实现的用户手册包含以下几个部分:

在页面左侧的导航栏,利用树状显示每一个级别的标题,每个标题前按顺序标有标题序号,形如2.1或2.1.8,对于包含子标题的标题要与不包含子标题的标题,即叶子标题区分显示,前面用不同的图片区分。 页面右侧是内容栏,除包含各级标题外,还包含各级标题下的具体内容,内容类型有文字和图片。不同级别的标题要使用不同的现实方式,每个标题前也加有标题序号,与左侧导航栏一致。 对于含有图片的内容部分,图片显示为缩略图,通过点击可以在正常大小与缩略图之间切换。 点击左侧导航栏的标题,可以定位到右侧的相关内容。 对于手册的标题和内容,应该可以方便添加与删除,修改了一个标题的级别,比如从一级标题换到三级标题的位置,其显示风格可以自动更改。理论上,标题的级别可能会有无限多层。 无论是左侧的导航栏,还是右侧的内容区域,用户只对标题的相对位置和级别负责,标题前面的序号要求自动生成。对应的技术实现我们打算开发如下几个部分来实现这个用户手册:

一个DTD定义文件,即Document Type Defination(文档类型定义)。定义这个文件需要根据客户对于手册的需求来做,这也是我们需要首先着手去做的部分。 一个XML文档,包含了用户手册的真实的、具体的内容,这个文档当然应该是良构的,需要遵循我们在上面DTD文件中定义的规则。这个文档的内容由手册的编写者来提供,他无需了解其他的技术细节,只需遵照一个简单的规则即可,如果能够使用像XMLSPY这样的工具编辑会更省力。 两个XSL文件,这是我们需要开发的主体部分,也就是决定内容将如何呈现给用户,共有两个XSL文件,分别针对左侧的导航栏和右侧的内容主体。 一个超文本文件,构成用户手册的框架,实现导航栏与内容主体的分割。定义DTD通过前面的需求陈述,可以抽象出如下页面元素:

标题,会有不同的级别 内容,包括文本和图片为此我们定义如下DTD元素:

<!ELEMENT Image (#PCDATA)><!ELEMENT Para (#PCDATA)><!ELEMENT Name (#PCDATA)>

分别表示图片、段落文本和标题。为了表示标题的嵌套关系,我们又定义一个Item元素,把它作为一个块,不同的Item之间可以是平行关系,也可以是嵌套关系,所有的基本元素(图片、段落文本和标题)必须从属于一个Item。具体的关系描述如下:

一个Item中只能包含、并且必须包含一个标题(Name) 一个Item中可以同时包含多段文字或者多张图片,或者只包含其中一种 一个Item中可以嵌套一个或多个Item,也可以不嵌套任何Item,嵌套的Item具有相同的属性通过以上描述,我们可以定义元素Item:

<!ELEMENT Item (Name, (Para | Image)*, Item*)>

元素名称后面的'*' 代表此元素可以为0个或者多个。

'(Para | Image)*' 与 'Para*, Image*' 同义。

作为DTD,还必须定义一个根元素,我们起名为'PMS_Help',它将包含Item元素,进而间接的包含了所有的元素。

<!ELEMENT PMS_Help (Item*)>

上面表示为,使用了这个DTD的XML文档中可以定义0个或者多个Item元素。

此外,我们还需要为图片的缩略图定义:

<!ATTLIST Image small ID #IMPLIED>

至此,DTD文件我们就已经定义完了,我们将其保存为PMS_Help.dtd,这里下载完整的DTD文件。

如何准备手册内容接下来我们要根据上面定义的DTD文件,整理含有实际内容的XML文件,文件内容示例如下:

<PMS_Help> <Item> <Name>第一级别标题</Name> <Item> <Name>第二级别标题</Name> <Item> <Name>第三级别标题</Name> <Para>段落文本1</Para> <Image small='image1_small.gif'>image1.gif</Image> <Para>段落文本2</Para> <Para>段落文本3</Para> </Item> <Item> ... </Item> </Item> <Item> ... </Item> </Item> <Item> ... </Item></PMS_Help>

在第三级标题下面,当然还可以继续嵌套Item元素,有更多级别的标题,只要你在下面的XSL样式文件中对应说明了这个级别标题如何显示即可。

在Para标记之间的文本在显示时会被格式化为一个段落,如果你有多段文本,应该分别用Para进行标记。

Image标记中有一个属性字段small,其值应是一个图片文件名,这个图片用作默认的缩略图显示,当点击这个图片,会显示出Image标记之间的图片文件,当然您也可以对着两个图片使用同一个文件名。图片文件的路径不需要在这里指定,路径是写在下面XSL文件中的。

当我们把包含手册内容的XML文件准备好之后,我们还需要做一个额外的工作,就是将此XML文件复制一份,两个文件分别取名为:PMS_Help.xml, PMS_Help_Lf.xml,两个文件分别绑定不同的XSL样式文件,以取得不同的显示效果,除此以外,两个XML文件是完全相同的。下载两个XML文件:导航栏XML文件,内容栏XML文件

最关键的部分: 开发XSL1. 首先实现相对简单一点的对应左侧导航栏的XSL文件。

XML文件以一定的结构描述了我们要展现的内容,而在XSL当中,决定了这些内容将以何种方式展现,实际上是XSL是将XML文档格式化为浏览器可以理解的HTML格式文本,在XSL中首先需要定义根级的模板,实际对应了HTML文档的Head以及Body部分,代码如下:

<xsl:template match='PMS_Help'><html><head><title>Index</title><style media='screen'><xsl:comment><![CDATA[body {font-fimaly:'宋体',Arial; font-size:9pt; color:#000000;}]]></xsl:comment></style></head><body><h2>Index</h2><p/><xsl:for-each select='Item'><xsl:apply-templates select='.'/></xsl:for-each></body></html></xsl:template>

接下来实现对应处理Item部分的模板:

<xsl:variable name='allParentNode' select='ancestor::Item/Name'/><xsl:variable name='allChildNode' select='child::Item/Name'/><xsl:variable name='numOfAllParentNode' select='count($allParentNode) + 1'/><xsl:variable name='numOfAllChildNode' select='count($allChildNode)'/><!-- output the Index of help BEGIN --><xsl:element name='div'><xsl:attribute name='onclick'>window.open('PMS_Help.xml#<xsl:number count='Item' level='multiple' format='01-01-01'/>','main')<!-- <xsl:value-of select='&quot;alert(this.id)&quot;' /> --></xsl:attribute><xsl:attribute name='style'><xsl:value-of select='concat('text-indent:',(($numOfAllParentNode - 1) * 30),';white-space: nowrap')'/></xsl:attribute><xsl:attribute name='id'><xsl:number count='Item' level='multiple' format='01-01-01'/></xsl:attribute><xsl:attribute name='onmouseover'><xsl:value-of select='&quot; this.style.color='red';this.style.cursor='hand' &quot;'/></xsl:attribute><xsl:attribute name='onmouseout'><xsl:value-of select='&quot; this.style.color='black' &quot;'/></xsl:attribute><xsl:choose><xsl:when test='$numOfAllParentNode = 0'><img src="http://doc.readmen.com/1/images/page/minus.gif" alt='' border='0'/></xsl:when><xsl:when test='$numOfAllParentNode != 0 and $numOfAllChildNode = 0'><img src="http://doc.readmen.com/1/images/page/passage.gif" alt='' border='0'/></xsl:when><xsl:otherwise><img src="http://doc.readmen.com/1/images/page/minus.gif" alt='' border='0'/></xsl:otherwise></xsl:choose><xsl:number count='Item' level='multiple'/>_<xsl:value-of select='Name'/></xsl:element><!-- 如果还存在下一级Item,则递归调用此模板 --><xsl:if test='count(Item)>0'><xsl:apply-templates select='Item'/></xsl:if></xsl:template>

在上面的代码中,我们通过如下的方式定义变量:

<xsl:variable name='allParentNode' select='ancestor::Item/Name'/>

其中,allParentNode是我们为变量取的名字,后面使用select关键字为其赋值,ancestor关键字用于取出后面给定节点的所有父节点,Item/Name是我们在DTD中定义的节点名称。之后我们可以通过 $allParentNode 来引用这个变量。接下来我们使用:

<xsl:variable name='numOfAllParentNode' select='count($allParentNode) + 1'/>

取出当前节点所有父节点的数量。使用以下代码能够在XSL当中实现一个类似case语句的功能,用来判断是否是叶子节点,以显示不同的图片。

<xsl:choose><xsl:when test='$numOfAllParentNode = 0'><img src="http://doc.readmen.com/1/images/page/minus.gif" alt='' border='0'/></xsl:when><xsl:when test='$numOfAllParentNode != 0 and $numOfAllChildNode = 0'><img src="http://doc.readmen.com/1/images/page/passage.gif" alt='' border='0'/></xsl:when><xsl:otherwise><img src="http://doc.readmen.com/1/images/page/minus.gif" alt='' border='0'/></xsl:otherwise></xsl:choose>

由于我们的DTD中不限制Item定义的级别,因此,我们需要递归的处理Item的显示,使用如下语句:

<xsl:if test='count(Item)>0'><xsl:apply-templates select='Item'/></xsl:if>

我们将此XSL文件保存为PMS_Help_Lf.xsl,下载此文件。

2. 接下来实现对应右侧具体内容展示的XSL。

同样,也需要定义Head和body部分,不同的是,这里定义了更加复杂的样式单(CSS),并增加了一段Javascript脚本:

<xsl:template match='PMS_Help'><html><head><title>Content</title><style media='screen, print'><xsl:comment><![CDATA[body {font-fimaly:'宋体',Arial; font-size:9pt; color:#000000;}.Title1 {font-fimaly:'宋体'; font-size:17.2pt; color:#333333; font-weight:bold; margin-left:0pt; background-color:#eeeeee; white-space: nowrap}.Title2 {font-fimaly:'宋体'; font-size:14pt; color:#000000; font-weight:bold; margin-left:0pt; white-space: nowrap}.Title3 {font-fimaly:'宋体'; font-size:12pt; color:#333333; font-weight:bold; margin-left:90pt; background-color:#eeeeee; white-space: nowrap}.Title4 {font-fimaly:'宋体'; font-size:12pt; color:#000000; font-weight:bold; margin-left:90pt; white-space: nowrap}.Title5 {font-fimaly:'宋体'; font-size:10pt; color:#333333; font-weight:bold; margin-left:160pt; background-color:#eeeeee; white-space: nowrap}.Title6 {font-fimaly:'宋体'; font-size:10pt; color:#000000; font-weight:bold; margin-left:160pt; white-space: nowrap}.Para1 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}.Para2 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}.Para3 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}.Para4 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:90pt}.Para5 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:160pt}.Para6 {font-fimaly:'宋体'; font-size:9pt; color:#333333; margin-left:160pt}.image {cursor:hand}]]></xsl:comment></style><script><xsl:comment><![CDATA[/* 当用户点击图片时,交替显示缩略图和全图 */function swapImg(which, small, normal){var s = '/';//alert(which + '|' + small + '|' + normal);//alert(which.src.substring(which.src.lastIndexOf(s)+1));if (which.src.substring(which.src.lastIndexOf(s)+1) == small){which.src = 'images/' + normal;which.alt = '===> 缩小 <===';}else{which.src = 'images/' + small;which.alt = '<=== 放大 ===>';}}]]></xsl:comment></script></head><body><a name='top'/><h2>Content</h2><p align='right'><a href='mailto:yourname@yourdomain.com'>EMail to us</a><xsl:text> </xsl:text><a href='javascript:print()'>打印本手册</a></p><xsl:for-each select='Item'><xsl:apply-templates select='.'/></xsl:for-each></body></html></xsl:template>

接下来是处理Item的部分:

<xsl:template match='Item'><xsl:variable name='allParentNode' select='ancestor::Item/Name'/><xsl:variable name='allChildNode' select='child::Item/Name'/><xsl:variable name='numOfAllParentNode' select='count($allParentNode) + 1'/><xsl:variable name='numOfAllChildNode' select='count($allChildNode)'/><!-- output the content of help --><!-- 显示各级标题 --><xsl:element name='a'><xsl:attribute name='name'><xsl:number count='Item' level='multiple' format='01-01-01'/></xsl:attribute></xsl:element><xsl:element name='div'><xsl:attribute name='class'><xsl:value-of select='concat('Title', $numOfAllParentNode)'/></xsl:attribute><xsl:number count='Item' level='multiple'/><xsl:text> </xsl:text><xsl:value-of select='Name'/><!-- 级别为奇数的标题下加横线 --><xsl:if test='$numOfAllParentNode = 1 or ($numOfAllParentNode mod 2) != 0'>::<a href='#top' target='_self'>top</a><hr size='1' noshade='noshade'/></xsl:if></xsl:element><p/><!-- 如果还存在下一级Item,则递归调用此模板 --><xsl:if test='count(Item)>0'><xsl:apply-templates select='Item'/></xsl:if><!-- 如果不存在下一级标题,显示段落和图片内容 --><xsl:if test='count(Item)=0'><xsl:apply-templates select='Para | Image'/></xsl:if></xsl:template>

为了以不同的样式显示不同级别的标题我们做了如下处理,根据级别的不同,选择不同的样式单(CSS)定义:

<xsl:attribute name='class'><xsl:value-of select='concat('Title', $numOfAllParentNode)'/></xsl:attribute>

额外的,我们在级别为奇数的标题下画上横线:

<!-- 级别为奇数的标题下加横线 --><xsl:if test='$numOfAllParentNode = 1 or ($numOfAllParentNode mod 2) != 0'>::<a href='#top' target='_self'>top</a><hr size='1' noshade='noshade'/></xsl:if>

同样的,我们也需要递归调用此模板进行处理:

<!-- 如果还存在下一级Item,则递归调用此模板 --><xsl:if test='count(Item)>0'><xsl:apply-templates select='Item'/></xsl:if>

与上一个XSL不同,我们在这里还需要处理Para和Image标记,这些内容在导航栏是不需要显示的。我们定义了模板:

<xsl:template match='Para | Image'><xsl:variable name='allParentNode' select='ancestor::Item/Name'/><xsl:variable name='numOfAllParentNode' select='count($allParentNode) + 1'/><!-- 如果节点是Para,显示段落 --><xsl:if test='self::Para'><xsl:element name='div'><xsl:attribute name='class'><xsl:value-of select='concat('Para', ($numOfAllParentNode) - 1)'/></xsl:attribute><p><xsl:value-of select='.'/></p></xsl:element></xsl:if><!-- 如果节点是Image,显示图片 --><xsl:if test='self::Image'><xsl:variable name='small' select='normalize-space(@small)'/><xsl:variable name='normal' select='normalize-space(.)'/><xsl:element name='div'><xsl:attribute name='class'><xsl:value-of select='concat('Para', ($numOfAllParentNode) - 1)'/></xsl:attribute><xsl:element name='img'><xsl:choose><xsl:when test='boolean($small)'><xsl:attribute name='src'>images/<xsl:value-of select='$small'/></xsl:attribute><xsl:attribute name='alt'>&lt;=== 放大 ===&gt;</xsl:attribute><xsl:attribute name='onclick'><xsl:value-of select='concat('swapImg(this, &quot;',$small,'&quot;,&quot;',$normal,'&quot;)')'/></xsl:attribute></xsl:when><xsl:when test='not(boolean($small))'><xsl:attribute name='src'>images/<xsl:value-of select='$normal'/></xsl:attribute></xsl:when></xsl:choose><xsl:attribute name='class'>image</xsl:attribute><xsl:attribute name='border'>0</xsl:attribute></xsl:element><p/></xsl:element></xsl:if></xsl:template>

通过onclick属性,我们为图片定义了onclick事件,以处理缩略图与正常大小图片之间的转换:

<xsl:attribute name='onclick'><xsl:value-of select='concat('swapImg(this, &quot;',$small,'&quot;,&quot;',$normal,'&quot;)')'/></xsl:attribute>

这个XSL文件我们保存为PMS_Help.xsl,下载此XSL文件。

将各部分粘合起来最后写一个HTML文件,定义两个框架,将两个XML文件粘合起来,点此查看此用户手册的效果。

回顾一下,此用户手册包含:

一个DTD文件,用于定义XML文档的结构。此文件在XML文件中被引用,但并不是强制性的,换句话说,你可以根本就不生成这个文件,但实际上你必须遵循一定的规则来编写XML文件以及实现你的XSL样式文件。 两个XML文件,包含了手册的实际内容。这两个文件除了链接的XSL样式文件不同外,其他完全相同。 两个XSL样式文件,在以上的两个XML文件中被引用。一个用于格式化左侧导航栏显示,一个用于格式化右侧具体内容显示。 一个超文本文件,用于粘合两个XML文件。

参考资源

下载此用户手册全部代码的打包文件 下载电子教程:XML初学进阶,来自XML中国论坛-http://www.xml.net.cn (域名已失效!)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有