如何使用XSLT从平面xml文件创建html列表

时间:2010-10-12 22:03:14

标签: xml xslt xpath xslt-2.0 xslt-grouping

好的家伙我认为这是一个棘手的问题......

我正在寻找一种使用XSLT执行以下操作的简洁方法:

转换此来源:

<para>blah blah</para>
<list>num1</list>
<list>num2</list>
<list>num3</list>
<para>blah blah</para>
<list>num1</list>
<list>num2</list>
<para>blah blah blah blah blah</para>

到此输出:

<p>blah blah</p>
<ol>
<li>num1</li>
<li>num2</li>
<li>num3</li>
</ol>
<p>blah blah</p>
<ol>
<li>num1</li>
<li>num2</li>
</ol>
<p>blah blah blah blah blah</p>

请记住,我不确切知道会有多少<list>

到目前为止,我有这个:

<xsl:template match="para">
<p><xsl:value-of select="." /></p>
</xsl:template>

<xsl:template match="list">
<ol><li><xsl:value-of select="." /></li></ol>
</xsl:template>

但我的输出看起来像这样:

<p>blah blah</p>    
<ol><li>num1</li></ol>
<ol><li>num2</li></ol>
<ol><li>num3</li></ol>
<p>blah blah</p>
<ol><li>num1</li></ol>
<ol><li>num2</li></ol>
<p>blah blah blah blah blah</p>

我知道为什么我会收到重复的<ol>元素,但我不知道如何阻止它。相当一个脑筋急转弯。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:2)

这个XSLT 1.0样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()">
        <xsl:apply-templates select="node()[1]|following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="para">
        <p>
            <xsl:value-of select="."/>
        </p>
        <xsl:apply-templates select="following-sibling::node()[1]"/>
    </xsl:template>
    <xsl:template match="list[preceding-sibling::node()[1][not(self::list)]]">
        <ol>
            <xsl:call-template name="makeList"/>
        </ol>
        <xsl:apply-templates select="following-sibling::node()
                                              [not(self::list)][1]"/>
    </xsl:template>
    <xsl:template match="list" name="makeList">
        <li>
            <xsl:value-of select="."/>
        </li>
        <xsl:apply-templates select="following-sibling::node()[1]
                                              [self::list]"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<p>blah blah</p>
<ol>
        <li>num1</li>
        <li>num2</li>
        <li>num3</li>
</ol>
<p>blah blah</p>
<ol>
        <li>num1</li>
        <li>num2</li>
</ol>
<p>blah blah blah blah blah</p>

注意:细粒度遍历。

编辑:压缩代码。

答案 1 :(得分:1)

XSLT 2.0具有专门用于此类操作的工具:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="xml">
        <xsl:for-each-group select="*" group-adjacent="boolean(self::list)">
            <xsl:choose>
                <xsl:when test="current-grouping-key()">
                    <ol>
                        <xsl:apply-templates select="current-group()"/>
                    </ol>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:template>
    <xsl:template match="para">
        <p>
            <xsl:apply-templates/>
        </p>
    </xsl:template>
    <xsl:template match="list">
        <li>
            <xsl:apply-templates/>
        </li>
    </xsl:template>
</xsl:stylesheet>

使用此XML:

<xml>
    <para>blah blah</para>
    <list>num1</list>
    <list>num2</list>
    <list>num3</list>
    <para>blah blah</para>
    <list>num1</list>
    <list>num2</list>
    <para>blah blah blah blah blah</para>
</xml>

您将获得所需的输出:

<p>blah blah</p>
<ol>
    <li>num1</li>
    <li>num2</li>
    <li>num3</li>
</ol>
<p>blah blah</p>
<ol>
    <li>num1</li>
    <li>num2</li>
</ol>
<p>blah blah blah blah blah</p>

您应该在http://www.w3.org/TR/xslt20/#xsl-for-each-group

上阅读for-each-group

答案 2 :(得分:0)

此转化

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kFollowing" match="list"
  use="generate-id(preceding-sibling::para[1])"/>

 <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="para">
  <p>
    <xsl:apply-templates/>
  </p>
   <xsl:variable name="vFol"
    select="key('kFollowing',generate-id())"/>
   <xsl:if test="$vFol">
      <ol>
       <xsl:apply-templates mode="copy"
           select="key('kFollowing',generate-id())"/>
      </ol>
  </xsl:if>
 </xsl:template>

 <xsl:template match="list" mode="copy">
  <li><xsl:value-of select="."/></li>
 </xsl:template>
 <xsl:template match="list"/>
</xsl:stylesheet>

应用于以下XML文档(将提供的输入包装在单个顶部元素中):

<t>
    <para>blah blah</para>
    <list>num1</list>
    <list>num2</list>
    <list>num3</list>
    <para>blah blah</para>
    <list>num1</list>
    <list>num2</list>
    <para>blah blah blah blah blah</para>
</t>

生成想要的正确结果

<t>
    <p>blah blah</p>
    <ol>
        <li>num1</li>
        <li>num2</li>
        <li>num3</li>
    </ol>
    <p>blah blah</p>
    <ol>
        <li>num1</li>
        <li>num2</li>
    </ol>
    <p>blah blah blah blah blah</p>
</t>

更新:OP已在评论中指出现在他想要一个解决方案,其中任何list元素可以划分一组相邻的{{ 1}}兄弟姐妹。

以下是已更改问题的解决方案

list

对以下XML文档应用此转换时(请注意,分隔元素现在具有随机名称):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kFollowing" match="list"
  use="generate-id(preceding-sibling::*[not(self::list)][1])"/>

 <xsl:template match="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*[not(self::list) and following-sibling::*[1][self::list]]">
  <xsl:call-template name="identity"/>
   <xsl:variable name="vFol"
    select="key('kFollowing',generate-id())"/>
   <xsl:if test="$vFol">
      <ol>
       <xsl:apply-templates mode="copy"
           select="key('kFollowing',generate-id())"/>
      </ol>
  </xsl:if>
 </xsl:template>

 <xsl:template match="list" mode="copy">
  <li><xsl:value-of select="."/></li>
 </xsl:template>
 <xsl:template match="list"/>
</xsl:stylesheet>

产生了想要的正确结果

<t>
    <bara>blah blah</bara>
    <list>num1</list>
    <list>num2</list>
    <list>num3</list>
    <vara>blah blah</vara>
    <list>num1</list>
    <list>num2</list>
    <dara>blah blah blah blah blah</dara>
</t>