Объединить записи в XML с XSLT

Вот фрагмент из моего XML-файла, каждый продукт является отдельным <SHOPITEM> :

<?xml version="1.0" encoding="UTF-8"?>
<SHOP>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,00</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY><YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,99</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>red / green</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY><YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>

<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY><YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
</SHOP>

Можно группировать варианты продукта на одном и том же <PRODUCT>, как это:

<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>

<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>

<PRODUCT_VARIANT id="2">
<COLOR>red / green</COLOR>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,99</CODE>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<AVAILABLE>NO</AVAILABLE>
<NOTE>Available 15.2.2015</NOTE>
</PRODUCT_VARIANT>
<PRODUCT_VARIANT id="3">
<COLOR>black / white</COLOR>
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<AVAILABLE>NO</AVAILABLE>
<NOTE>Available 15.2.2015</NOTE>
</PRODUCT_VARIANT>
</SHOPITEM>

0

Решение

Примечание: это основано на предположении, что элементы магазина должны быть сгруппированы на основе тех же значений <PRODUCT> дочерний узел. В случае, если нужно сравнить другие значения узлов, добавьте это к вопросу.

Следующий XSLT

<?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" doctype-public="XSLT-compat"omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SHOPITEM[not(PRODUCT=preceding-sibling::SHOPITEM/PRODUCT)]">
<xsl:variable name="currentProduct" select="PRODUCT"/>
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="following-sibling::SHOPITEM[PRODUCT=$currentProduct]">
<xsl:apply-templates select="following-sibling::SHOPITEM
[PRODUCT=$currentProduct]" mode="variant"/>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:template match="SHOPITEM" mode="variant">
<xsl:variable name="currentProduct" select="PRODUCT"/>
<PRODUCT_VARIANT>
<xsl:attribute name="id">
<xsl:value-of select="count(preceding-sibling::SHOPITEM/PRODUCT
[.=$currentProduct]) + 1"/>
</xsl:attribute>
<xsl:apply-templates/>
</PRODUCT_VARIANT>
</xsl:template>
<xsl:template match="SHOPITEM[PRODUCT=preceding-sibling::SHOPITEM/PRODUCT]"/>
</xsl:transform>

при применении к вашему входному XML генерирует желаемый результат.

Шаблон

<xsl:template match="SHOPITEM[not(PRODUCT=preceding-sibling::SHOPITEM/PRODUCT)]">

копирует все SHOPITEM узлы, которые имеют PRODUCT дочерний узел, который не был дочерним по отношению к предыдущим элементам магазина. Если это SHOPITEM имеет следующий брат с тем же PRODUCT

<xsl:if test="following-sibling::SHOPITEM[PRODUCT=$currentProduct]">

они копируются как вариант с использованием

<xsl:template match="SHOPITEM" mode="variant">

Этот шаблон создает элемент <PRODUCT_VARIANT> и устанавливает в качестве атрибута id количество всех предыдущих продуктов с тем же значением, что и произведение текущего SHOPITEM + 1:

<xsl:value-of select="count(preceding-sibling::SHOPITEM/PRODUCT
[.=$currentProduct]) + 1"/>

Соответствие шаблону

<xsl:template match="SHOPITEM[PRODUCT=preceding-sibling::SHOPITEM/PRODUCT]"/>

пусто и удаляет SHOPITEM узлы, которые уже были записаны как варианты.

Обновить: На вопрос в комментарии можно ли добавить CODE как PRIMARY_CODE каждому варианту — следующий настроенный XSLT

 <?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" doctype-public="XSLT-compat"omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="SHOPITEM[not(PRODUCT=preceding-sibling::SHOPITEM/PRODUCT)]">
<xsl:variable name="currentProduct" select="PRODUCT"/>
<xsl:variable name="currentCode" select="CODE"/>
<xsl:copy>
<xsl:apply-templates/>
<xsl:if test="following-sibling::SHOPITEM[PRODUCT=$currentProduct]">
<xsl:apply-templates select="following-sibling::SHOPITEM
[PRODUCT=$currentProduct]" mode="variant">
<xsl:with-param name="code" select="$currentCode"/>
</xsl:apply-templates>
</xsl:if>
</xsl:copy>
</xsl:template>
<xsl:template match="SHOPITEM" mode="variant">
<xsl:param name="code"/>
<xsl:variable name="currentProduct" select="PRODUCT"/>
<PRODUCT_VARIANT>
<xsl:attribute name="id">
<xsl:value-of select="count(preceding-sibling::SHOPITEM/PRODUCT
[.=$currentProduct]) + 1"/>
</xsl:attribute>
<PRIMARY_CODE>
<xsl:value-of select="$code"/>
</PRIMARY_CODE>
<xsl:apply-templates/>
</PRODUCT_VARIANT>
</xsl:template>
<xsl:template match="SHOPITEM[PRODUCT=preceding-sibling::SHOPITEM/PRODUCT]"/>
</xsl:transform>

