Вот фрагмент из моего 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>
Примечание: это основано на предположении, что элементы магазина должны быть сгруппированы на основе тех же значений <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
Вот еще один вариант, который использует 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>