基于拆分值的不同列表

时间:2013-08-14 07:01:47

标签: list templates xslt split unique

首先:我是XSL菜鸟。完全!在我的例子中,那些愚蠢的少量XSL花了我几天的时间。所以请光临我。

我的最终目标是拥有一个可以迭代的唯一ID列表。这是:

我有一个产品XML(Items)。在完整的XML中,将有+200,000个项目。在这个例子中有两个:

<?xml version="1.0" encoding="utf-8"?>
<Export Shop="Demo Webshop" Type="Full" Clean="true" CleanIsolationShopID="SHOP1">
<Items>
    <Item ItemNo="1001" ShopID="SHOP1" VariantCode="1616_42.1615_01.ct_HD">
    </Item>
    <Item ItemNo="1001" ShopID="SHOP1" VariantCode="1616_42.1615_02.ct_HD" >
    </Item>
</Items>

我需要拆分属性VariantCode的内容。对于应该给我1616_42和1615_01以及ct_HD的第一个项目。最终结果是将其导入到具有复合主键ItemNo + VariantOption的表中(VariantOption是拆分值)。

XSLT还具有:

<table tableName="EcomVariantOptionsProductRelation">
<xsl:for-each select="Export/Items/Item">
    <xsl:call-template name="split">
    <xsl:with-param name="pText" select="@VariantCode"/>
    <xsl:with-param name="ProductID" select="concat(@ItemNo,'@@',@ShopID)"/>
    /xsl:call-template>
</xsl:for-each>

正在调用的模板执行实际拆分:

    <xsl:template match="text()" name="split">
    <xsl:param name="pText" select="."/>
    <xsl:param name= "ProductID" select="." />
    <xsl:choose>
        <xsl:when test="string-length($pText) > 0">
            <xsl:choose>
                <xsl:when test="contains($pText, '.')">
                    <!-- has dot (more than one variantOption) -->
                    <item tableName="EcomVariantOptionsProductRelation">
                        <column columnName="VariantOptionsProductRelationVariantID">
                            <xsl:value-of select="substring-before($pText,'.')"/>
                        </column>
                        <column columnName="VariantOptionsProductRelationProductID">
                            <xsl:value-of select="$ProductID"/>
                        </column>
                    </item>
                </xsl:when>
                <xsl:otherwise>
                    <item tableName="EcomVariantOptionsProductRelation">
                        <column columnName="VariantOptionsProductRelationVariantID">
                            <xsl:value-of select="$pText"/>
                        </column>
                        <column columnName="VariantOptionsProductRelationProductID">
                            <xsl:value-of select="$ProductID"/>
                        </column>
                    </item>
                </xsl:otherwise>
            </xsl:choose>
            <xsl:call-template name="split">
                <xsl:with-param name="pText" select="substring-after($pText, '.')"/>
                    <xsl:with-param name="ProductID" select="$ProductID"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <!-- empty string (no variants) -->
            <xsl:value-of select="$pText"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

问题在于转换后的输出,即

        <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[1616_42]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
重复

,因为“1616_42”(和“ct_HD”)部分在两个不同的项目中存在两次。我需要输出是唯一的,因为它最终会转到一个表,其中这个复合键(VariantID + ProductID)是唯一的。

两者的理想结果应该是:

    <table tableName="EcomVariantOptionsProductRelation">
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[1616_42]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[1615_01]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[ct_HD]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[1615_02]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[1616_50]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
    <item tableName="EcomVariantOptionsProductRelation">
        <column columnName="VariantOptionsProductRelationVariantID"><![CDATA[ct_NHD]]></column>
        <column columnName="VariantOptionsProductRelationProductID"><![CDATA[1001@@SHOP1]]></column>
    </item>
</table>

指出:没有重复。

搜索网络我可以看到创建带有某种唯一标识符的列表的可能性。但是我不知道在我的场景中是否可能,即使它是,也不知道如何实现。

想法?使用XSLT 1.0。

1 个答案:

答案 0 :(得分:0)

