首先:我是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。
答案 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多个元素,那么这可能不会特别快。