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

Posted

技术标签:

【中文标题】使用 XSLT 对 HTML 输出进行分组(muenchian 分组?)【英文标题】:Group a HTML output with XSLT(muenchian grouping?) 【发布时间】:2017-11-13 22:11:23 【问题描述】:

我想以一种特殊的方式对输出进行分组,也许使用 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)

我希望在表格中像这样对输出进行分组:

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)

【问题讨论】:

请将示例简化为仅演示问题所必需的内容 - 请参阅:minimal reproducible example。 这已经从一个更大的文件中减少了。我认为这里的一切都是必要的。 我不认为标记化部分对于对其结果进行分组是必要的。 【参考方案1】:

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

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

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

解决方案就是在创建chart 属性时使用normalize-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>

【讨论】:

工作得很好!非常感谢:)

以上是关于使用 XSLT 对 HTML 输出进行分组(muenchian 分组?)的主要内容,如果未能解决你的问题,请参考以下文章

使用 XSLT 程序对具有逗号分隔值的 XML 元素进行分组

在 DotNetNuke 表单和列表中使用 XSLT 对项目进行分组

XSLT 对特定同级进行分组

是否可以使用 XSLT 1.0 对条目进行分组?

在 XSLT 中对每个父节点下的子节点进行分组

在xslt中对缺少的表元素进行分组和填充