如何在XSL for-each中选择具有相同名称的节点?

时间:2013-04-11 10:00:35

标签: xml xslt xpath

我有一个xml文件:

<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>

我想使用xsl转换来实现这样的结果:

<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>

我需要的是 - NAME_ENCOLOR/DESCRIPTION_EN名称相同的所有节点上的每个节点。

我知道这样的每一个:

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

但它不会为我而烦恼。 有没有办法实现这个目标?

3 个答案:

答案 0 :(得分:1)

以下是使用XSLT 1.0和Muenchian方法按照NAME_EN字段的值对产品进行分组的解决方案。

<?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>

<强>输出

<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>

答案 1 :(得分:0)

我认为@Efrain是正确的,你所锁定的是分组。 &LT;的xsl:for-每个-组&gt;仅适用于xslt 2.0。 有关更多信息,请查看例如:how to apply group by on xslt elements

答案 2 :(得分:0)

以下是完整的解决方案:

<?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>