来源:远方网络 | 2006-2-26 | (有2359人读过)
14.8 缺省的模板规则
在XSL样式单中,十分小心地映射XML文档的层次,是很困难的。如果文档不按照固定的、可预料的顺序(如周期表)排列,而是正像许多Web网页那样随意地将元素放在一起,这种情况就很难映射XML文档的层次。在这些情况下,应有通用的规则,来查找元素并将模板应用于此元素,而不必考虑此元素究竟出现在源文档的何处。
为了使此过程更容易,XSL定义两个缺省的模板规则,在所有的样式单中都隐性地包括这两个规则。第一个缺省规则将模板应用于所有元素的子元素,以递归的形式,降序排列元素的结构树。这种方式可确保应用于元素的所有模板规则都能够被说明。第二个缺省规则应用于下一个节点,将这些节点的值复制到输出流中。这两个规则共同使用,表示即使是没有任何元素的空XSL样式单,仍将产生把输入的XML文档的原始字符数据作为输出内容的结果。
14.8.1 元素的缺省规则
第一个缺省规则应用于任何类型的元素节点或根节点:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
*|/ 是“任何元素的节点或根节点”的缩写形式。本规则的目的,就是要确保所有的元素即使没有受到隐性规则的影响,也都按递归的方式处理。也就是说,除非其他的规则覆盖了本规则(特别是对根元素就是如此),否则所有的元素节点都要处理。
但是,一旦存在任何父元素的隐性规则,那么对于子元素,除非父元素的模板规则有xsl:apply-templates子元素,否则本规则将无效。例如,按照如下方式,通过匹配根元素,并且既不应用模板,也不使用xsl:for-each来处理子元素,就可以阻止所有的处理过程:
<xsl:template match="/">
</xsl:template>
14.8.2 文本节点的缺省规则
细心的读者或许已经注意到,有几个例子似乎已输出了有些元素的内容,但实际上没有获得输出的元素值!这些内容是由XSL用于以元素内容出现的文本节点的缺省规则提供的。此规则如下:
<xsl:template match="text()">
<xsl:value-of select="."/>
</xsl:template>
这一规则匹配所有的文本节点(match="text()"),并输出文本节点(<xsl:value-of select="."/>)的值。换言之,此规则将文本从输入复制到输出。
本规则确保最少输出一个元素的文本,即使没有任何规则明确地与此文本匹配。对于特定的元素(从中可或多或少获得元素的文本内容),另一个规则可以覆盖此规则。
14.8.3 两个缺省规则的含义
这两个缺省的规则结合在一起,意味着把只有xsl:stylesheet元素而不包括任何子元素的空样式单(如**14-14)应用于XML文档时,将把输入元素中所有的#PCDATA复制到输出。但是,这种方法不产生任何标记。可是这些规则的优先级很低。因此,任何其他匹配都优先于这两个规则。
**14-14:空的XML样式单
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
</xsl:stylesheet>
在Internet Explorer 5.0中,对XSL产生混淆的最常见的根源之一是,没有提供任何缺省规则。要确保明确地匹配准备输出其内容(包括其后代)的任何节点。
14.9 决定输出要包含的内容
在未读取输入文档时,推迟决定输出何种标记往往是必要的。例如,或许想将FILENAME元素的内容改为A元素的HREF特性,或者根据特性的值,将输入文档中的某个元素类型用输出文档中的几个不同元素类型代替。这可以通过使用xsl:element、xsl:attribute、xsl:pi、xsl:comment和xsl:text来实现。在这些元素的内容中使用XSL指令,并在这些元素的特性值中使用特性值模板,就能改变它们的输出内容。
14.9.1 使用特性值模板
特性值模板将数据从输入中的元素内容复制到样式单中的特性值中。从那里,就可将其写入输出中。例如,假定根据要利用下面的基于特性的形式将周期表转换成空的ATOM元素:
<ATOM NAME="Vanadium"
ATOMIC_WEIGHT="50.9415"
ATOMIC_NUMBER="23"
OXIDATION_STATES="5, 4, 3, 2"
BOILING_POINT="3650K"
MELTING_POINT="2163K"
SYMBOL="V"
DENSITY="6.11 grams/cubic centimeter"
/>
为此,需要提取输入文档中元素的内容,并将这些内容放在输出文档的特性值中。首先,要完成下列内容:
<xsl:template match="ATOM">
<ATOM NAME="<xsl:value-of select='NAME'/>”
ATOMIC_WEIGHT="<xsl:value-of select='ATOMIC_WEIGHT'/>”
ATOMIC_NUMBER="<xsl:value-of select='ATOMIC_NUMBER'/>”
/>
</xsl:template>是畸形的XML。在特性值内部不能使用<字符。而且,要编写在大多数一般情况下都能解析此句的软件,是极其困难的。
取而代之的是,在特性值内部,以放在花括号{}中的数据来代替xsl:value-of元素。上面的正确编写方式如下:
<xsl:template match="ATOM">
<ATOM NAME="{NAME}/>”
ATOMIC_WEIGHT="{ATOMIC_WEIGHT}/>”
ATOMIC_NUMBER="{ATOMIC_NUMBER}/>”
/>
</xsl:template>
在输出文档中,{NAME}由当前节点的NAME子元素值所代替。{ATOMIC_WEIGHT}由当前节点的ATOMIC_WEIGHT子元素值所代替。{ATOMIC_NUMBER}由当前节点的ATOMIC_NUMBER子元素值所代替,等等。
特性值模板的模式比只是一个元素名要复杂。实际上,在特性值模板中,可使用前面讨论过的任何字符串表达式。例如,下面的模板规则以**14-1中使用的形式来选择DENSITY元素。
<xsl:template match="DENSITY">
<BULK_PROPERTY
NAME="DENSITY"
ATOM="{../NAME}"
VALUE="{.}"
UNITS="{@UNITS}"
/>
</xsl:template>
上面的模板规则将特性值模板转换成类似于如下所示的BULK_PROPERTY元素:
< BULK_PROPERTY NAME="DENSITY" ATOM="Helium" VALUE="
0.1785
“ UNITS="grams/cubic centimeter"/>
特性值并不局限于在一个特性值模板中使用。可以将特性值模板与文字数据或其他特性值模板组合起来使用。例如,下面的模板规则匹配ATOM元素,并且将元素名以H.html、He.html等格式设置成链接文件,来代替这些元素。此文件名来源于特性值模板{SYMBOL},而文字数据提供句号和扩展名。
<xsl:template match="ATOM">
<A HREF="{SYMBOL}.html">
<xsl:value-of select="NAME"/>
</A>
</xsl:template>
在特性值中,可以包含多个特性值模板。例如,下面的模板规则将密度单位作为VALUE特性的一部分,而不是使密度单位成为单独的特性:
<xsl:template match="DENSITY">
<BULK_PROPERTY
NAME="DENSITY"
ATOM="{../NAME}"
VALUE="{.} {@UNITS}"
/>
</xsl:template>
可在一个XSL样式单中将特性值模板用于许多特性的值中。这在xsl:element、xs1:attribute和xsl:pi元素中特别重要,因为在这些元素中,特性值模板允许设计者决定在读取输入文档之前,在输出文档中准确地显示何种元素、特性或处理指令。不能将特性值模板作为select或match特性的值、xmlns特性、提供另一个XSL指令元素名的特性或顶层元素(为xsl:stylesheet直系子元素)特性来使用。
第18章"命名域"将讨论xmlns特性。
14.9.2 使用xsl:element将元素插入到输出文档中
通常,只使用文字元素本身就可以将元素插入到输出文档中。例如,要插入P元素,只需要在样式单的适当位置键入<P>和</P>。但是,偶尔也需要使用输入文档的详细内容,来确定将哪个元素放在输出文档中。例如,当将使用特性来提供信息的源符号集变换成使用元素来提供相同信息的输出符号集时,就是这种情况。
xsl:element元素将元素插入到输出文档中。元素名由xsl:element元素的name特性中的特性值模板给出。元素的内容来自于xsl:element元素的内容,此元素可能包括要插入这些项的xsl:attribute、xsl:pi和xsl:comment指令(下面讨论所有的指令)。
例如,假设根据STATE特性的值,要用GAS、LIQUID和SOLID元素来代替ATOM元素。使用xsl:element将STATE特性值转换为某个元素名,从而只需要一条规则就可以做到这一点。具体作法如下所示:
<xsl:template match="ATOM">
<xsl:element name="{@STATE}">
<NAME><xsl:value-of select="NAME"/></NAME>
<!- rules for other children ->
</xsl:element>
</xsl:template>
使用更为复杂的特性值模板,就可以实现所需的大多数运算。
14.9.3 使用xsl:attribute将特性插入到输出文档中
只使用文字特性,就可以将特性包括在输出文档中。例如,要插入带有ALIGN特性(其值为CENTER)的DIV元素,只需在样式单的适当位置处键入<DIV ALIGN="CENTER">和</DIV>即可。但是,为了确定特性值,有时甚至是为了确定特性名,常常不得不依赖于从输入文档中读取的数据。
例如,假设要获得一样式单,可选择原子名,并把这些原子名格式化为与H.html、He.html、Li.html等等文件的链接:
<LI><A HREF="H.html">Hydrogen</A></LI>
<LI><A HREF="He.html">Helium</A></LI>
<LI><A HREF="Li.html">Lithium</A></LI>
在输入文档中,每个不同的元素都有一个不同的HREF特性值。xsl:attribute元素计算特性名和值,并将它插入到输出文档中。每个xsl:attribute元素要么是xs1:element元素的子元素,要么是文字元素。在输出中,xsl:attribute计算出来的特性关联到与其父元素计算出来的元素上。特性名是由xsl:attribute元素的name特性指定的。特性值是由xsl:attribute元素的内容给出的。例如,下面的模板规则将产生上面显示的输出结果:
<xsl:template match="ATOM">
<LI><A>
<xsl:attribute name="HREF">
<xsl:value-of select="SYMBOL"/>.html
</xsl:attribute>
<xsl:value-of select="NAME"/>
</A></LI>
</xsl:template>
所有的xsl:attribute元素都必须放在其父元素的任何其他内容之前。在已经开始写出元素内容之后,就不能将特性加到元素中。例如,下面的模板是非法的:
<xsl:template match="ATOM">
<LI><A>
<xsl:value-of select="NAME"/>
<xsl:attribute name="HREF">
<xsl:value-of select="SYMBOL"/>.html
</xsl:attribute>
</A></LI>
</xsl:template>
14.9.4 定义特性集合
经常需要将同一组特性应用于许多不同的元素(既可是同类的,也可以是不同类的)。例如,将样式特性应用于HTML表中的每个单元格。要使这一操作更加简单,可使用xsl:attribute-set,在样式单的顶层定义一个或多个特性作为特性集合的成员,然后使用xsl:use将此特性集合包括在元素中。
例如,下面的xsl:attribute-set元素定义一个名为cellstyle的元素,其font-family特性为New York、Times New Roman、Times和serif,其font-size特性为12pt。
<xsl:attribute-set name="cellstyle">
<xsl:attribute name="font-family">
New York, Times New Roman, Times, serif
</xsl:attribute>
<xsl:attribute name="font-size">12pt</xsl:attribute>
</xsl:attribute-set>
然后,用下面的模板规则将这些特性应用于输出文档的td元素。与xsl:attribute一样,插入特性集合的xsl:use元素也必须放在作为td子元素而加入的任何内容之前。
<xsl:template match="ATOM">
<tr>
<td>
<xsl:use attribute-set="cellstyle"/>
<xsl:value-of select="NAME"/>
</td>
<td>
<xsl:use attribute-set=眂ellstyle"/>
<xsl:value-of select="ATOMIC_NUMBER"/>
</td>
</tr>
</xsl:template>
如果某个元素使用一个以上的特性集合,那么,就将所有集合的所有特性应用于该元素。如果一个以上的特性集合使用不同的值定义相同的特性,那么就使用较为重要集合的特性。重要性相同的多个特性集合定义相同的特性,那么此样式单就会出现错误。
14.9.5 使用xsl:pi生成处理指令
xsl:pi元素将指令放在输出文档中。处理指令的目标由所需的name特性指定。xsl:pi元素的内容成为处理指令的内容。例如,下面的规则将PROGRAM元素用gcc处理指令代替:
<xsl:template select="PROGRAM">
<xsl:pi name="gcc"> -04</xsl:pi>
</xsl:template>
输入文档中的PROGRAM元素由输出文档中的下面的处理指令所代替:
<?gcc -04?>
若这些指令的结果为纯文本,那么xsl:pi元素的内容可包括xsl:value-of元素和xsl:apply-templates元素。例如,
<xsl:template select="PROGRAM">
<xsl:pi name="gcc">-04 <xsl:value-of select="NAME"/></xsl:pi>
</xsl:template>
xsl:pi的最常用的用途之一,就是当从XML生成XML时,用来插入XML声明(尽管XML声明在技术上并不是处理指令)。例如:
<xsl:pi name="xml">version="1.0" standalone="yes"</xsl:pi>
xsl:pi元素不能包括xsl:element和在结果中产生元素和特性的其他指令。此外,它还不能包括在输出文档中插入?>的任何指令和文字文本,因为这会使处理指令提前结束。
14.9.6 使用xsl:comment生成注释
xsl:comment元素在输出文档中插入注释。它没有特性。其内容为注释文本。例如,
<xsl:template select="ATOM">
<xsl:comment>There was an atom here once.</xsl:comment>
</xsl:template>
此规则使用下面的输出代替ATOM节点:
<!-There was an atom here once.->
如果xsl:value-of元素和xsl:apply-templates元素指令的结果是纯文本的话,那么xsl:comment元素的内容可包括这些元素。它不能包括xsl:element以及在结果中产生元素和特性的其他指令。此外,xsl:comment还不能包括在注释中插入双连字号的任何指令或文字文本。这样在输出文档中会使注释很难看,这种情况是不允许的。
14.9.7 使用xsl:text生成文本
xsl:text元素将其内容作为文字文本插入到输出文档中。例如,下面的规则将每个ATOM元素用字符串"There was an atom here once"代替。
<xsl:template select="ATOM">
<xsl:text>There was an atom here once.</xsl:text>
</xsl:template>
xsl:text元素用得不多,这是因为在多数情况下,键入文本更容易。但是,xsl:text的确有一个优点。它可以准确地保留空白。当处理诗句、计算机源代码或空白显示具有重要意义的其他信息时,使用xsl:text是很有用的。
14.10 使用xsl:copy复制当前节点
xsl:copy元素将源代码复制到输出文档中。子元素、特性和其他内容不会自动复制。但是,xsl:copy元素的内容也是选择要复制这些内容的xsl:template元素。当将文档从某个标记符号集转换成相同的或相近的相关标记符号集时,这种方法通常是有用的。例如,下面的模板规则删除原子的特性和子元素,并用其内容值来代替:
<xsl:template match="ATOM">
< xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
xsl:copy使模板具有的用途之一就是恒等转换;也就是说,可将一文档转换成本身。这种转换与下面类似:
<xsl:templdte match="*|@*|comment()|pi()|text()">
< xsl:copy>
<xsl:apply-templates select="*|@*|comment()|pi()|text()"/>
</xsl:copy>
</xsl:template>
可对恒等转换进行稍微调节,以产生相似的文档。例如,**14-15是一样式单,它可去掉文档中的注释而文档的其他部分不受影响。在恒等转换中,去掉comment()节点的match和select特性值,而保留此节点的其他部分就可以产生这种结果。
**14-15:从文档中删除注释的XSL样式单
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
<xsl:template match="* | @* | pi() | text()">
< xsl:copy>
<xsl:apply-templates select="* | @* | pi() | text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
xsl:copy只复制源节点。使用xsl:copy-of,可以复制其他节点,可能不止一个。xsl:copy-of的select特性选择要复制的节点。例如,**14-16是一样式单,它使用xsl:copy-of,只复制有MELTING_POINT子元素的ATOM元素,从而将没有熔点的元素从周期表中去掉。
**14-16:只复制有MELTING_POINT子元素的ATOM元素的样式单
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
<xsl:template match="/PERIODIC_TABLE">
<PERIODIC_TABLE>
<xsl:apply-templates select="ATOM"/>
</PERIODIC_TABLE>
</xsl:template>
<xsl:template match="ATOM">
<xsl:apply-templates
select="MELTING_POINT"/>
</xsl:template>
<xsl:template match="MELTING_POINT">
<xsl:copy-of select="..">
<xsl:apply-templates select="*|@*|pi()|text()"/>
</xsl:copy-of>
</xsl:template>
<xsl:template match="* | @* | pi() | text()">
<xsl:copy>
<xsl:apply-templates select="* | @* | pi() | text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这是一个从源符号集到同一个符号集的XSL转换的例子。不像本章中的大多数例子那样,此例不转换成结构整洁的HTML。
14.11 使用xsl:number为节点计数
xsl:number在输出文档中插入格式化整数。由expr特性计算出来的数值,通过四舍五入成最接近的整数,然后根据format特性值,对此整数进行格式化,从而获得整数值。为这两个特性提供了恰当的缺省值。例如,考查**14-17中的ATOM元素的样式单。
**14-17:为原子计数的XSL样式单
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/XSL/Transform/1.0">
<xsl:template match="PERIODIC_TABLE">
<html>
<head><title>The Elements</title></head>
<body>
<table>
<xsl:apply-templates select="ATOM"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="ATOM">
<tr>
<td><xsl:number expr="position()"/></td>
<td><xsl:value-of select="NAME"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
当此样式单应用于**14-1时,输出类似如下显示:
<html><head><title>The
Elements</title></head><body><table><tr><td>l</td><td>Hydrogen<
/td></tr>
<tr><td>2</td><td>Helium</td></tr>
</table></body></html>
由于氢是其父元素的第一个ATOM元素,所以其号码为1。由于氦是其父元素的第二个ATOM元素,所以其号码为2。(这些号码对应于氢和氦的原子序数,这种对应关系是**14-1的副产品,而**14-1正是以原子序数的顺序进行排列的。)
14.11.1 缺省数值
如果使用expr特性来计算编号,那么这就是所需要的值。但是,如果省略expr特性,那么源树形结构中的当前节点位置就作为编号来使用。但是,可使用下面三个特性来调整此缺省值:
* level
* count
* from
这三个特性是从以前的不支持较为复杂的表达式XSL草案中延续下来的。如果它们完全使你混淆,那么我建议不要去考虑它们,使用expr来代替。
14.11.1.1 level特性
按缺省行为,当不存在expr特性时,xsl:number可对源节点的同属节点加以计数。例如,如果对ATOMIC_NUMBER元素而不是ATOM元素加以编号,那么由于一个ATOM元素绝不会有多个ATOMIC_NUMBER子元素,所以任何一个编号都不会大于1。尽管文档包含多个ATOMIC_NUMBER元素,但它们不是同属的。
将xsl:number的level特性设置成any,可对与文档中当前节点同类的所有元素加以计数。此情况不仅包括与当前规则相匹配的元素,还包括类型与要求相一致的所有元素。例如,即使只选择气体的原子序数,固体和液体也仍然计数在内(即便固体和液体没有输出也是如此)。看看下面的这些规则:
<xsl:template match="ATOM">
<xsl:apply-templates select="NAME"/>
</xsl:template>
<xsl:template match="NAME">
<td><xsl:number level="any"/></td>
<td><xsl:value-of select="."/></td>
</xsl:template>
由于level设置成any,上面的规则对每个新的NAME元素产生的输出不是从1开始,其输出结果如下:
<td>l</td><td>Hydrogen</td>
<td>2</td><td>Helium</td>
如果删除level特性或设置成缺省的single值,那么输出结果如下:
<td>l</td><td>Hydrogen</td>
<td>l</td><td>Helium</td>
另一个不大有用的方法将xsl:number的level特性设置成multi,以便对当前节点的同属及其祖先(但不是当前节点同属的子节点)加以计数。
14.11.1.2 count特性
按缺省行为,当没有expr特性时,只对与当前节点元素同类的元素加以计数。但可以将xsl-number的count特性设置成选择表达式,从而指定对什么元素加以计数。例如,下面的规则对ATOM的所有子元素进行编号:
<xsl:template match="ATOM/*">
<td><xsl:number count="*"/></td>
<td><xsl:value-of select="."/></td>
</xsl:template>
应用此规则获得的输出结果如下:
<td>l</td><td>Helium</td>
<td>2</td><td>He</td>
<td>3</td><td>2</td>
<td>4</td><td>4.0026</td>
<td>5</td><td>l</td>
<td>6</td><td>4.216</td>
<td>7</td><td>0.95</td>
<td>8</td><td>
0.1785
</td>
14.11.1.3 from特性
from特性包含select表达式,它指定在输入树形结构中以哪个元素开始计数。但仍可以从1而不从2或10或某个其他数字开始计算。
|