xslt как объединить узлы продукта

#xml #xslt-1.0

#xml #xslt-1.0

Вопрос:

мне нужно объединить продукты магазина, размер которых указан в названии. Как вы можете видеть, я должен вырезать размер из названия продукта и поместить его в новый элемент внутри группы-элемент «РАЗМЕРЫ», то же самое с PARTNO и STOCK, которые необходимы для определения продукта, его размеров и количества. Я создал некоторый xslt с шаблоном и циклом foreach, но я не знаю, как проверить, был ли создан какой-либо узел ранее.

Это очень важно для меня, потому что xslt — это моя новая возможность в моей работе, я новичок в xslt, поэтому мой код очень простой и уродливый.

вот пример xml:

     <?xml version="1.0" encoding="Windows-1250"?>
    <SHOP xmlns="http://www.b2b.abisaltest.pl">
       <SHOPITEM>
          <PRODUCT>ZBRS180 CZERWONA BRAMKA PIŁKARSKA</PRODUCT>
          <PRICE>167.01</PRICE>
          <PARTNO>5907695527654</PARTNO>
          <STOCK>177</STOCK>
       </SHOPITEM>
       <SHOPITEM>
          <PRODUCT>NJ2117 A NIEBIESKO-RÓŻOWE SIZE. M (34-37)  ŁYŻWOROLKI NILS 
           EXTREME</PRODUCT>
          <PRICE>113.01</PRICE>
          <PARTNO>5907695510333</PARTNO>
          <STOCK>158</STOCK>
       </SHOPITEM>
       <SHOPITEM>
          <PRODUCT>NJ2117 A NIEBIESKO-RÓŻOWE SIZE. L (38-41)  ŁYŻWOROLKI NILS 
          EXTREME</PRODUCT>
          <PRICE>113.01</PRICE>
          <PARTNO>5907695510334</PARTNO>
          <STOCK>35</STOCK>
       </SHOPITEM>
    </SHOP>

**Output file should looks like that:**

    <?xml version="1.0" encoding="Windows-1250"?>
    <SHOP>
       <SHOPITEM>
          <PRODUCT>ZBRS180 CZERWONA BRAMKA PIŁKARSKA</PRODUCT>
          <PRICE>167.01</PRICE>
          <PARTNO>5907695527654</PARTNO>
          <STOCK>177</STOCK>
       </SHOPITEM>
       <SHOPITEM>
          <PRODUCT>NJ2117 A NIEBIESKO-RÓŻOWE ŁYŻWOROLKI NILS EXTREME</PRODUCT>
          <SIZES>
             <SIZE>M (34-37)</SIZE>
             <SIZE>L (38-41)</SIZE>
          </SIZES>
          <PARTNOS>
            <PARTNO>5907695510333</PARTNO>
            <PARTNO>5907695510334</PARTNO>
          </PARTNOS>
          <STOCKS>
            <STOCK>158</STOCK>
            <STOCK>35</STOCK>
          </STOCKS>
          <PRICE>113.01</PRICE>
       </SHOPITEM>
</SHOP>
  

Вот мой xsl-код:

 <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abi="http://www.b2b.abisaltest.pl">
<xsl:output method="xml" indent="yes" version="1.0" encoding="iso-8859-2"/>


      <xsl:template match="abi:SHOP">
        <xsl:element name="SHOP">
        <xsl:apply-templates select="abi:SHOPITEM">
          <xsl:with-param name="pNodeset" select="abi:SHOPITEM"/>
        </xsl:apply-templates>
        </xsl:element>
      </xsl:template>




