簡體   English   中英

如何使用 XSLT 從平面 XML 文件創建 HTML 列表(基於之前的問題)

[英]How to create an HTML list from flat XML file using XSLT (based on earlier question)

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

但是,請注意,這不能很好地擴展。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM