使用XSLT合并XML条目

时间:2015-01-15 22:13:55

标签: php xml 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>

2 个答案:

答案 0 :(得分:3)

注意:这是基于假设应该基于<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个节点,这些子节点不是前一个shopitems的子节点。如果此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

答案 1 :(得分:2)

这是另一个使用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>