<xsl:template match="abi:SHOPITEM">
    <xsl:param name="pNodeset" />
    <xsl:element name="SHOPITEM" >
        <xsl:choose>
            <xsl:when test="contains(abi:PRODUCT/text(), 'SIZE.')">
                <!-- Creating variable with node name -->
                <xsl:variable name="currentCuttedNodeName">
                    <xsl:value-of select="substring-before(abi:PRODUCT/text(), 'SIZE.')"/>
                </xsl:variable>
                <!-- Looking for SIZES -->
                <xsl:element name="SIZES">
                    <xsl:for-each select="$pNodeset">
                        <xsl:if test="contains(abi:PRODUCT/text(), $currentCuttedNodeName)">
                            <xsl:element name="SIZE">
                                <xsl:choose>
                                    <xsl:when test="contains(abi:PRODUCT/text(),')')">
                                        <xsl:variable name="from">
                                            <xsl:value-of select="string-length(substring-before(abi:PRODUCT/text(), '(')) 1"/>
                                        </xsl:variable>
                                        <xsl:variable name="to">
                                            <xsl:value-of select="string-length(substring-before(abi:PRODUCT/text(), ')'))"/>
                                        </xsl:variable>
                                        <xsl:value-of select="substring(abi:PRODUCT/text(),$from,7)"/>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:variable name="from">
                                            <xsl:value-of select="string-length(substring-before(abi:PRODUCT/text(), 'SIZE.'))"/>
                                        </xsl:variable>
                                        <xsl:value-of select="substring(abi:PRODUCT/text(),$from 7, 2)"/>
                                    </xsl:otherwise>
                                </xsl:choose>
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
                <!-- Looking for EANS -->
                <xsl:element name="EANS">
                    <xsl:for-each select="$pNodeset">
                        <xsl:if test="contains(abi:PRODUCT/text(), $currentCuttedNodeName)">
                            <xsl:element name="EAN">
                                <xsl:value-of select="abi:PARTNO/text()"/>
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
                <!-- Looking for external codes -->
                <xsl:element name="CODES">
                    <xsl:for-each select="$pNodeset">
                        <xsl:if test="contains(abi:PRODUCT/text(), $currentCuttedNodeName)">
                            <xsl:element name="CODE">
                                <xsl:value-of select="abi:PRODUCTNO/text()"/>
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
                <!-- Looking for stocks -->
                <xsl:element name="STOCKS">
                    <xsl:for-each select="$pNodeset">
                        <xsl:if test="contains(abi:PRODUCT/text(), $currentCuttedNodeName)">
                            <xsl:element name="STOCK">
                                <xsl:value-of select="abi:STOCK/text()"/>
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
                <xsl:element name="NAME">
                                <xsl:choose>
                                    <xsl:when test="contains(abi:PRODUCT/text(),')')">
                                        <xsl:variable name="from">
                                            <xsl:value-of select="string-length(substring-before(abi:PRODUCT/text(), 'SIZE.'))"/>
                                        </xsl:variable>
                                        <xsl:variable name="to">
                                            <xsl:value-of select="string-length(substring-before(abi:PRODUCT/text(), ')'))"/>
                                        </xsl:variable>
                                        <xsl:variable name="firstPart">
                                            <xsl:value-of select="substring-before(abi:PRODUCT/text(),'SIZE.')"/>
                                        </xsl:variable>
                                        <xsl:variable name="secondPart">
                                            <xsl:value-of select="substring-after(abi:PRODUCT/text(),')')"/>
                                        </xsl:variable>
                                        <xsl:value-of select="concat($firstPart,$secondPart)"/>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:variable name="firstPart">
                                            <xsl:value-of select="substring-before(abi:PRODUCT/text(), 'SIZE.')"/>
                                        </xsl:variable>
                                        <xsl:variable name="fromNumber">
                                            <xsl:value-of select="string-length(substring-after(abi:PRODUCT/text(), 'SIZE.'))  10"/>
                                        </xsl:variable>
                                        <xsl:variable name="secondPart">
                                            <xsl:value-of select="substring(abi:PRODUCT/text(), $fromNumber, string-length(abi:PRODUCT/text()) - $fromNumber)"/>
                                        </xsl:variable>
                                        <xsl:value-of select="concat($firstPart, $secondPart)"/>
                                    </xsl:otherwise>
                                </xsl:choose>
                </xsl:element>
                <xsl:element name="ID">
                    <xsl:value-of select="position()"/>
                </xsl:element>
                <xsl:element name="DESC">
                    <xsl:value-of select="abi:DESCRIPTION/text()"/>
                </xsl:element>
                <xsl:element name="IMGURL">
                    <xsl:value-of select="abi:IMGURL"/>
                </xsl:element>
                <xsl:element name="BRAND">
                    <xsl:value-of select="abi:BRAND"/>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <!-- Filling elements to no size item -->
                <xsl:element name="SIZES">
                    <xsl:element name="SIZE">
                        <xsl:value-of select="'Uniwersalny'"/>
                    </xsl:element>
                </xsl:element>
                <xsl:element name="EANS">
                    <xsl:element name="EAN">
                        <xsl:value-of select="abi:PARTNO/text()"/>
                    </xsl:element>
                </xsl:element>
                <xsl:element name="CODES">
                    <xsl:element name="CODE">
                        <xsl:value-of select="abi:PRODUCTNO/text()"/>
                    </xsl:element>
                </xsl:element>
                <xsl:element name="STOCKS">
                    <xsl:element name="STOCK">
                        <xsl:value-of select="abi:STOCK/text()"/>
                    </xsl:element>
                </xsl:element>
                <xsl:element name="NAME">
                    <xsl:value-of select="abi:PRODUCT/text()"/>
                </xsl:element>
                <xsl:element name="ID">
                    <xsl:value-of select="position()"/>
                </xsl:element>
                <xsl:element name="DESC">
                    <xsl:value-of select="abi:DESCRIPTION/text()"/>
                </xsl:element>
                <xsl:element name="IMGURL">
                    <xsl:value-of select="abi:IMGURL"/>
                </xsl:element>
                <xsl:element name="BRAND">
                    <xsl:value-of select="abi:BRAND"/>
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
      </xsl:element>
