【问题标题】:How to create html list from flat xml file using XSLT如何使用 XSLT 从平面 xml 文件创建 html 列表
【发布时间】:2010-10-12 22:03:13
【问题描述】:

我正在寻找一种使用 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>

请记住,我不知道究竟会有多少&lt;list&gt;

到目前为止,我有这个:

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

我知道为什么我得到重复的&lt;ol&gt; 元素,但我不知道如何阻止它。相当脑筋急转弯。

任何帮助将不胜感激。

【问题讨论】:

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


【解决方案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

【讨论】:

  • 谢谢!这看起来是个不错的方法。
  • +1 好答案。但我觉得这样的“砖头”模板规则不好用。
【解决方案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>

注意:细粒度遍历。

编辑:压缩代码。

【讨论】:

  • 感谢您的帮助,这是一种可能的方法。但是这种方法不是很难维持吗?因为我必须在我匹配的每个顶级元素上添加以下兄弟选择。
  • @joe:此样式表有四个规则:默认细粒度遍历、p 规则、ol 规则和li 规则。所以,这是一对一的转换。我没有看到 难以维护 的问题。你问 Dimitre:如果我在列表之前有一个元素 H1 怎么办。那么,你必须声明与H1 有什么关系。现在,它被绕过了。因此,有两种解决方案:复制您不想转换的所有内容或为H1 添加规则。
  • 我的意思是如果我添加
  • @joe:是的,将模板应用于第一个孩子和第一个跟随兄弟是这种工作方式。
【解决方案3】:

这种转变

<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 在评论中表示,现在他想要一个解决方案,其中 anylist 元素可以分隔一组相邻的list 兄弟姐妹.

这是更改后问题的解决方案

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

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

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

产生想要的正确结果

<t>
    <bara>blah blah</bara>
    <ol>
        <li>num1</li>
        <li>num2</li>
        <li>num3</li>
    </ol>
    <vara>blah blah</vara>
    <ol>
        <li>num1</li>
        <li>num2</li>
    </ol>
    <dara>blah blah blah blah blah</dara>
</t>

【讨论】:

    猜你喜欢
    • 2019-08-17
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    相关资源
    最近更新 更多