XSLT 中除第一个元素外的元素分组

Grouping of elements in XSLT except the first one

(这个问题的标题可能不对,如果有人有更好的建议,我会改的。)

我有一个体育赛事的结果列表,我想在其中输出以下内容:

<p>
1) John Doe, Norway 1.23,45, 2) Dave Doe, Norway 0.15 behind, 3) Nicholas Doe, Norway Same Time, 4) Barack Doe, USA S.T, 5) Vladimir Doe, Russia 1.00,00 behind, 6) Xi Min Doe, China S.T. .... 
</p>

书面语: 第一个结果有总时间。第二个结果使用差距,我们在后面添加,如果第三个结果与第二个结果有相同的差距,我们同时给出一个。如果第四个结果与第二个结果有相同的差距,我们将其缩短为 s.t。 如果有新的差距,我们写出差距,但所有与本组第一名差距相同的结果,只能有s.t.

替代输出:

<results>
<result>
<rank>1</rank>
<name>John</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:23:45</time>
<gap>00:00</gap>
</result>

<result>
<rank>2</rank>
<name>Dave</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>3</rank>
<name>Nicholas</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>same time</gap>
</result>

<result>
<rank>4</rank>
<name>Barack</name>
<lastname>Doe</name>
<country>USA</country>
<time>1:24:00</time>
<gap>s.t.</gap>
</result>

<result>
<rank>5</rank>
<name>Vladimir</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>

<result>
<rank>6</rank>
<name>Xi Min</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>s.t.</gap>
</result>
</results>

XML 与此非常相似:

<results>
<result>
<rank>1</rank>
<name>John</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:23:45</time>
<gap>00:00</gap>
</result>

<result>
<rank>2</rank>
<name>Dave</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>3</rank>
<name>Nicholas</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>4</rank>
<name>Barack</name>
<lastname>Doe</name>
<country>USA</country>
<time>1:24:00</time>
<gap>00:15</gap>
</result>

<result>
<rank>5</rank>
<name>Vladimir</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>

<result>
<rank>6</rank>
<name>Xi Min</name>
<lastname>Doe</name>
<country>Norway</country>
<time>1:24:45</time>
<gap>01:15</gap>
</result>
</results>

输出,如代码: 我正在使用 XSLT 2.0,但可能有人在使用 1.0,所以任何支持 XSLT 的任何一个(或全部)版本的建议,我和许多处于相同情况的人都会非常高兴。

以下样式表:

XSLT 2.0

<xsl:stylesheet version="2.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="*"/>

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

<xsl:template match="/results">
    <xsl:copy>
        <xsl:for-each-group select="result" group-by="gap">
            <xsl:apply-templates select="current-group()"/>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template match="result">
    <xsl:copy>
        <xsl:apply-templates select="* except gap"/>
        <gap>
            <xsl:value-of select="if (position()=1) then gap else 'SAME TIME'" />
        </gap>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

当应用于您的示例输入时(在更正不匹配的 namelastname 标签后!),将 return:

<?xml version="1.0" encoding="UTF-8"?>
<results>
   <result>
      <rank>1</rank>
      <name>John</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:23:45</time>
      <gap>00:00</gap>
   </result>
   <result>
      <rank>2</rank>
      <name>Dave</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>00:15</gap>
   </result>
   <result>
      <rank>3</rank>
      <name>Nicholas</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>4</rank>
      <name>Barack</name>
      <lastname>Doe</lastname>
      <country>USA</country>
      <time>1:24:00</time>
      <gap>SAME TIME</gap>
   </result>
   <result>
      <rank>5</rank>
      <name>Vladimir</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>01:15</gap>
   </result>
   <result>
      <rank>6</rank>
      <name>Xi Min</name>
      <lastname>Doe</lastname>
      <country>Norway</country>
      <time>1:24:45</time>
      <gap>SAME TIME</gap>
   </result>
</results>

这是我解决问题的方法。在 post 之后我被告知只呈现前 10 个,所以我在代码周围添加了一个 if rank <= 10。

此处是原始数据源结构的一部分(仅显示数据、名称和 ID 的部分已被删除)。

如果您发现任何奇怪的地方,请随时发表评论或提问。

<RESULT>
        <rank>42</rank>
        <bib>43</bib>
        <name>John</name>
        <surname>Doe</surname>
        <short_team>ABA</short_team>
        <long_team>ABBA</long_team>
        <time>04:42:18</time>
        <gap>13</gap>
        <code>ABA1234</code>
        <nationality>BEL</nationality>
    </RESULT>

要分组并获得正确输出的 XSLT 部分。请注意,这是生产代码,因此某些文本可能与示例不同。我在有差异的地方添加了评论:

<xsl:for-each-group select="RESULT" group-by="time">
            <xsl:for-each select="current-group()">
                <xsl:if test="rank &lt;= 10">
                    <xsl:value-of select="rank"/>
                    <xsl:text>) </xsl:text>
                    <xsl:value-of select="name"/>
                    <xsl:text> </xsl:text>
                    <xsl:variable name="surname">
                        <xsl:for-each select="tokenize(surname, ' ')">
                            <xsl:value-of
                                select="concat(upper-case(substring(., 1, 1)), lower-case(substring(., 2)))"/>
                            <xsl:text> </xsl:text>
                        </xsl:for-each>
                    </xsl:variable>
                    <xsl:value-of select="normalize-space($surname)"/>

                    <xsl:text>, </xsl:text>
                    <xsl:value-of select="nationality"/>
                    <xsl:text> (</xsl:text>
                    <xsl:value-of select="long_team"/>
                    <xsl:text>)</xsl:text>
                    <xsl:text> </xsl:text>
                    <xsl:choose>
                        <xsl:when test="rank = '1'">
                            <!-- the replace is just there to get the time in the correct format -->
                            <xsl:value-of select="replace(time, ':', '.')"/>
                        </xsl:when>
                        <xsl:when test="rank = '2'">
                            <xsl:choose>
                                <xsl:when test="gap != ''">
                                    <xsl:if test="string-length(gap) = 2">
                                       <!-- this text is here because the gap might just have two characters, and we want it like this 0.12 -->
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                    <!-- This is norwegian text for minutes behind (shortened) -->
                                    <xsl:text> min. bak</xsl:text>
                                </xsl:when>
                                <xsl:otherwise>
                                    <!-- This is norwegian for same time -->
                                    <xsl:text> samme tid</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:choose>
                                <xsl:when test="position() = 1">
                                    <xsl:if test="string-length(gap) = 2">
                                        <xsl:text>0.</xsl:text>
                                    </xsl:if>
                                    <xsl:value-of select="replace(gap, ':', '.')"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:text> s.t.</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>

                        </xsl:otherwise>
                    </xsl:choose>
                    <xsl:choose>
                        <xsl:when test="position() != last()">
                            <xsl:text>, </xsl:text>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:text>, </xsl:text>
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:if>
            </xsl:for-each>
        </xsl:for-each-group>