【问题标题】:How should i group data in xslt?我应该如何在 xslt 中对数据进行分组?
【发布时间】:2019-04-03 07:59:55
【问题描述】:

我需要按时期对历史进行分组,但我无法执行此分组。 有人可以在这里帮忙吗?我尝试使用 xsl 键,但它只针对第一个响应执行操作。 你能建议任何不同的方法吗?是否有任何分组方法,如下面的预期输出所示。

输入

<TEST>
     <RESPONSE>
        <NUMBER>XXXX</NUMBER>
        <HISTORY>
             <Period Year="2013" Month="Apr" Value="77"></Period>
             <Period Year="2013" Month="Mar" Value="99"></Period>
             <Period Year="2013" Month="Feb" Value="88"></Period>
             <Period Year="2012" Month="Jan" Value="11"></Period>
             <Period Year="2012" Month="Mar" Value="22"></Period>
             <Period Year="2011" Month="Apr" Value="444"></Period>
         </HISTORY>
     </RESPONSE>
     <RESPONSE>
        <NUMBER>ZZZZ</NUMBER>
        <HISTORY>
             <Period Year="2016" Month="Jan" Value="999"></Period>
             <Period Year="2016" Month="Mar" Value="454"></Period>
             <Period Year="2015" Month="Dec" Value="234"></Period>
             <Period Year="2014" Month="Jan" Value="767"></Period>
             <Period Year="2014" Month="Sep" Value="667"></Period>
             <Period Year="2013" Month="May" Value="112"></Period>
         </HISTORY>
     </RESPONSE>
</TEST>

预期输出

<TEST>
     <RESPONSE>
        <NUMBER>XXXX</NUMBER>
        <HISTORY>
             <Period Year="2013" Month="Apr" Value="77"></Period>
             <Period Year="2013" Month="Mar" Value="99"></Period>
             <Period Year="2013" Month="Feb" Value="88"></Period>
             <Period Year="2012" Month="Jan" Value="11"></Period>
             <Period Year="2012" Month="Mar" Value="22"></Period>
             <Period Year="2011" Month="Apr" Value="444"></Period>
         </HISTORY>
         <GROUP-HISTORY>
                <YEAR Value="2013">
                        <Months Month="Apr" Value="77"/>
                        <Months Month="Mar" Value="99"/>
                        <Months Month="Feb" Value="88"/>
                </YEAR>
                <YEAR Value="2012">
                        <Months Month="Jan" Value="11"/>
                        <Months Month="Mar" Value="22"/>
                </YEAR>
                <YEAR Value="2011">
                        <Months Month="Apr" Value="444"/>       
                </YEAR>
         </GROUP-HISTORY>
     </RESPONSE>
     <RESPONSE>
        <NUMBER>ZZZZ</NUMBER>
        <HISTORY>
             <Period Year="2016" Month="Jan" Value="999"></Period>
             <Period Year="2016" Month="Mar" Value="454"></Period>
             <Period Year="2015" Month="Dec" Value="234"></Period>
             <Period Year="2014" Month="Jan" Value="767"></Period>
             <Period Year="2014" Month="Sep" Value="667"></Period>
             <Period Year="2013" Month="May" Value="112"></Period>
         </HISTORY>
         <GROUP-HISTORY>
                <YEAR Value="2016">
                        <Months Month="Jan" Value="999"/>
                        <Months Month="Mar" Value="454"/>   
                </YEAR>
                <YEAR Value="2015">
                        <Months Month="Dec" Value="234"/>       
                </YEAR>
                <YEAR Value="2014">
                        <Months Month="Jan" Value="767"/>   
                        <Months Month="Sep" Value="667"/>                               
                </YEAR>
                <YEAR Value="2013">
                        <Months Month="May" Value="112"/>       
                </YEAR>
         </GROUP-HISTORY>
     </RESPONSE>
</TEST>

示例 xslt

