我习惯了以下变量:
<xsl:variable name="openTag">
<![CDATA[
<div>
]]>
</xsl:variable>
<xsl:variable name="closeTag">
<![CDATA[
</div>
]]>
</xsl:variable>
以下列方式实施:
<div class="root">
<xsl:variable name="ownerid" select="Prp[@name='owner id']/@value" />
<xsl:variable name="nextownerid" select="preceding-sibling::Node[1]/Prp[@name='owner id']/@value"/>
<xsl:choose>
<xsl:when test="$ownerid = '-1'">
<xsl:copy-of select="$LogText"/>
</xsl:when>
<xsl:when test="$ownerid < $nextownerid">
<xsl:copy-of select="$openTag"/>
<xsl:copy-of select="$LogText"/>
</xsl:when>
<xsl:when test="$ownerid > $nextownerid">
<xsl:copy-of select="$openTag"/>
<xsl:copy-of select="$LogText"/>
<xsl:copy-of select="$closeTag"/>
<xsl:copy-of select="$closeTag"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$openTag"/>
<xsl:copy-of select="$LogText"/>
<xsl:copy-of select="$closeTag"/>
</xsl:otherwise>
</xsl:choose>
</div>
问题是div
标记作为文本输出而不能识别为HTML标记。有没有解决方法?
答案 0 :(得分:8)
XSLT是一种树转换语言:它将一个节点树作为输入,并生成一个节点树作为输出。它不读取包含开始和结束标记的词法XML,也不输出包含开始和结束标记的词法XML。输入树(通常)由词汇XML由称为XML解析器的单独处理器构造,输出树(通常)由称为XML序列化器的单独处理器转换为词汇XML。
disable-output-escaping是一种破解,XSLT处理器将带外信息发送给串行器。因此,它会在变换器和串行器之间产生不希望的紧密耦合,这会阻止您的XSLT代码在没有序列化器的管道中工作(例如,Firefox)。
看起来您的逻辑正在尝试解决“群组相邻”问题:将具有相同所有者ID的所有相邻元素分组。在不使用序列化黑客的情况下,有更好的方法可以解决XSLT中的问题。在XSLT 2.0中:
<xsl:for-each-group select="*" group-adjacent="owner-id">
<div>
<xsl:copy-of select="current-group()"/>
</div>
</xsl:for-each-group>
如果你受限于XSLT 1.0,这有点棘手,但并不难:寻找“兄弟递归”的例子。
答案 1 :(得分:1)
这是您实际寻找的XSLT 1.0转换。
它创建了一个结构良好的HTML的嵌套树,没有任何disable-output-escaping
技巧。
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="html" indent="yes" />
<!-- index Nodes by their owner ID -->
<xsl:key name="kNodeByOwnerId" match="Node" use="Prp[@name = 'owner id']/@value" />
<xsl:template match="/">
<html>
<head>
<style type="text/css"><![CDATA[
body {
font-size: 14px;
font-family: verdana;
background-color: white;
}
div.root {
padding: 0px 0px 2px 0px;
}
div.root div {
padding: 0px 0px 0px 0px;
margin-left: 3em;
}
div.remark {
margin-left: 2em;
}
img.icon {
padding-right: 5px;
}
span.log {
font-weight: bold;
}
span.log.fail {
color: red;
}
span.log.pass {
color: green;
}
span.log.info {
color: blue;
}
]]></style>
</head>
<body>
<!-- output "top level" nodes, i.e. those with owner ID -1 -->
<xsl:apply-templates select="key('kNodeByOwnerId', '-1')">
<xsl:sort select="substring-after(@name, 'message ')" data-type="number" />
<xsl:with-param name="containerClass" select="'root'" />
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="Node">
<xsl:param name="containerClass" select="''" />
<xsl:variable name="messageClass">
<xsl:apply-templates select="Prp[@name = 'type']" />
</xsl:variable>
<div class="{$containerClass}">
<img class="icon" src="./{$messageClass}.png" />
<span class="log {$messageClass}">
<xsl:value-of select="Prp[@name='message']/@value"/>
</span>
<xsl:apply-templates select="Prp[@name = 'remarks']" />
<!-- output nodes that belong to this node (recursive!) -->
<xsl:apply-templates select="key('kNodeByOwnerId', Prp[@name = 'id']/@value)">
<xsl:sort select="substring-after(@name, 'message ')" data-type="number" />
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="Prp[@name = 'remarks']">
<xsl:if test="normalize-space(@value) != ''">
<div class="remark">
<img class="icon" src="./info.png" />
<xsl:value-of select="@value"/>
</div>
</xsl:if>
</xsl:template>
<xsl:template match="Prp[@name = 'type']">
<xsl:choose>
<xsl:when test="@value = '0'">pass</xsl:when>
<xsl:when test="@value = '4'">info</xsl:when>
<xsl:otherwise>fail</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
请注意使用<xsl:key>
。可以找到密钥如何工作的详尽说明in an earlier answer of mine。
代码很简单,所以你应该很难理解它的作用。如果有什么不清楚的话,请不要犹豫。
我强烈建议将CSS代码放入单独的文件中。