我需要使用XSLT 1.0从平面XML结构创建HTML无序列表。输入XML由一系列要转换为列表项的节点组成。但是,此系列可能会被不同类型的非列表节点打断:
<input>
<paragraph>abc</paragraph>
<paragraph>def</paragraph>
<listable>123</listable>
<listable>456</listable>
<other-block>
<other-text>Foo</other-text>
</other-block>
<listable>789</listable>
<listable>012</listable>
</input>
我的目标是:
<div class="output">
<p>abc</p>
<p>def</p>
<ul>
<li>123</li>
<li>456</li>
</ul>
<div class="my-block">
<p class="other">Foo</p>
</div>
<ul>
<li>789</li>
<li>012</li>
</ul>
</div>
我找到了old thread with a solution that almost works for me(页面上的最后一个解决方案,作者Dimitre Novatchev)并对其进行了修改。这是基于该解决方案的最小样式表:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="utf-8" indent="yes" />
<xsl:strip-space elements="*" />
<!-- IDENTITY TRANSFORM: -->
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<!-- NON-LIST ITEMS: -->
<xsl:template match="input">
<div class="output">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="paragraph">
<p>
<xsl:apply-templates />
</p>
</xsl:template>
<xsl:template match="other-block">
<div class="my-block">
<xsl:apply-templates select="descendant::other-text" />
</div>
</xsl:template>
<xsl:template match="other-text">
<p class="other">
<xsl:copy-of select="text()" />
</p>
</xsl:template>
<!-- LIST HANDLING: -->
<xsl:key name="kFollowingUL" match="listable"
use="generate-id(preceding-sibling::*[not(self::listable)][1])"/>
<xsl:template match="*[not(self::listable) and following-sibling::*[1][self::listable]]">
<xsl:call-template name="identity" />
<xsl:variable name="vFolUL"
select="key('kFollowingUL',generate-id())"/>
<xsl:if test="$vFolUL">
<ul>
<xsl:apply-templates mode="copy"
select="key('kFollowingUL',generate-id())" />
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="listable" mode="copy">
<li>
<xsl:value-of select="normalize-space()" />
</li>
</xsl:template>
<xsl:template match="listable" />
</xsl:stylesheet>
此方法的问题在于,它不会将转换应用于每个列表之前的最后一个不可列出的节点。尽管模板已应用于<paragraph>
的后代,但输入中的<other-block>
和<other-block>
节点被直接复制到输出:
<div class="output">
<p>abc</p>
<paragraph>def</paragraph>
<ul>
<li>123</li>
<li>456</li>
</ul>
<other-block>
<p class="other">Foo</p>
</other-block>
<ul>
<li>789</li>
<li>012</li>
</ul>
</div>
有人可以提出一种方法来修改早期的XSLT 1.0解决方案,并在可列出组之前添加最后一个节点的转换吗?
答案 0 :(得分:1)
我会这样:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/input">
<div class="output">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="paragraph">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="other-block">
<div class="my-block">
<xsl:apply-templates/>
</div>
</xsl:template>
<xsl:template match="other-text">
<p class="other">
<xsl:apply-templates/>
</p>
</xsl:template>
<xsl:template match="listable">
<xsl:if test="not(preceding-sibling::*[1][self::listable])">
<ul>
<xsl:apply-templates select="." mode="list"/>
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="listable" mode="list">
<li>
<xsl:apply-templates/>
</li>
<xsl:apply-templates select="following-sibling::*[1][self::listable]" mode="list"/>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
您的问题来自此模板:
<xsl:template match="*[not(self::listable) and following-sibling::*[1][self::listable]]">
<xsl:call-template name="identity" />
<xsl:variable name="vFolUL"
select="key('kFollowingUL',generate-id())"/>
<xsl:if test="$vFolUL">
<ul>
<xsl:apply-templates mode="copy"
select="key('kFollowingUL',generate-id())" />
</ul>
</xsl:if>
</xsl:template>
此匹配与listable
元素作为后一个同级元素的任何元素。然后,在内容模板中,它调用名为identity
的模板(在这种情况下,这是标识规则)。与other-block
元素的其他模板相比,它的default priority更好:
<xsl:template match="other-text">
<p class="other">
<xsl:apply-templates/>
</p>
</xsl:template>
我喜欢michael.hor257k解决方案,这是我在original answer中提供的方法。另一个可能的解决方案是遵循相同的原则:
<xsl:template match="*[not(self::listable) and following-sibling::*[1][self::listable]]">
<xsl:call-template name="separator" />
<xsl:variable name="vFolUL"
select="key('kFollowingUL',generate-id())"/>
<xsl:if test="$vFolUL">
<ul>
<xsl:apply-templates mode="copy"
select="key('kFollowingUL',generate-id())" />
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="other-text" name="separator">
<p class="other">
<xsl:apply-templates/>
</p>
</xsl:template>
但是,请注意,这不能很好地扩展。