我有一个XML文档,该文档是这样构成的:
<?xml version="1.0" encoding="UTF-8"?>
<text>
...
<cb n="1" />
...
<cb n="2" />
...
<cb n="" />
...
</text>
XML文档中的每个列开关的部分以<cb n="1" />
标签开头,并以<cb n="" />
标签结尾,其中一个或多个<cb n="2" />
,<cb n="3" />
等。它们之间的标签。<cb>
标签都是<text>
的直接子女。我想生成HTML,其中每个<cb n="1" />...<cb n="" />
块转换为<div>...</div>
,然后将每个<cb n="x" />...<cb n="x+1" />
块转换为<div class="column">...</div>
。例如,上面的XML的输出为
<html>
<body>
...
<div>
<div class="column">
...
</div>
<div class="column">
...
</div>
</div>
...
</body>
</html>
我的XSLT样式表是:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="text">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<!-- this is the part that fails -->
<xsl:template match="cb[@n='1']">
<div>
<div class="column">
</xsl:template>
<xsl:template match="cb[@n='']">
</div>
</div>
</xsl:template>
<xsl:template match="cb">
</div>
<div class="column">
</xsl:template>
</xsl:stylesheet>
但这是不起作用的,因为样式表本身不是有效的XML。XSLT 1.0?
要理解的第一件事是,生成HTML或XML输出时,XSL会全部生成输出元素;隔离的启动或结束标签不能发射到输出中(部分是因为它们在输入中不接受(。因此,输出文档中的每个节点都来自输入文档中特定节点的转换,因此作为转换作者的作业的一部分是选择要转换为所需的输出节点的输入节点。
。特别是,包含输出列组的<div>
的源节点的唯一好候选者是<text>
元素,也是<cb>
元素之一。如果选择后者,那么您需要选择具有区别特征的一个,例如第一个或最后一个或具有特定属性值。
此外,任何节点的转换提供了包含的<div>
必须也必须 对列组内容负责,因为模板无法将内容添加到由不同模板产生的输出节点,甚至相同模板的不同实例化。如果让<text>
元素的模板也转换为这些模板,则必须进行额外的工作以避免不需要的输出。
这是您可以将它们放在一起的一种方法:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<!-- identity transform for nodes not otherwise matched with a template -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match='/text'>
<html>
<body>
<!-- transform child nodes up to and including the first <cb>, if any -->
<xsl:apply-templates select="node()[not(preceding::cb)]" />
</body>
</html>
</xsl:template>
<!-- template for most <cb> elements: -->
<xsl:template match="cb">
<xsl:variable name="column" select="preceding-sibling::cb[1]/@n" />
<div class="column">
<!-- contents come from transforming nodes between the previous <cb>
and this one -->
<xsl:apply-templates
select="preceding-sibling::node()[preceding-sibling::cb[@n = $column]]" />
</div>
</xsl:template>
<!-- template for <cb> elements that are their parent's first child;
produces the column-group div, its contents, and the nodes following -->
<xsl:template match="cb[1]">
<div>
<xsl:apply-templates select="following-sibling::cb" />
</div>
<xsl:apply-templates
select="../cb[position() = last()]/following-sibling::node()" />
</xsl:template>
</xsl:stylesheet>
不利用(因此不依赖(输入n
属性的特定值;它仅依靠它们是独特的。此外,由于它通过转换第一个<cb>
来创建包含<div>
的列组,因此它将忽略没有任何<cb>
元素。总体而言,请注意使用preceding-sibling
和following-sibling
轴选择其他节点之间的节点。
您的问题仍然不清楚。如果我猜正确的话,您想进行类似的输入:
XML
<text>
<cb n="1">a</cb>
<cb n="2">b</cb>
<cb n="2">c</cb>
<cb n=""></cb>
<cb n="4">d</cb>
<cb n="5">e</cb>
<cb n=""></cb>
<cb n="6">f</cb>
<cb n="7">g</cb>
<cb n="8">h</cb>
<cb n="9">i</cb>
<cb n="">j</cb>
</text>
并为每组以<cb n=""/>
结尾的连续cb
元素创建div
包装器。这在XSLT 2.0中很容易做到,但是在XSLT 1.0中有些棘手:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:key name="cb-by-end" match="cb[not(@n='')]" use="generate-id(following-sibling::cb[@n=''][1])" />
<xsl:template match="/text">
<html>
<body>
<xsl:apply-templates select="cb[@n='']" mode="group"/>
</body>
</html>
</xsl:template>
<xsl:template match="cb" mode="group">
<div>
<xsl:apply-templates select="key('cb-by-end', generate-id())"/>
</div>
</xsl:template>
<xsl:template match="cb">
<div class="column">
<xsl:apply-templates/>
</div>
</xsl:template>
</xsl:stylesheet>
结果
<html>
<body>
<div>
<div class="column">a</div>
<div class="column">b</div>
<div class="column">c</div>
</div>
<div>
<div class="column">d</div>
<div class="column">e</div>
</div>
<div>
<div class="column">f</div>
<div class="column">g</div>
<div class="column">h</div>
<div class="column">i</div>
</div>
</body>
</html>