</xsl:template>

</xsl:transform>
  

Комментарии:

1. Начните отсюда: jenitennison.com/xslt/grouping/muenchian.html — P.S. Правильно ли было бы предположить, что первое слово в PRODUCT является уникальным идентификатором продукта?

2. Да, первое слово PRODUCT имеет только уникальное значение.

Ответ №1:

Вы можете попробовать это:

 <xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:abi="http://www.b2b.abisaltest.pl"
exclude-result-prefixes="abi">
<xsl:output method="xml" indent="yes" version="1.0" encoding="iso-8859-2"/>

<xsl:key name="qualifierKey" match="/abi:SHOP/abi:SHOPITEM" use="concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')'))" />

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

<xsl:template match="abi:SHOP">
    <xsl:element name="SHOP">
        <xsl:for-each select="abi:SHOPITEM[generate-id(.) = generate-id(key('qualifierKey', concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')')))[1])]">
            <xsl:apply-templates select="." />
        </xsl:for-each>
    </xsl:element>
</xsl:template>

<xsl:template match="abi:SHOPITEM">
    <SHOPITEM>
        <xsl:choose>
            <xsl:when test="contains(abi:PRODUCT, 'SIZE.')">
                <xsl:element name="PRODUCT">
                    <xsl:value-of select="concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')'))" />
                </xsl:element>      

                <!-- Looking for SIZES -->
                <xsl:element name="SIZES">
                    <xsl:for-each select="key('qualifierKey', concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')')))">
                        <xsl:element name="SIZE">
                            <xsl:value-of select="normalize-space(concat(substring-after(substring-before(abi:PRODUCT,')'),'SIZE.'), ')'))" />
                        </xsl:element>
                    </xsl:for-each>
                </xsl:element>

                <!-- Looking for EANS -->
                <xsl:element name="PARTNOS">
                    <xsl:for-each select="key('qualifierKey', concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')')))">
                        <xsl:if test="abi:PARTNO != ''">
                            <xsl:element name="PARTNO">
                                <xsl:value-of select="abi:PARTNO/text()" />
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>

                <!-- Looking for stocks -->
                <xsl:element name="STOCKS">
                    <xsl:for-each select="key('qualifierKey', concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')')))">
                        <xsl:if test="abi:STOCK != ''">
                            <xsl:element name="STOCK">
                                <xsl:value-of select="abi:STOCK/text()" />
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>

                <!-- Looking for external codes -->
                <xsl:element name="CODES">
                    <xsl:for-each select="key('qualifierKey', concat(substring-before(abi:PRODUCT,'SIZE.'),substring-after(abi:PRODUCT,')')))">
                        <xsl:if test="abi:PRODUCTNO != ''">
                            <xsl:element name="CODE">
                                <xsl:value-of select="abi:PRODUCTNO/text()" />
                            </xsl:element>
                        </xsl:if>
                    </xsl:for-each>
                </xsl:element>
                <xsl:element name="PRICE">
                    <xsl:value-of select="abi:PRICE" />
                </xsl:element>
                <xsl:element name="ID">
                    <xsl:value-of select="position()" />
                </xsl:element>
                <xsl:element name="DESC">
                    <xsl:value-of select="abi:DESCRIPTION/text()" />
                </xsl:element>
                <xsl:element name="IMGURL">
                    <xsl:value-of select="abi:IMGURL" />
                </xsl:element>
                <xsl:element name="BRAND">
                    <xsl:value-of select="abi:BRAND" />
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="PRODUCT">
                    <xsl:value-of select="abi:PRODUCT/text()" />
                </xsl:element>
                <xsl:element name="PRICE">
                    <xsl:value-of select="abi:PRICE" />
                </xsl:element>
                <xsl:element name="PARTNO">
                    <xsl:value-of select="abi:PARTNO/text()" />
                </xsl:element>
                <xsl:element name="STOCK">
                    <xsl:value-of select="abi:STOCK/text()" />
                </xsl:element>
                <xsl:element name="SIZE">
                    <xsl:value-of select="'Uniwersalny'" />
                </xsl:element>
                <xsl:element name="CODE">
                    <xsl:value-of select="abi:PRODUCTNO/text()" />
                </xsl:element>
                <xsl:element name="ID">
                    <xsl:value-of select="position()" />
                </xsl:element>
                <xsl:element name="DESC">
                    <xsl:value-of select="abi:DESCRIPTION/text()" />
                </xsl:element>
                <xsl:element name="IMGURL">
                    <xsl:value-of select="abi:IMGURL" />
                </xsl:element>
                <xsl:element name="BRAND">
                    <xsl:value-of select="abi:BRAND" />
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </SHOPITEM>
</xsl:template>

