XSLT:我如何迭代节点,只在满足条件时输出一次,但需要多个条件

时间:2017-01-20 18:55:26

标签: xml templates xslt recursion

我是XSL的新手,正在努力弄清楚如何做我需要做的事情。我有一个订单数据集,并将其转换为我可以用来将订单导入到履行系统的格式。我正在使用for-each迭代节点并输出数据。足够简单到这一点。

现在新的要求是我需要检查项目子节点中的值。如果定义了任何这些值,我需要在输出中添加一行。棘手的部分是这个额外的行是在项目级别决定的,但是即使在多个子节点中找到该值,也只应该在我的输出中。这就是我无法分配变量值或破坏for-each的地方。我确定某种类型的递归是答案,但我无法弄清楚如果不重复我试图找到的每个属性的所有项目节点,它将如何工作。我也受限于XSLT 1.0,因为这必须通过不支持2.0的第三方应用程序来完成。

以下是我的数据集

的示例
<orders>
<order>
    <id>1</id>
    <items>
        <item>
            <sku>12345</sku>
            <price>5.00</sku>
        </item>
        <item>
            <sku>23456</sku>
            <price>4.00</sku>
            <options>
                <option1>item_upgrade</option1>
            </option>
        </item>
        <item>
            <item>
            <sku>34567</sku>
            <price>3.00</sku>
            <options>
                <option2>finish_upgrade</option2>
            </option>
        </item>
        <item>
            <sku>45678</sku>
            <price>2.00</sku>
            <options>
                <option1>item_upgrade</option1>
                <option2>finish_upgrade</option2>
            </option>
        </item>
   </items>
</order>
<order>
    <id>2</id>
    <items>
        <item>
            <sku>12345</sku>
            <price>5.00</sku>
        </item>
        <item>
            <sku>23456</sku>
            <price>4.00</sku>
        </item>
        <item>
            <item>
            <sku>34567</sku>
            <price>3.00</sku>
        </item>
   </items>
</order>
<order>
....
</order>
</orders>

最终我希望看到的是

   1 12345 5.00
   1 23456 4.00
   1 99999 0.00
   1 34567 3.00
   1 88888 0.00
   1 45678 2.00
   2 12345 5.00
   2 23456 4.00
   2 34567 3.00
   ...

99999和88888在源数据中不存在,但在履行系统中是静态值,分别代表item_upgrade和finish_upgrade。如果在订单中的任何项目上定义了item_upgrade,则应将99999行添加到输出中。对于finish_upgrade也是如此,这需要为输出添加88888行。这些静态条目(例如99999)的排序顺序无关紧要,只要它仍按顺序/ id排序即可。多个项目节点上可以存在相同的选项,并且不一定存在于任何项目节点上。

我正在使用它是为我正在创建的导入文件中的订单添加附加费。这些费用在订单级别,如果在订单中包含的任何项目上选择了特定选项,则应为单行。费用在源数据中不存在,无法添加。幸运的是,它们是静态值,因此我只需要确定何时添加它们。

目前采用for-each而不考虑这些费用的做法看起来像是

<xsl:template match="/">
    <xsl:for-each select="orders/order/items/item">
        <xsl:value-of select="../../id" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="sku" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="price" />
    </xsl:for-each>
</xsl:template>

如果我想为任何出现添加静态费用,在一个订单中多次允许,我会添加类似

的内容
<xsl:template match="/">
    <xsl:for-each select="orders/order/items/item">
        <xsl:value-of select="../../id" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="sku" />
        <xsl:text> </xsl:text>
        <xsl:value-of select="price" />
        <xsl:if test="options/option1='item_upgrade'">
           <xsl:value-of select="../../id" />
           <xsl:text> 99999 0.00</xsl:text>
        </xsl:if>
        <xsl:if test="options/option2='finish_upgrade'">
           <xsl:value-of select="../../id" />
           <xsl:text> 88888 0.00</xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

这就是我被困住的地方。每个订单需要添加一次此值,但是是否需要添加它需要在项目级别决定。我希望这对于能够更好地理解这一点的人来说很容易,但是到目前为止,我已经采取了任何方法,我会回过头来为每次出现添加额外的静态线,而不是为了整个订单添加一次,即使是相同的选项是在单个订单中的多个项目上定义的。任何人都可以引导我采用一种适用于此的方法吗?提前谢谢

1 个答案:

答案 0 :(得分:0)

基本上,这是分组问题,因为您使用的是XSLT 1.0,所以最好使用Muenchian method进行处理。

考虑以下样式表:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8" />

<xsl:key name="opt" match="options/*" use="concat(ancestor::order/id, '|', .)" />

<xsl:template match="/orders">
    <xsl:for-each select="order">
        <xsl:variable name="order-id" select="id" />
        <!-- items -->
        <xsl:for-each select="items/item">
            <xsl:value-of select="$order-id" />
            <xsl:text>&#9;</xsl:text>
            <xsl:value-of select="sku" />
            <xsl:text>&#9;</xsl:text>
            <xsl:value-of select="price" />
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
        <!-- options -->
        <xsl:for-each select="items/item/options/*[count(. | key('opt', concat($order-id, '|', .))[1]) = 1]">
            <xsl:value-of select="$order-id" />
            <xsl:text>&#9;</xsl:text>
            <xsl:value-of select="." />
            <xsl:text>&#9;0.00&#10;</xsl:text>
        </xsl:for-each> 
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

当应用于以下格式良好的(!)输入示例时:

<强> XML

<orders>
   <order>
      <id>1</id>
      <items>
         <item>
            <sku>12345</sku>
            <price>5.00</price>
         </item>
         <item>
            <sku>23456</sku>
            <price>4.00</price>
            <options>
               <option1>red</option1>
            </options>
         </item>
         <item>
            <sku>34567</sku>
            <price>3.00</price>
            <options>
               <option2>circle</option2>
            </options>
         </item>
         <item>
            <sku>45678</sku>
            <price>4.00</price>
            <options>
               <option1>red</option1>
            </options>
         </item>
      </items>
   </order>
   <order>
      <id>2</id>
      <items>
         <item>
            <sku>12345</sku>
            <price>5.00</price>
         </item>
         <item>
            <sku>23456</sku>
            <price>4.00</price>
         </item>
         <item>
            <sku>34567</sku>
            <price>3.00</price>
         </item>
      </items>
   </order>
</orders>

结果将是:

1   12345   5.00
1   23456   4.00
1   34567   3.00
1   45678   4.00
1   red     0.00
1   circle  0.00
2   12345   5.00
2   23456   4.00
2   34567   3.00