如何使用 XSLT 从平面 XML 文件创建 HTML 列表(基于前面的问题)



我需要使用 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>

我找到了一个旧线程,其中包含几乎适合我的解决方案(页面上的最后一个解决方案,由迪米特雷·诺瓦切夫(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 解决方案并在可列出组之前添加最后一个节点的转换?

我会这样做:

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>

您的问题来自此模板:

<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元素,这比其他模板具有更好的默认优先级

<xsl:template match="other-text">
    <p class="other">
        <xsl:apply-templates/>      
    </p>
</xsl:template>

我喜欢michael.hor257k解决方案,这是我在原始答案中给出的方法。 另一种可能的解决方案是遵循相同的原则:

<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>

但是,请注意,这不能很好地扩展。

最新更新