使用 XSLT(muenchian 分组?)对 HTML 输出进行分组

Group a HTML output with XSLT(muenchian grouping?)

我希望以特殊方式对输出进行分组,也许使用 muenchian 分组?但我卡住了

这是 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<NTC_PUBLICATION xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="test.xsd">
    <SECTION_CONTENT_LIST_ITEM>
        <NM_TORP_NTC>
            <PUBLISH_NUMBER>138</PUBLISH_NUMBER>
            <TERM>Temporary</TERM>
            <NM_CHART_AFFECTED_LIST>
                <CHART_NUM>NZ 21 (INT 641), NZ 522, NZ 5219</CHART_NUM>
            </NM_CHART_AFFECTED_LIST>
        </NM_TORP_NTC>
    </SECTION_CONTENT_LIST_ITEM>
    <SECTION_CONTENT_LIST_ITEM>
        <NM_TORP_NTC>
            <PUBLISH_NUMBER>139</PUBLISH_NUMBER>
            <TERM>Temporary</TERM>
            <NM_CHART_AFFECTED_LIST>
                <CHART_NUM>NZ 522, NZ 5321</CHART_NUM>
            </NM_CHART_AFFECTED_LIST>
        </NM_TORP_NTC>
    </SECTION_CONTENT_LIST_ITEM>
    <SECTION_CONTENT_LIST_ITEM>
        <NM_TORP_NTC>
            <PUBLISH_NUMBER>141</PUBLISH_NUMBER>
            <TERM>Preliminary</TERM>
            <NM_CHART_AFFECTED_LIST>
                <CHART_NUM>NZ 268</CHART_NUM>
            </NM_CHART_AFFECTED_LIST>
        </NM_TORP_NTC>
    </SECTION_CONTENT_LIST_ITEM>
    <SECTION_CONTENT_LIST_ITEM>
        <NM_TORP_NTC>
            <PUBLISH_NUMBER>143</PUBLISH_NUMBER>
            <TERM>Preliminary</TERM>
            <NM_CHART_AFFECTED_LIST>
                <CHART_NUM>NZ 26, NZ 268</CHART_NUM>
            </NM_CHART_AFFECTED_LIST>
        </NM_TORP_NTC>
    </SECTION_CONTENT_LIST_ITEM>
</NTC_PUBLICATION>

这是我目前拥有的 XSLT 样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:set="http://exslt.org/sets" xmlns:exslt="http://exslt.org/common" exclude-result-prefixes="msxsl exslt">
    <xsl:output method="html" encoding="UTF-8" indent="yes"
        doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
        doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
    <xsl:key name="item" match="item" use="@chart"/>
    <xsl:template match="/">
        <div>
            <xsl:variable name="result">
                <xsl:for-each select="//NM_TORP_NTC">
                    <xsl:call-template name="split">
                        <xsl:with-param name="notice" select="PUBLISH_NUMBER"/>
                        <xsl:with-param name="string" select="NM_CHART_AFFECTED_LIST/CHART_NUM"/>
                        <xsl:with-param name="term" select="TERM"/>
                    </xsl:call-template>
                </xsl:for-each>
            </xsl:variable>
            <xsl:copy-of select="$result"/>
            <table style="padding-left:200px;align:center;">
                <xsl:choose>
                    <xsl:when test="function-available('msxsl:node-set')">
                        <xsl:call-template name="sub-class">
                            <xsl:with-param name="result" select="msxsl:node-set($result)"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="sub-class">
                            <xsl:with-param name="result" select="exslt:node-set($result)"/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </table>
            <!-- Apply the templates for each Notice -->
            <xsl:apply-templates select="SECTION_CONTENT_LIST_ITEM/NM_TORP_NTC"/>
        </div>
    </xsl:template>

    <xsl:template name="split">
        <xsl:param name="notice"/>
        <xsl:param name="string"/>
        <xsl:param name="term"/>
        <xsl:if test="substring-after($string,',')!=''">
            <item>
                <xsl:attribute name="notice">
                    <xsl:value-of select="$notice"/>
                </xsl:attribute>
                <xsl:attribute name="chart">
                    <xsl:value-of select="substring-before($string,',')"/>
                </xsl:attribute>
                <xsl:attribute name="term">
                    <xsl:value-of select="$term"/>
                </xsl:attribute>
            </item>
        </xsl:if>
        <xsl:choose>
            <xsl:when test="substring-after($string,',')!=''">
                <xsl:call-template name="split">
                    <xsl:with-param name="notice" select="$notice"/>
                    <xsl:with-param name="string" select="substring-after($string,',')"/>
                    <xsl:with-param name="term" select="$term"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="not(contains($string,','))">
                <item>
                    <xsl:attribute name="notice">
                        <xsl:value-of select="$notice"/>
                    </xsl:attribute>
                    <xsl:attribute name="chart">
                        <xsl:value-of select="$string"/>
                    </xsl:attribute>
                    <xsl:attribute name="term">
                        <xsl:value-of select="$term"/>
                    </xsl:attribute>
                </item>
            </xsl:when>
        </xsl:choose>
    </xsl:template>

    <xsl:template name="sub-class">
        <xsl:param name="result"/>
        <xsl:for-each select="$result/item[count(. | key('item', @chart)[1]) = 1]">
            <xsl:sort select="@chart" data-type="text" order="ascending"/>
            <tr>
                <td>
                    <xsl:value-of select="@chart"/>
                </td>
                <td>
                    <xsl:for-each select="key('item', @chart)">
                        <xsl:sort select="@notice" data-type="number"/>
                        <xsl:variable name="pos" select="position()"/>
                        <xsl:if test="$pos!=1">
                            <xsl:text>, </xsl:text>
                        </xsl:if>
                        <xsl:value-of select="@notice"/><xsl:text> </xsl:text>
                        <xsl:if test="@term='Temporary'">
                            <xsl:text>(T)</xsl:text>
                        </xsl:if>
                        <xsl:if test="@term='Preliminary'">
                            <xsl:text>(P)</xsl:text>
                        </xsl:if>
                    </xsl:for-each>
                </td>
            </tr>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

