xslt - 我应该如何在 xslt 中对数据进行分组?
问题描述
我需要按时期对历史进行分组,但我无法执行此分组。有人可以在这里帮忙吗?我尝试使用 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>
解决方案
主要问题是您需要考虑NUMBER
密钥中的 ,否则您将在整个文档中对所有匹配年份进行分组
<xsl:key name="years" match="Period" use="concat(../../NUMBER, '|', @Year)"/>
此外,对于您的第一个xsl:for-each
,您使用 开始选择表达式/TEST/RESPONSE/HISTORY/Period
,当您确实需要它与 current 相关时,它还将检查文档中的所有 Periods 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>
请注意:
- 您不需要在 中指定匹配元素的完整路径
xsl:key
(除非您有其他具有相同名称但您不想匹配的不同路径的元素)。 - 您的模板匹配
TEST
是不必要的,因为身份模板会做同样的事情。 - 您不需要使用
xsl:element
元素名称为静态的位置。直接写出元素标签即可。 - 您可以使用属性值模板来简化属性的创建。
推荐阅读
- python - Tensorflow 2.0 - 我可以使用 `tf.keras` 从 Boosted Tree 模型中获取类概率吗?
- go - 带有 Channels 的 Golang 内存参考
- c# - ASP.NET WebForms Authentication Logout, Cookie 仍然可以用来访问站点
- revit-api - 元素选择更改事件 - 实施斗争
- javascript - 为什么将这个函数放在 Promise 中时会中断?
- python - 获取一个 25x2 数组,如下所示(带有坐标的排序列表)
- sql - BigQuery SQL 语法错误 FOR 函数(“语法错误:预期的”)“但在 [7:18] 获得关键字 FOR”)
- angular - 使用 bootstrap-select 和 angular 为选择生成项目
- python - numpy 随机采样布尔数组
- cytoscape.js - 指定样式以及节点的数据时,“不推荐在创建元素时设置 `style` 绕过”