</xsl:transform>
  

http://xsltfiddle .liberty-development.net/gWvjQfg

Ответ №2:

Это не совсем просто — но я думаю, что это могло бы быть намного проще, чем то, что вы пытаетесь сделать:

XSLT 1.0

 <xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:abi="http://www.b2b.abisaltest.pl"
exclude-result-prefixes="abi">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="item" match="abi:SHOPITEM" use="substring-before(abi:PRODUCT, ' ')" />

<xsl:template match="/abi:SHOP">
    <SHOP>
        <!-- MUENCHIAN GROUPING -->
        <xsl:for-each select="abi:SHOPITEM[count(. | key('item', substring-before(abi:PRODUCT, ' '))[1]) = 1]">
            <xsl:variable name="current-group" select="key('item', substring-before(abi:PRODUCT, ' '))" />
            <xsl:choose>
                <xsl:when test="count($current-group) > 1">
                    <SHOPITEM>
                        <PRODUCT>
                            <xsl:value-of select="substring-before(abi:PRODUCT, 'SIZE.')"/>
                            <xsl:value-of select="substring-after(substring-after(abi:PRODUCT, 'SIZE. '), ')')"/>                                                   
                        </PRODUCT>
                        <SIZES>
                            <xsl:for-each select="$current-group">
                                <SIZE>
                                    <xsl:value-of select="substring-before(substring-after(abi:PRODUCT, 'SIZE. '), ')')"/>
                                    <xsl:text>)</xsl:text>
                                </SIZE>
                            </xsl:for-each>
                        </SIZES>
                        <PARTNOS>
                            <xsl:apply-templates select="$current-group/abi:PARTNO"/>
                        </PARTNOS>
                        <STOCKS>
                            <xsl:apply-templates select="$current-group/abi:STOCK"/>
                        </STOCKS>
                     </SHOPITEM>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="$current-group"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>
    </SHOP>
</xsl:template>

<xsl:template match="abi:*">
    <xsl:element name="{local-name()}">
        <xsl:apply-templates/>
     </xsl:element>
</xsl:template>

</xsl:stylesheet>