目前的输出是这样的((T)是临时的,(P)是初步的):

NZ 268     143 (P)
NZ 5219    138 (T)
NZ 522     138 (T)
NZ 5321    139 (T)
NZ 21      138 (T)
NZ 26      143 (P)
NZ 268     141 (P)
NZ 522     139 (T)

我希望输出像这样分组,在 table:

NZ 21      138 (T)
NZ 26      143 (P)
NZ 268     141 (P), 143 (P)
NZ 522     139 (T), 141 (P)
NZ 5219    138 (T)
NZ 5321    139 (T)

我看到您的 XSLT 复制了 $result 变量(用于调试),如果您仔细观察就会发现您希望分组的项目之间存在差异

<item notice="141" chart="NZ 268" term="Preliminary"></item>
....
<item notice="143" chart=" NZ 268" term="Preliminary"></item>

chart 属性的一个值之前有一个 space,这意味着就分组而言,这些值是不同的。

解决方案是在创建 chart 属性时简单地使用 normalize-space 到 trim space。

尝试将您的 split 模板更改为此....

<xsl:template name="split">
    <xsl:param name="notice"/>
    <xsl:param name="string"/>
    <xsl:param name="term"/>
    <xsl:if test="substring-after($string,',')!=''">
        <item>
            <xsl:attribute name="notice">
                <xsl:value-of select="$notice"/>
            </xsl:attribute>
            <xsl:attribute name="chart">
                <xsl:value-of select="normalize-space(substring-before($string,','))"/>
            </xsl:attribute>
            <xsl:attribute name="term">
                <xsl:value-of select="$term"/>
            </xsl:attribute>
        </item>
    </xsl:if>
    <xsl:choose>
        <xsl:when test="substring-after($string,',')!=''">
            <xsl:call-template name="split">
                <xsl:with-param name="notice" select="$notice"/>
                <xsl:with-param name="string" select="substring-after($string,',')"/>
                <xsl:with-param name="term" select="$term"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="not(contains($string,','))">
            <item>
                <xsl:attribute name="notice">
                    <xsl:value-of select="$notice"/>
                </xsl:attribute>
                <xsl:attribute name="chart">
                    <xsl:value-of select="normalize-space($string)"/>
                </xsl:attribute>
                <xsl:attribute name="term">
                    <xsl:value-of select="$term"/>
                </xsl:attribute>
            </item>
        </xsl:when>
    </xsl:choose>
</xsl:template>

或者将它简化为这个....

<xsl:template name="split">
    <xsl:param name="notice"/>
    <xsl:param name="string"/>
    <xsl:param name="term"/>
    <xsl:if test="normalize-space($string)!=''">
        <item>
            <xsl:attribute name="notice">
                <xsl:value-of select="$notice"/>
            </xsl:attribute>
            <xsl:attribute name="chart">
                <xsl:value-of select="normalize-space(substring-before(concat($string, ','),','))"/>
            </xsl:attribute>
            <xsl:attribute name="term">
                <xsl:value-of select="$term"/>
            </xsl:attribute>
        </item>
    </xsl:if>
    <xsl:if test="contains($string,',')">
        <xsl:call-template name="split">
            <xsl:with-param name="notice" select="$notice"/>
            <xsl:with-param name="string" select="substring-after($string,',')"/>
            <xsl:with-param name="term" select="$term"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>