-2

I have an xml file:

<PRODUCTS>
  <PRODUCT>
    <NAME_EN>jacket</NAME_EN>
    <SKU>1</SKU>
    <SIZE>
      <CODE>01 </CODE>
      <DESCRIPTION>34</DESCRIPTION>
    </SIZE>
    <COLOR>
      <DESCRIPTION_EN>black</DESCRIPTION_EN>
    </COLOR>
  </PRODUCT>
  <PRODUCT>
    <NAME_EN>jacket</NAME_EN>
    <SKU>2</SKU>
    <SIZE>
      <CODE>02</CODE>
      <DESCRIPTION>35</DESCRIPTION>
    </SIZE>
    <COLOR>
      <DESCRIPTION_EN>black</DESCRIPTION_EN>
    </COLOR>
  </PRODUCT>
  <PRODUCT>
    <NAME_EN>shoes</NAME_EN>
    <SKU>3</SKU>
    <SIZE>
      <CODE>01</CODE>
      <DESCRIPTION>34</DESCRIPTION>
    </SIZE>
    <COLOR>
      <DESCRIPTION_EN>black</DESCRIPTION_EN>
    </COLOR>
  </PRODUCT>
</PRODUCTS>

I want to use xsl transformation to achieve such result:

<catalog>
  <product>
    <variants>
      <variant>
        <sku>1</sku>        
        <options>
          <option>
            <code>size</code>
            <value>34</value>
          </option>
        </options>
      </variant>
      <variant>
        <sku>2</sku>        
        <options>
          <option>
            <code>size</code>
            <value>35</value>
          </option>
        </options>
      </variant>
    </variants>
  </product>
  <product>
    <variants>
      <variant>
        <sku>3</sku>        
        <options>
          <option>
            <code>size</code>
            <value>34</value>
          </option>
        </options>
      </variant>
    </variants>
  </product>
</catalog>

What I need is to for-each over all nodes where NAME_EN and COLOR/DESCRIPTION_EN where name the same.

I know about such for-each:

<xsl:for-each select="PRODUCTS/PRODUCT[NAME_EN='jacket']">

But it will not fork for me. Is there any way to achieve this?

Viacheslav Kondratiuk
  • 8,493
  • 9
  • 49
  • 81
  • I don't understand your 'where' clauses. You want the nodes where NAME_EN == COLOR/DESCRIPTION_EN ? – Efrain Apr 11 '13 at 10:04
  • No, one node set where NAME_EN == jacket and COLOR/DESCRIPTION_EN == blue, next one NAME_EN == jacket and COLOR/DESCRIPTION_EN == white, next one NAME_EN == shoes and COLOR/DESCRIPTION_EN == green – Viacheslav Kondratiuk Apr 11 '13 at 10:06
  • 1
    Hm. Check out , I think that will work for you. – Efrain Apr 11 '13 at 10:16

3 Answers3

1

Here is a solution using XSLT 1.0 and the Muenchian Method to group the products by the value of the NAME_EN field.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:key name="product-by-name" match="PRODUCT" use="NAME_EN"/>

    <xsl:template match="/PRODUCTS">
        <products>
            <xsl:apply-templates select="PRODUCT"/>
        </products>
    </xsl:template>

    <xsl:template match="PRODUCT">
        <xsl:variable name="name-group" select="key('product-by-name', NAME_EN)"/>
        <xsl:if test="generate-id() = generate-id($name-group[1])">
            <product>
                <variants>
                    <xsl:for-each select="$name-group">
                        <variant>
                            <xsl:apply-templates select="SKU"/>
                            <options>
                                <xsl:apply-templates select="SIZE"/>
                            </options>
                        </variant>
                    </xsl:for-each>
                </variants>
            </product>
        </xsl:if>
    </xsl:template>

    <xsl:template match="SKU">
        <sku><xsl:value-of select="."/></sku>
    </xsl:template>

    <xsl:template match="SIZE">
        <option>
            <code>size</code>
            <value><xsl:value-of select="DESCRIPTION"/></value>
        </option>
    </xsl:template>

</xsl:stylesheet>

output

<products>
   <product>
      <variants>
         <variant>
            <sku>1</sku>
            <options>
               <option>
                  <code>size</code>
                  <value>34</value>
               </option>
            </options>
         </variant>
         <variant>
            <sku>2</sku>
            <options>
               <option>
                  <code>size</code>
                  <value>35</value>
               </option>
            </options>
         </variant>
      </variants>
   </product>
   <product>
      <variants>
         <variant>
            <sku>3</sku>
            <options>
               <option>
                  <code>size</code>
                  <value>34</value>
               </option>
            </options>
         </variant>
      </variants>
   </product>
</products>
Borodin
  • 126,100
  • 9
  • 70
  • 144
0

I think @Efrain is right,what your are locking for is grouping. <xsl:for-each-group> only works with xslt 2.0. For more info have a look to e.g.: how to apply group by on xslt elements

Community
  • 1
  • 1
hr_117
  • 9,589
  • 1
  • 18
  • 23
0

Here is the complete solution:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

  <xsl:template match="NAME_EN | DESCRIPTION_EN"/>

  <xsl:template match="/">
    <catalog>
      <xsl:call-template name="product"/>
    </catalog>
  </xsl:template>


  <xsl:template name="product">
    <xsl:for-each-group select="//NAME_EN" group-by="text()">
      <xsl:for-each select="current-grouping-key()">
        <product>
          <variants>
            <xsl:for-each select="current-group()/parent::PRODUCT">
              <variant>
                <xsl:apply-templates/>
              </variant>
            </xsl:for-each>
          </variants>
        </product>
      </xsl:for-each>
    </xsl:for-each-group>
  </xsl:template>

  <xsl:template match="SKU">
    <sku>
      <xsl:apply-templates/>
    </sku>
  </xsl:template>

  <xsl:template match="SIZE">
    <options>
      <option>
        <xsl:apply-templates/>
      </option>
    </options>
  </xsl:template>

  <xsl:template match="CODE">
    <code>
      <xsl:apply-templates/>
    </code>
  </xsl:template>

  <xsl:template match="DESCRIPTION">
    <value>
      <xsl:apply-templates/>
    </value>
  </xsl:template>
</xsl:stylesheet>
Navin Rawat
  • 3,208
  • 1
  • 19
  • 31