<xsl:stylesheet xmlns:xalan="http://xml.apache.org/xalan"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:key name="years" match="/TEST/RESPONSE/HISTORY/Period" use="@Year"/>
    <xsl:template match="TEST">
        <xsl:element name="TEST">
            <xsl:apply-templates select="RESPONSE"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="node() | @*">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="HISTORY">
        <xsl:element name="GROUP-HISTORY">
            <xsl:for-each
                select="/TEST/RESPONSE/HISTORY/Period[generate-id(.) = generate-id(key('years', @Year)[1])]">
                <xsl:sort select="@Year" order="descending"/>
                <xsl:variable name="currY" select="@Year"/>
                <xsl:element name="Year">
                    <xsl:attribute name="Value">
                        <xsl:value-of select="$currY"/>
                    </xsl:attribute>
                    <xsl:for-each select="/TEST/RESPONSE/HISTORY/Period[@Year = $currY]">
                        <xsl:element name="Months">
                            <xsl:attribute name="Month">
                                <xsl:value-of select="@Month"/>
                            </xsl:attribute>
                            <xsl:attribute name="Value">
                                <xsl:value-of select="@Value"/>
                            </xsl:attribute>
                        </xsl:element>
                    </xsl:for-each>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

【问题讨论】:

  • 能否请您发布您的 XSLT,以便我们更正它的任何问题?如果您使用的是xsl:key,那么您实际上可能不是那种解决方案。谢谢!
  • 你可以在上面找到xslt

标签: xslt xslt-1.0 xslt-grouping


【解决方案1】:

主要问题是您需要在密钥中考虑NUMBER,否则您会将整个文档中的所有匹配年份分组

<xsl:key name="years" match="Period" use="concat(../../NUMBER, '|', @Year)"/>

此外,对于您的第一个 xsl:for-each,您可以使用 /TEST/RESPONSE/HISTORY/Period 开始选择表达式,当您确实需要它与当前 HISTORY 相关时,它还将检查文档中的所有句点,如下所示:

<xsl:for-each select="Period[generate-id(.) = generate-id(key('years', concat(../../NUMBER, '|', @Year))[1])]">

试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:key name="years" match="Period" use="concat(../../NUMBER, '|', @Year)"/>

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

    <xsl:template match="HISTORY">
        <xsl:call-template name="identity" />
        <GROUP-HISTORY>
            <xsl:for-each select="Period[generate-id(.) = generate-id(key('years', concat(../../NUMBER, '|', @Year))[1])]">
                <xsl:sort select="@Year" order="descending"/>
                <xsl:variable name="currY" select="@Year"/>
                <Year Value="{$currY}">
                    <xsl:for-each select="key('years', concat(../../NUMBER, '|', $currY))">
                        <Months Month="{@Month}" Value="{@Value}" />
                    </xsl:for-each>
                </Year>
            </xsl:for-each>
        </GROUP-HISTORY>
    </xsl:template>
</xsl:stylesheet>

请注意:

  1. 您不需要在xsl:key 中指定匹配元素的完整路径(除非您有其他同名但不同路径的元素不想匹配)。
  2. 您的模板匹配 TEST 是不必要的,因为身份模板会做同样的事情。
  3. 您不需要使用xsl:element,其中元素的名称是静态的。直接写出元素标签就行了。
  4. 您可以使用Attribute Value Templates 来简化属性的创建。

【讨论】:

  • 嗨,蒂姆,感谢您的输入,但在某些情况下我得到 NUMBER 作为重复项,那么我应该如何处理这个
  • 您可以在xsltfiddle.liberty-development.net/ej9EGcn 看到它的运行情况。您是否向 XSLT 添加了更多模板?
  • 我试过但仍然面临问题,您只需在输入中将 ZZZZ 替换为 XXXX 并运行,您将得到不同的输出,而不是预期的输出,
  • 那么您的实际 XML 中能否有多个具有相同 NUMBERRESPONSE 元素?
  • 是的,我在实际 XML 中有多个具有相同 NUMBER 的 RESPONSE 元素
猜你喜欢
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 2018-07-22
  • 2011-02-15
  • 1970-01-01
  • 2013-08-31
  • 2020-08-16
  • 1970-01-01
相关资源
最近更新 更多