我能想到的唯一方法(在XSLT 1.0中)是通过“两次通过”转换。实际上,您执行两次转换(尽管这可以在单个样式表中完成,我将演示)。第一个转换会将当前的 VariantCode 属性拆分为单独的元素,因此结果就像这样

 <Item ProductId="1001@@SHOP1"> 
    <Variant>1616_42</Variant>
    <Variant>1615_01</Variant>
    <Variant>ct_HD</Variant>
</Item>

然后第二个转换可以使用一种名为Muenchian Grouping的技术来输出您需要的不同Variant元素。

为此,第一次变换的结果只存储在变量

<xsl:variable name="variantSplit">
  <xsl:apply-templates select="//Item" />
</xsl:variable>

因此,在这种情况下,您将拥有一个匹配的模板来执行所需的复制和拆分:

 <xsl:template match="Item">
    <Item ProductID="{@ItemNo}@@{@ShopID}">
       <xsl:call-template name="VariantCodeSplit" />
    </Item>
 </xsl:template>

(如果您之前没有看过它们,ProductID属性中的花括号是“属性值模板”,并指示要评估的表达式,而不是字面输出。)

现在,您已在变量中转换了XML,其中每个 Item 元素都有多个子 Variant 元素,如上所示。

但是等等!这是XSLT 1.0,这意味着变量上的内容实际上是“结果树片段”。如果要开始在其上应用模板,则需要使用扩展函数将其转换为节点集。这取决于您使用的处理器,但您几乎肯定可以使用节点设置功能。这只是声明正确命名空间的情况。 (有关详细信息,请参阅http://www.xml.com/pub/a/2003/07/16/nodeset.html。)

无论如何,下一阶段涉及Muenchian分组技术。这涉及通过ProductId和(拆分)变体代码

的组合来定义匹配新 Variant 元素的键。
<xsl:key name="Test" match="Variant" use="concat(../@ProductID, '|', .)" />

然后,要获得不同的 Variant 元素,您需要查找 xsl:key 中首先出现的元素,以获取其给定的ProductID和代码组合

<xsl:apply-templates select="msxml:node-set($variantSplit)/Item/Variant
     [generate-id() = generate-id(key('Test', concat(../@ProductID, '|', .))[1])]" />

(注意在这里使用 node-set 扩展功能。就我而言,我使用的是Microsoft)。

然后,您可以拥有一个与 Variant 元素匹配的模板,并且您知道每个匹配都是一个明显的匹配项,因此您可以输出产品ID和代码。

尝试将此XSLT作为入门者。请注意,它没有为您提供示例中使用的元素和属性名称(为简洁起见,我已缩短它们),但它应该给您一个开始,假设您的头部此时没有爆炸:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:msxml="urn:schemas-microsoft-com:xslt"
                exclude-result-prefixes="msxml">

  <xsl:output method="xml" version="1.0" indent="yes" encoding="ISO-8859-1"/>

  <xsl:key name="Test" match="Variant" use="concat(../@ProductID, '|', .)" />

  <xsl:template match="/">
    <xsl:variable name="variantSplit">
      <xsl:apply-templates select="//Item" />
    </xsl:variable>
    <table>
      <xsl:apply-templates select="msxml:node-set($variantSplit)/Item/Variant[generate-id() = generate-id(key('Test', concat(../@ProductID, '|', .))[1])]" />
    </table>
  </xsl:template>

  <xsl:template match="Item">
    <Item ProductID="{@ItemNo}@@{@ShopID}">
      <xsl:call-template name="VariantCodeSplit" />
    </Item>
  </xsl:template>

  <xsl:template name="VariantCodeSplit">
    <xsl:param name="Code" select="@VariantCode" />
    <xsl:choose>
      <xsl:when test="contains($Code, '.')">
        <Variant>
          <xsl:value-of select="substring-before($Code, '.')"/>
        </Variant>
        <xsl:call-template name="VariantCodeSplit">
          <xsl:with-param name="Code" select="substring-after($Code, '.')" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <Variant>
          <xsl:value-of select="$Code"/>
        </Variant>
     </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="Variant">
    <Item>
      <Column name="Variant">
        <xsl:value-of select="."/>
      </Column>
      <Column name="Product">
        <xsl:value-of select="../@ProductID"/>
      </Column>
    </Item>
  </xsl:template>
</xsl:stylesheet>

当然,如果您的实际XML有200000多个元素,那么这可能不会特别快。