#xslt #xslt-1.0 #xslt-grouping
#xslt #xslt-1.0 #xslt-группировка
Вопрос:
Мне нужно сгруппировать историю по периодам, но я не могу выполнить эту группировку. может кто-нибудь, пожалуйста, помочь здесь. я пробовал с ключом xsl, но он выполняет операцию только для 1-го ответа. не могли бы вы, пожалуйста, предложить какой-либо другой подход. существует ли какой-либо подход для группировки, как показано в ожидаемом выводе ниже.
Ввод
<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>
Комментарии:
1. Не могли бы вы, пожалуйста, опубликовать свой XSLT, чтобы мы могли исправить любую проблему, которая в нем есть? Если вы используете
xsl:key
, то вы, возможно, на самом деле не являетесь этим для решения. Спасибо!2. вы можете найти xslt выше
Ответ №1:
Основная проблема заключается в том, что вам необходимо учитывать NUMBER
в вашем ключе, иначе вы сгруппируете все совпадающие годы по всему вашему документу
<xsl:key name="years" match="Period" use="concat(../../NUMBER, '|', @Year)"/>
Кроме того, для вашего первого xsl:for-each
, вы начинаете выражение select с /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>
Обратите внимание:
- Вам не нужно указывать полный путь к соответствующему элементу в
xsl:key
(если только у вас нет других элементов с таким же именем, но разными путями, которые вы не хотите сопоставлять). - Ваше сопоставление с шаблоном
TEST
было ненужным, поскольку шаблон идентификации делал бы то же самое. - Вам не нужно использовать,
xsl:element
где имя элемента является статическим. Просто выпишите тег элемента напрямую. - Вы можете использовать шаблоны значений атрибутов для упрощения создания атрибутов.
Комментарии:
1. Привет, Тим, спасибо за твой вклад, но в некоторых случаях я получаю ЧИСЛО в виде дубликатов, тогда как мне с этим справиться
2. Вы можете увидеть это в действии на xsltfiddle. liberty-development.net/ej9EGcn . Вы добавили еще какие-нибудь шаблоны в XSLT?
3. я пытался, но все еще сталкиваюсь с проблемой, вы просто заменяете ZZZZ на XXXX во входных данных и запускаете, вы получите другой результат, а не ожидаемый,
4. Тогда у вас может быть несколько
RESPONSE
элементов с одинаковымиNUMBER
значениями в вашем реальном XML?5. да, у меня есть несколько элементов ОТВЕТА с одинаковым номером в реальном XML