генерирует желаемый результат, только соответствующую часть:

<PRODUCT_VARIANT id="2">
<PRIMARY_CODE>032,00</PRIMARY_CODE>
<CATEGORY>Full</CATEGORY>
...

Корректировка просто установить <xsl:variable name="currentCode" select="CODE"/> в шаблоне соответствия SHOPITEM а затем применить шаблоны mode="variant" с currentCode в качестве параметра:

<xsl:apply-templates select="following-sibling::SHOPITEM
[PRODUCT=$currentProduct]" mode="variant">
<xsl:with-param name="code" select="$currentCode"/>
</xsl:apply-templates>

в <xsl:template match="SHOPITEM" mode="variant"> параметр добавляется как <xsl:param name="code"/> и просто написано как

<PRIMARY_CODE><xsl:value-of select="$code"/></PRIMARY_CODE>

после <PRODUCT_VARIANT>,

Для удобства я сохранил это здесь: http://xsltransform.net/bFDb2Cd

3

Другие решения

Вот еще один вариант, который использует xsl:key

Ввод XML

<SHOP>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,00</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,99</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>red / green</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL></URL>
<IMGURL1></IMGURL1>
<IMGURL2></IMGURL2>
<IMGURL3></IMGURL3>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</SHOPITEM>
</SHOP>

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="products" match="SHOPITEM" use="PRODUCT"/>

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

<xsl:template match="/SHOP">
<xsl:copy>
<xsl:for-each select="SHOPITEM[generate-id() = generate-id(key('products',PRODUCT)[1])]">
<SHOPITEM>
<xsl:for-each select="/*/SHOPITEM[key('products',PRODUCT)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</SHOPITEM>
</xsl:for-each>
</xsl:copy>
</xsl:template>

<xsl:template match="SHOPITEM[position()=1]">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>

<xsl:template match="SHOPITEM">
<PRODUCT_VARIANT>
<xsl:attribute name="id">
<xsl:number/>
</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</PRODUCT_VARIANT>
</xsl:template>

</xsl:stylesheet>

Вывод XML

<SHOP>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,00</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL/>
<IMGURL1/>
<IMGURL2/>
<IMGURL3/>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
<PRODUCT_VARIANT id="2">
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,99</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>red / green</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL/>
<IMGURL1/>
<IMGURL2/>
<IMGURL3/>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</PRODUCT_VARIANT>
<PRODUCT_VARIANT id="3">
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL/>
<IMGURL1/>
<IMGURL2/>
<IMGURL3/>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
</PRODUCT_VARIANT>
</SHOPITEM>
</SHOP>

Это отличается от желаемого результата тем, что содержит все исходные потомки SHOPITEM для вариантов. Вот модифицированная версия, которая сохраняет только те элементы, которые отличаются от первой SHOPITEM в группе:

XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="products" match="SHOPITEM" use="PRODUCT"/>

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

<xsl:template match="/SHOP">
<xsl:copy>
<xsl:for-each select="SHOPITEM[generate-id() = generate-id(key('products',PRODUCT)[1])]">
<SHOPITEM>
<xsl:for-each select="/*/SHOPITEM[key('products',PRODUCT)]">
<xsl:apply-templates select="."/>
</xsl:for-each>
</SHOPITEM>
</xsl:for-each>
</xsl:copy>
</xsl:template>

<xsl:template match="SHOPITEM[position()=1]">
<xsl:apply-templates select="@*|node()"/>
</xsl:template>

<xsl:template match="SHOPITEM">
<PRODUCT_VARIANT>
<xsl:attribute name="id">
<xsl:number/>
</xsl:attribute>
<xsl:apply-templates select="*" mode="variant"/>
</PRODUCT_VARIANT>
</xsl:template>

<xsl:template match="SHOPITEM/*" mode="variant">
<xsl:if test="not(key('products',../PRODUCT)[1]/*[name()=name(current())]=.)">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

Вывод XML

<SHOP>
<SHOPITEM>
<CATEGORY>Full</CATEGORY>
<WHEEL>27.5</WHEEL>
<FRAMESIZE>MD</FRAMESIZE>
<CODE>032,00</CODE>
<PRODUCT>POINT</PRODUCT>
<COLOR>black / white</COLOR>
<NOTE>Available 15.2.2015</NOTE>
<URL/>
<IMGURL1/>
<IMGURL2/>
<IMGURL3/>
<PRICE>3199.99</PRICE>
<CURRENCY>EUR</CURRENCY>
<YEAR>2015</YEAR>
<AVAILABLE>NO</AVAILABLE>
<PRODUCT_VARIANT id="2">
<CODE>032,99</CODE>
<COLOR>red / green</COLOR>
</PRODUCT_VARIANT>
<PRODUCT_VARIANT id="3">
<FRAMESIZE>LG</FRAMESIZE>
<CODE>032,01</CODE>
</PRODUCT_VARIANT>
</SHOPITEM>
</SHOP>
2

По вопросам рекламы [email protected]