Давайте начнем с исправления вашего исходного XML:
Должен быть только один корневой элемент (я назвал его Root ), и внутри него может бытьнесколько (например, Document ) элементов.
Шаблон, выполняющий преобразование, должен соответствовать элементу Root .
Как видно из ожидаемого результата,Вы хотите сгруппировать элементы Document по DocumentRef , поэтому в приведенном ниже скрипте есть соответствующая инструкция xsl: for-each-group .
Для каждой такой группы должен быть выходной элемент Document , а внутри него Ref элемент со значением текущего ключа группировки.
Тогда должен быть Элемент строк и внутри него для каждого члена текущей группы должен быть элемент Item , а внутри него 2 дочерних элемента с необходимыми значениями из исходного элемента.
Таким образом,Весь сценарий может выглядеть следующим образом:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="Root">
<xsl:copy>
<xsl:for-each-group select="Document" group-by="DocumentRef">
<Document>
<Ref><xsl:value-of select="current-grouping-key()"/></Ref>
<Lines>
<xsl:for-each select="current-group()">
<Item>
<ItemCode><xsl:value-of select="DocumentLines/ItemCode"/></ItemCode>
<Qty><xsl:value-of select="DocumentLines/ItemQty"/></Qty>
</Item>
</xsl:for-each>
</Lines>
</Document>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
Рабочий пример, включая исправленный ввод, см. http://xsltransform.net/eieE3PX
Версия XSLT 1.0
В XSLT 1.0 это также возможно, используя Muenchian Grouping :
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:key name="groups" match="row" use="OrderRef"/>
<xsl:template match="Payload">
<xsl:copy>
<xsl:apply-templates select="row[generate-id() = generate-id(
key('groups', OrderRef)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="row">
<Document>
<Ref><xsl:value-of select="OrderRef"/></Ref>
<CardCode><xsl:value-of select="CustomerCode"/></CardCode>
<Lines>
<xsl:for-each select="key('groups', OrderRef)">
<Item>
<ItemCode><xsl:value-of select="ItemCode"/></ItemCode>
<Qty><xsl:value-of select="Quantity"/></Qty>
</Item>
</xsl:for-each>
</Lines>
</Document>
</xsl:template>
</xsl:transform>
Первым шагом является создание xsl:key
.Каждый ключ должен иметь name
, чтобы ссылаться на него позже.match
определяет, какие элементы включить в этот ключ , а use
определяет ключ группировки.
Затем посмотрите:
<xsl:apply-templates select="row[generate-id() = generate-id(
key('groups', OrderRef)[1])]"/>
Это "вызывает действие"(в данном случае xsl:apply-templates
) для первого объекта в каждой группе.
Остальная часть кода из моего исходного решения была перемещена в шаблон, соответствующий строка .
Начальная часть выполняет действия для текущей группы (генерирует выходные данные Document , Ref , CardCode и Lines elements).
Остальные (xsl:for-each
) выполняют действия для отдельных членов текущей группы, генерируя элементы Item , ItemCode и Кол-во .
Я обновил ваше решение в xsltransform , поэтому вы можете просмотреть его на http://xsltransform.net/jxWYjW2/2
Обратите внимание, что я изменил XSLT-движок на Saxon 6.5.5 .Вы также можете переключить его на Xalan , хотя тогда вы потеряете отступ.
Если этот подход для вас новый, возможно, вам следует прочитать немного о generate-id и мюнхенская группировка сама.Даже StackOverflow содержит много сообщений об этих проблемах.