0

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

我有一个产品(项目)的 XML。在完整的 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。

4

1 回答 1

1

我能想到的唯一方法(在 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>

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

 <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 Grouping 技术。这涉及通过 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])]" />

(注意这里使用了节点集扩展功能。在我的例子中,我使用的是微软的)。

然后,您可以拥有一个与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 多个元素,这可能不会特别快。

于 2013-08-15T23:20:53.807 回答