使用 XSLT 合并 XML 中的条目

Merge entries in XML with 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>

注意:这是基于这样的假设,即应根据 <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> 并将与当前 SHOPITEM + 1 的产品具有相同值的所有前面产品的计数设置为属性 id

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

调整只是在模板匹配SHOPITEM中设置<xsl:variable name="currentCode" select="CODE"/>,然后应用模板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>