XSLT 1.0 将同一级别的多个相同节点以不同的值分组

Posted

技术标签:

【中文标题】XSLT 1.0 将同一级别的多个相同节点以不同的值分组【英文标题】:XSLT 1.0 grouping multiple same nodes on same level with different values 【发布时间】:2015-03-20 06:00:00 【问题描述】:

我有一个元素列表:

<vehiciles>
  <vehicile value="_CAR">CAR</vehicile>
  <vehicile value="01">vehicile1</vehicile>
  <vehicile value="02">vehicile2</vehicile>
  <vehicile value="03">vehicile3</vehicile>
  <vehicile value="_TRUCK">TRUCK</vehicile>
  <vehicile value="04">vehicile4</vehicile>
  <vehicile value="05">vehicile5</vehicile>
  <vehicile value="06">vehicile6</vehicile>
</vehiciles>

不幸的是,我无法更改结构,但我必须按车辆指示的类别(在 html select/optgroup 标记中)分组,该值以下划线开头。

我想达到的结果:

<select>
  <optgroup label="CAR">
    <option value="01">vehicile1</option>
    <option value="02">vehicile2</option>
    <option value="03">vehicile3</option> 
  </optgroup>
  <opgroup label="TRUCK">
    <option value="04">vehicile4</option>
    <option value="05">vehicile5</option>
    <option value="06">vehicile6</option>
  </optgroup>
</select>

我尝试的是:

<xsl:template match="field" mode="dropdown_list">
  <select>
    <xsl:choose>
      <xsl:when test="vehiciles/vehicile[starts-with(@value, '_')]">
        <xsl:for-each select="vehiciles/vehicile[starts-with(@value, '_')]">
          <xsl:variable name="lastValue" select="following-sibling::*[starts-with(@value, '_')][@value]" />
          <optgroup>
            <xsl:attribute name="label">
              <xsl:value-of select="text()"/>
            </xsl:attribute>

            <xsl:for-each select="following-sibling::*[not(preceding::vehicile[1][@value = $lastValue])]">
              <option value="@value">
                <xsl:value-of select="text()"/>
              </option>
            </xsl:for-each>

          </optgroup>
        </xsl:for-each>

     </xsl:when>
     <xsl:otherwise>
       <!-- something here -->
     </xsl:otherwise>
   </xsl:choose>
 </select>
</xsl:template>

它输出第二个循环不错,但首先包含所有元素。尝试了几个小时没有运气。

尝试通过递归进行但失败以及 Muenchian 分组。

有没有办法从某个节点向上查找符合条件的第一个兄弟节点?还是其他方式?

任何帮助表示赞赏。

【问题讨论】:

【参考方案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:key name="vehicile" match="vehicile[not(starts-with(@value, '_'))]" use="generate-id(preceding-sibling::vehicile[starts-with(@value, '_')][1])" />

<xsl:template match="/vehiciles">
    <select>
        <xsl:for-each select="vehicile[starts-with(@value, '_')]">
            <optgroup label="substring-after(@value, '_')">
                <xsl:for-each select="key('vehicile', generate-id())">
                    <option value="@value">
                        <xsl:value-of select="."/>
                    </option>
                </xsl:for-each>
            </optgroup>
        </xsl:for-each>
    </select>
</xsl:template>

</xsl:stylesheet>

【讨论】:

像魅力一样工作!谢谢!【参考方案2】:

使用

<xsl:template match="vehiciles">
  <select>
    <xsl:apply-templates select="vehicile[starts-with(@value, '_')]"/>
  </select>
</xsl:template>

<xsl:template match="vehicile[starts-with(@value, '_')]">
  <optgroup label="substring(@value, 2)">
    <xsl:apply-templates select="key('k1', generate-id())"/>
  </optgroup>
</xsl:template>

<xsl:key name="k1" match="vehicile[not(starts-with(@value, '_'))]"
  use="generate-id(preceding-sibling::vehicile[starts-with(@value, '_')][1])"/>

<xsl:template match="vehicile[not(starts-with(@value, '_'))]">
  <xsl:variable name="num">
    <xsl:number count="vehicile[not(starts-with(@value, '_'))]"/>
  </xsl:variable>
  <option value="format-number($num, '00')"><xsl:value-of select="."/></option>
</xsl:template>

完整示例请参见http://xsltransform.net/jyH9rMi。

【讨论】:

感谢您的帮助。虽然@michael 解决方案更加简洁。

以上是关于XSLT 1.0 将同一级别的多个相同节点以不同的值分组的主要内容,如果未能解决你的问题,请参考以下文章

具有相同级别下重复节点的 XSLT 变换

xslt 1.0 使用复合键分组(在不同级别)

XSLT 1.0 - 用 HTML 包装的多个子节点模板

使用 xslt 将 xml 复杂节点元素拆分为多个节点

XSLT 将来自多个节点的属性连接成单个值

使用 xslt 1.0 从两个不同的父标签中查找不同的元素