使用 XSLT 作为 HTML 的样式表
简介
当听到样式表这个词时,您可能会想到 CSS 样式表。XSLT 样式表通常用于 XML 转换,比如在 Web 服务之间映射数据。因为 XSLT 非常适合此用途,所以创建了顶层元素 <stylesheet> 的 <xsl:transform> 别名,虽然这很少使用。这种 XSLT 转换的输入结构与输出结构有很大的不同。最重要的是,命名空间的不同。
XSLT 样式表的输入结构与输出结构相似,但却更简单些。其中已经扩充了一些标记,但大部分标记只是原样复制到输出。输入和输出的命名空间是相同的 (HTML)。输入文档也可以包含样式表指令(比如创建脚注),这些指令属于另一个命名空间,不会传递到输出中。
常用缩略语
- CSS:级联样式表
- XHTML:可扩展超文本标记语言
- XPath:XML 路径语言
- XSLT:可扩展样式表语言转换
在本文中,我们将学习如何使用 XSLT 样式表扩充 XHTML 文档。文中的示例展示了如何使用指令,如何引用其他源文档的部分,以及如何使用链接在主文档中导航。此外,我们还探索了页面的解释和编译之间的区别。
CSS 样式表的局限性
XSLT 样式表不会阻止您使用其他技术,比如 JavaScript 或 CSS。CSS 适用于字体、加粗、颜色、间距等。它不适合将来自不同位置的信息组合在一起,比如脚注、模块或生成一个目录。这正是 XSLT 的用武之地,它补充而不是替代了 CSS。
XSLT 用途示例
实际上,您可以将 XSLT 代码集中在一个文件中。为了简单起见,本文中的每个示例均位于一个独立的 XSLT 文件中,除了一些必要的代码。清单 1 给出了必需的代码。
清单 1. 必需的代码(位于 samples/common.xml 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
< s:stylesheet exclude-result-prefixes = "a h" version = "1.0" > < s:template match = "h:head" > < s:copy > < s:apply-templates select = "@*|node()" /> < meta http-equiv = "content-type" content = "text/html;charset=UTF-8" /> < link href = "common.css" rel = "stylesheet" type = "text/css" /> </ s:copy > </ s:template > < s:template match = "*" > < s:copy > < s:copy-of select = "@*" /> < s:apply-templates /> </ s:copy > </ s:template > </ s:stylesheet > |
XHTML 的命名空间定义了两次:默认定义和 h:。默认命名空间用于编写输出 XHTML 标记,其中应该避免使用命名空间前缀。h: 用在 XPath 表达式中。
本文使用 XSLT 1.0 版本。目前,大部分浏览器都无法解释 XSLT 2.0。但是,如果 XSLT 运行在服务器上,那么它可能是一个实用的选择。XSLT 2.0 还提供了:
- XPATH 2.0(if…then…else 和许多内置的函数)
- 内置和用户编写的 XPATH 函数
- 分组
在 清单 1 中:
- s:template match="head" 扩充了源文档的 head 一节,添加了一个 CSS 样式表的链接。即使 UTF-8 是在 XML 中的默认编码,一些浏览器也需要内容类型才能呈现它。
- s:template match="*" 是默认的详细副本。原则上,所有内容都会复制到目标文档中。如果遗漏了此模板,只会将标记的文本内容复制到目标文档。不会复制处理指令节点。
本文中的所有其他示例都是导入 common.xsl 的独立文件。
扩充
通过扩充,添加了一个未在源文档中显式请求的特性。一个示例是 清单 1 中的 CSS 样式表的链接。尝试另一个示例,向每个内部链接添加一个小箭头 (^ v),指明目标在它之前还是之后。清单 2 给出了该样式表。
清单 2. 样式表(在 samples/linkUpDown.xsl 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
< s:stylesheet version = "1.0" > < s:import href = "common.xsl" /> < s:template match = "h:a[starts-with(@href,'#')]" > < s:copy > < s:copy-of select = "@*" /> < s:variable name = "name" select = "substring-after(@href,'#')" /> < s:choose > < s:when test = "preceding::h:a[@name=$name]" > < s:text >^</ s:text > </ s:when > < s:when test = "following::h:a[@name=$name]" > < s:text >v</ s:text > </ s:when > </ s:choose > < s:apply-templates /> </ s:copy > </ s:template > </ s:stylesheet > |
首先,导入清单 2 中的通用样式表。模板与内部链接(以 '#' 开头)相匹配。如果链接指向的锚点位于链接之前,那么使用一个向上箭头扩充该链接(如果情况相反,则使用向下箭头)。
s:copy-of 和 s:apply-templates 可确保不会沿途丢下任何内容。
清单 3 给出了一个示例文档(其中包含内部链接),它经过了清单 2 中的样式表进行扩充。
清单 3. 源文档(在 samples/linkUpDown.xml 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<? xml-stylesheet href = "linkUpDown.xsl" type = "text/xsl" ?> < head /> < body > < a name = "a" /> < p >This link goes < a href = "vb" >downward.</ a ></ p > < br /> < p >Reduce the size of the window to verify the link really works.</ p > < br /> < a name = "b" /> < p >This link goes < a href = "^a" >upward.</ a > </ p > </ body > </ html > |
目标文档看起来相同,除了清单 4 中的条目。
清单 4. 目标文档(在 samples/linkUpDown.html 中)
1
2
|
… < a href = "#b" >v downwards.</ a > … … < a href = "#a" >^ upwards.</ a > … |
指令
您可以在源文档中添加一些指令,告诉样式表执行何种操作。它们属于另一个命名空间(在本例中为前缀 a:),不会被复制到目标文档。
在清单 5 中,源文档中任何地方的指令标记 a:ref 都会创建一个脚注。
清单 5. 样式表(在 samples/footnote.xsl 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
< s:stylesheet version = "1.0" > < s:import href = "common.xsl" /> < s:template match = "h:body" > < s:copy > < s:apply-templates select = "@*|node()" / <!-- put the footnotes at the end if there is no a:references directive --> < s:if test = "not(descendant::a:references)" > < s:call-template name = "references" /> </ s:if > </ s:copy > </ s:template > <!-- Create a footnote --> < s:template match = "a:ref" > < s:variable name = "number" select = "count(preceding::a:ref) + 1" /> < a name = "ref-{$number}" ></ a > < a class = "footnote" href = "#reference-{$number}" > < s:value-of select = "concat('v ',$number)" /> </ a > </ s:template > <!-- if a:reference is missing, assume it at the end of the body --> < s:template match = "a:references" name = "references" > < hr /> < s:for-each select = "//a:ref" > < s:variable name = "number" select = "count(preceding::a:ref) + 1" /> < p > < a name = "reference-{$number}" ></ a > < a class = "footnote" href = "#ref-{$number}" > < s:value-of select = "concat(' ^',$number)" /> </ a > < s:apply-templates /> </ p > </ s:for-each > </ s:template > </ s:stylesheet > |
使用源文档中的 a:references 指令,名为 references 的模板会在模板与该指令匹配的地方分配脚注。如果缺少这样一个指令,第一个与 body 匹配的模板会在 body 的末尾分配脚注,方法是调用名为 references 的相同模板。在两种情况下,都会列出脚注的内容,并生成一个由向上箭头表示的向上链接。
第二个模板(匹配 a:ref)使用向下箭头创建脚注的链接。脚注具有编号。这里忽略了它的内容。
class="footnote" 属性在 XSLT 转换之后由一个 CSS 样式表解析,该样式表链接在 XSLT 样式表 common.xsl 中。
清单 6 中的源文档使用 a:ref 指令创建脚注。
清单 6. 源文档(在 samples/footnote.xml 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<? xml-stylesheet href = "footnote.xsl" type = "text/xsl" ?> < html > < head /> < body > < p > This example looks a little scientific < a:ref > From Latin < em >scientia</ em > </ a:ref > and academic < a:ref >From Greek akademia</ a:ref >. </ p > < p > Do you know why? < a:ref > It uses < em >footnotes</ em >. </ a:ref > </ p > < p >Reduce size of window to verify links are generated.</ p > </ body > </ html > |
目标文档将脚注列表包含在底部,如清单 7 所示。
清单 7. 目标文档(在 samples/footnote.html 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
< html < head >< link type = "text/css" rel = "stylesheet" href = "common.css" /></ head > < body > < p >This example looks a little scientific < a name = "ref-1" />< a href = "#reference-1" class = "footnote" >v 1</ a > and academic. < a name = "ref-2" />< a href = "#reference-2" class = "footnote" >v 2lt;/a> </ p > < p >Do you know why? < a name = "ref-3" />< a href = "#reference-3" class = "footnote" >v 3</ a > </ p > < p >Reduce size of window to verify links are generated.</ p > br/>< br /> < hr /> < p >< a name = "reference-1" />< a href = "#ref-1" class = "footnote" > ^1</ a > From Latin < em >scientia</ em > </ p > < p >< a name = "reference-2" /> < a href = "#ref-2" class = "footnote" > ^2</ a >From Greek akademia</ p > < p >< a name = "reference-3" />< a href = "#ref-3" class = "footnote" > ^3</ a > It uses < em >footnotes</ em >. </ p > </ body > </ html > |
突破源文档的边界
也可引用其他源文档的其中一些部分。a:include 指令包含一个可能属于另一个源文档的元素并转换它,如清单 8 所示。
清单 8. 样式表(在 samples/include.xsl 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
< s:stylesheet version = "1.0" > < s:import href = "common.xsl" /> < s:template match = "a:include" > < s:choose > < s:when test = "0!=string-length(@src)" > < s:apply-templates select = "document(@src)//*[@id=current()/@refid]" /> </ s:when > < s:when test = "not(@src) and //a:default[1]/@src" > < s:apply-templates select = "document(//a:default[1]/@src)//*[@id=current()/@refid]" /> </ s:when > < s:when test = "0=string-length(@src) or not(//a:default[1]/@src)" > < s:apply-templates select = "//*[@id=current()/@refid]" /> </ s:when > </ s:choose > </ s:template > </ s:stylesheet > |
源文档中的一个 a:include 指令引用源元素的 id。包含该元素的文档可在一个 src 属性中命名。如果缺少该属性,将使用 a:default 指令的 src 属性。如果在任何地方都没有 src 属性,则使用同一个源文档。因此,refid 会引用 id 来避免无限的递归。
导入的元素可能具有一种复杂的类型,并在包含 (apply-templates)之后进行转换。清单 9、清单 10 和清单 11 给出了示例。
清单 9. 源文档(在 samples/include.xml 中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<? xml-stylesheet href = "include.xsl" type = "text/xsl" ?> < html < head > < a:default src = "includedY.xml" /> </ head > < body > < p >The following text is included:</ p > < a:include refid = "x" src = "includedX.xml" /> < a:include refid = "y1" /> < p id = "i" >double</ p > < a:include refid = "y2" /> < a:include refid = "i" src = "" /> </ body > </ html > |
清单 10. 源文档的部分(在 samples/includeY.xml 中)
1
2
|
< h2 id = "y2" >I'm the < em >included</ em > h2</ h2 > < h1 id = "y1" >I'm the < em >included</ em > h1</ h1 > |
清单 11. 目标文档(在 samples/include.html 中)
1
2
3
4
5
6
7
8
9
|
< body > < p >The following text is included:</ p > < p id = "x" >I'm the < em >included</ em > paragraph.</ p > < h1 id = "y1" >I'm the < em >included</ em > h1</ h1 > < p id = "i" >double</ p > < h2 id = "y2" >I'm the < em >included</ em > h2</ h2 > < p id = "i" >double</ p > </ body > </ html > |
主文档和导航
如果您有一个包含多个页面的演示,有一个主文档包含页面标题及其链接。您可以生成完整的导航,从每个页面到任何其他页面,以及到前一个和后一个页面。这些细节不属于本文的介绍范围,但 参考资料 中提供了使用主文档的 HTML 演示的链接。可将 .xml 替换为 .html 来获得编译后的版本。让浏览器向您显示 .xml 的整洁源代码。您会对它生成的源代码量感到惊奇。
解释与编译的对比
解释意味着页面为 XML 格式(其文件扩展名为 .xml,其内容类型为文本/xml 或应用程序/xml),并且处理指令所引用的 XSLT 样式表可在浏览器中执行。
编译意味着浏览器看到的是 HTML(其文件扩展名为 .html,内容类型为文本/html),它是在请求页面之前从您的开发环境中或服务器上的 XML 转换而来的。Xalan 和 Saxon 都是著名的 XSLT 处理器。
解释是未来的发展方向。所有现代浏览器都支持 XSLT,并且它具有一些优点:
- 当测试时,您会立即获得结果。只需在您测试的每个浏览器中按下 F5,即可反映源页面、CSS 和 XSLT 样式表的更改。
- 要传递给客户端的信息量减少了。
- 客户端看到的是一个干净、整洁的网页,因为还未生成扩充内容。
但也要注意一些缺点:
- 有一些旧浏览器可能不支持 XSLT。如果向一个受控的环境(内部网)发布页面,就不会出现问题。
- 一些现代浏览器禁止 XSLT 样式表引用另一个目录中的另一个样式表。
- 将 XSLT 域其他功能相结合(比如 SVG 或 iframe)可能在一些浏览器中导致问题。
- 因为大部分浏览器都不支持 XSLT 2.0 或即将推出的 3.0,所以您无法使用新功能。没有 XPath 2.0
if () then … else
,也没有用户编写的 XPath 功能。
无论进行编译还是解释,页面的其他转换 (CSS、JavaScript) 都会在 XSLT 转换之后执行。
结束语
在本文中,您学习了如何使用 XSLT 样式表来扩充 XHTML 文档。您可以使用本文中的示例作为起点,构建您自己的 XSLT 样式表。
下载
关于作者
Jürgen M. Regel 是位于德国汉诺威的 TUI InfoTec GmbH 的 Architecture Management & Software Engineering 部门的一名高级软件工程师。他主要研究旅游行业中的企业应用程序集成 (EAI)。
使用 XSLT 作为 HTML 的样式表的更多相关文章
- 微软BI 之SSIS 系列 - XML Task 中XSLT 样式表转换错误记录
开篇介绍 此文章专门记录 XSLT 样式表转换过程中的语法问题 错误一 值与属性的倒置 修改了几次样式表,但还是一如既往的报错,报错信息如下: [XML Task] Error: An error o ...
- XSLT可扩展样式表语言转换 System.Xml.Xsl、XslCompiledTransform类
XML文件 books.xml: <?xml version="1.0" encoding="utf-8" ?> <bookstore> ...
- 【HTML/XML 8】XSL,可扩展样式表语言
导读:上篇博客说了在XML文档中实现表现形式的一种形式:CSS层叠样式表,本篇博客将接着说明其另一种实现方式XSL,并将分析XSL和CSS之间的 关系. 一.XSL简介 XSL(eXtensible ...
- CSS样式表分类
1.内联样式表 <p style="font-size:11px;">内联样式表</p> 2.内嵌样式表 写在head标签里 <style typ ...
- 深度理解CSS样式表,内有彩蛋....
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- CSS样式表
CSS样式及属性 样式标的基本概念 样式表的分类 1.内联样式表 和html联合显示,控制精确,但可重用性差,冗余多. 例:<p style="font-size:14px;" ...
- CSS样式表基础
CSS的样式表其实就是美观页面的,加一些样式. 一.样式表的三种分类: ①内联样式:写在某一个标签里面的样式. 优点:控制精确. 缺点:代码重用性差.(太多了不好写)页面代码乱.(太乱,后期不方便看) ...
- 深入理解脚本化CSS系列第四篇——脚本化样式表
× 目录 [1]CSSStyleSheet [2]CSSRule 前面的话 关于脚本化CSS,查询样式时,查询的是计算样式:设置单个样式时,设置的是行间样式:设置多个样式时,设置的是CSS类名.脚本化 ...
- CSS:CSS样式表及选择器优先级总结
我们在写网页的时候经常会遇到同一个HTML文件,使用了外部样式.内部样式以及内联样式,那么如果发生冲突时浏览器是怎么抉择的呢? 也会遇到这样的情况,在样式表中,对同一个HTML元素,我们有可能既用到了 ...
随机推荐
- C语言的知识与能力予以自评
看到一个问卷不错,拟作为第三次作业的部分内容. 你对自己的未来有什么规划?做了哪些准备?答:多学习几门生存技巧,首先先学会碰壁. 你认为什么是学习?学习有什么用?现在学习动力如何?为什么?答:学习是人 ...
- Java 将数字转为16进制,然后转为字符串类型 将空格去掉。终结版
//十进制转为十六进制 public class ArrayTest7 { public static void main(String[] args){ System.out.println(toH ...
- 【第一周】c++实现词频统计
coding.net地址:https://coding.net/u/Boxer_ ssh:git@git.coding.net:Boxer_/homework.git ---------------- ...
- PAT 甲级 1005 Spell It Right
https://pintia.cn/problem-sets/994805342720868352/problems/994805519074574336 Given a non-negative i ...
- c语言----程序记录
1.结构体写入文件,读取 #include <stdio.h> #include <string.h> #include <stdlib.h> #define ma ...
- javascript之容易出错的地方
1: 不是所有的非空对象都有toString()方法的 var obj = Object.create(null); console.log(obj.toString()); // false; ...
- 【loj2325】「清华集训 2017」小Y和恐怖的奴隶主 概率dp+倍增+矩阵乘法
题目描述 你有一个m点生命值的奴隶主,奴隶主受伤未死且当前随从数目不超过k则再召唤一个m点生命值的奴隶主. T次询问,每次询问如果如果对面下出一个n点攻击力的克苏恩,你的英雄期望会受到到多少伤害. 输 ...
- android面试(2)----组件
1.anroid:id的作用? android:id是作为控件的唯一标示符.可以使用与releativelayout中,也可以再Activity中通过findviewbyid来获得指定的控件. 2.a ...
- (转)Linux GCC常用命令
1简介 2简单编译 2.1预处理 2.2编译为汇编代码(Compilation) 2.3汇编(Assembly) 2.4连接(Linking) 3多个程序文件的编译 4检错 5库文件连接 5.1编译成 ...
- [COCI2011-2012#5] POPLOCAVANJE 后缀自动机
题面:洛谷 题解: 其实还可以用AC自动机做,但是没调出来,,,不知道发生了什么... AC自动机做法如下: 观察到如果我们对给定的每个串建AC自动机,那么直接拿大串在上面匹配,如果遇到了一个单词的终 ...