XSLT有点困惑

时间:2013-02-17 05:22:35

标签: xslt

对XSLT有点麻烦..我想我可能会以错误的方式解决它... 尝试使用具有特殊1状态的项目的SKU显示客户名称... 然后客户有特殊的2项等,然后是第2部分(我还没有开始)没有状态的项目

所以对于这个XML文件,输出将是

Joe prod1 //special1
Joe prod3 //special2
Joe prod2 //no status
Joe prod4 //no status
Joe prod5 //no status
John Smith prod6 prod8 //special1
John Smith prod7 //no status
John Smith prod9 //no status
John Smith prod10 //no status

目前有点可行,但问题是如果没有special1或special2,我无法弄清楚如何让它不打印客户名称..

并且我不确定如何显示之后没有状态的那些 - 任何帮助都将非常感谢!

XML:

<customer>
<name>Joe</name>
<order>
    <item>
      <SKU>prod1</SKU>
      <status>special1</status>
    </item>
    <item>
      <SKU>prod2</SKU>
    </item>
    <item>
      <SKU>prod3</SKU>
      <status>special2</status>
    </item>
    <item>
      <SKU>prod4</SKU>
    </item>
    <item>
      <SKU>prod5</SKU>
    </item>
</order>
 </customer>
 <customer
<name>John Smith</name>
<order>
    <item>
      <SKU>prod6</SKU>
      <status>special1</status>
    </item>
    <item>
      <SKU>prod7</SKU>
    </item>
    <item>
      <SKU>prod8</SKU>  
      <status>special1</status>
    </item>
    <item>
     <SKU>prod9</SKU>
</item>
    <item>
      <SKU>prod10</SKU>
    </item>
</order>

XSLT:

<!DOCTYPE xsl:stylesheet[ <!ENTITY nl "&#xd;&#xa;"> ]>

    

<xsl:template match="customer">
    <xsl:value-of select="name" /><xsl:apply-templates select="order/item[status='special1']" /><xsl:text>&nl;</xsl:text>
    <xsl:value-of select="name" /><xsl:apply-templates select="order/item[status='special2']" /><xsl:text>&nl;</xsl:text>
</xsl:template>

<xsl:template match="item[status='special1']"><xsl:text> </xsl:text><xsl:value-of select="SKU" /></xsl:template>
<xsl:template match="item[status=special2']"><xsl:text> </xsl:text><xsl:value-of select="SKU" /></xsl:template>

<xsl:template match="text()"/>

3 个答案:

答案 0 :(得分:0)

你最简单的选择只是一个xsl:if

<xsl:template match="customer">
    <xsl:if test="order/item[status='special1']">
        <xsl:value-of select="name" /><xsl:apply-templates select="order/item[status='special1']" /><xsl:text>&nl;</xsl:text>
    </xsl:if>
    <xsl:if test="order/item[status='special2']">
        <xsl:value-of select="name" /><xsl:apply-templates select="order/item[status='special2']" /><xsl:text>&nl;</xsl:text>
    </xsl:if>
</xsl:template>

答案 1 :(得分:0)

我假设您不知道先验的不同状态。因此,如果您不希望XML可维护(无需在每次添加其他状态时更改它),您可以使用以下解决方案:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="text" />

    <!-- Use the following key for grouping -->
    <xsl:key name="status-key"
             match="item"
             use="status" />

    <!-- Cache the following operation to avoid doing it several times in the future.
         You can improve performance by changing // to a fixed path in your XML where
         all the items are (e.g. /customers/customer/order/item)  -->
    <xsl:variable name="item-group"
                  select="//item[generate-id(.) = generate-id(key('status-key', status)[1])]" />


    <xsl:template match="customer">

        <!-- Obtain the following values before losing the current context -->
        <xsl:variable name="current-id" select="generate-id(.)" />
        <xsl:variable name="current-name" select="name" />

        <!-- Display the products with a status defined -->
        <xsl:for-each select="$item-group">
            <!-- Obtain list of status for this costumer -->
            <xsl:variable name="customer-status"
                          select="key('status-key', status)[generate-id(../..) = $current-id]" />
            <!-- Print the status information if the costumer has at least one status -->
            <xsl:if test="$customer-status">
                <!-- Display the name of the costumer -->
                <xsl:value-of select="$current-name" />
                <!-- Group the product by status -->
                <xsl:for-each select="$customer-status">
                    <xsl:value-of select="concat(' ', SKU)" />
                </xsl:for-each>
                <!-- Output the status -->
                <xsl:value-of select="concat(' //', status, '&#xa;')" />
            </xsl:if>
        </xsl:for-each>

        <!-- Display the prodcuts without status -->
        <xsl:for-each select="order/item[not(status)]">
            <xsl:value-of select="concat($current-name, ' ', SKU, ' //no-status&#xa;')" />
        </xsl:for-each>

    </xsl:template>

    <xsl:template match="text()" />

</xsl:stylesheet>

答案 2 :(得分:0)

这是“分组”问题的一个示例。您正尝试通过客户和状态的组合对项目进行分组。解决这个问题的方法因您使用的是XSLT 1.0还是XSLT 2.0而有所不同。在XSLT 1.0中,您将使用一种名为Muenchian Grouping的技术。首先,通过定义一个键来保存要分组的节点。在这种情况下,您按客户项目

进行分组
<xsl:key name="items" match="item" use="concat(generate-id(../..), '|', status)" />

然后,对于您匹配的每个客户元素,您将获得每个的不同状态元素,如下所示:

<xsl:apply-templates select="order/item
    [status != '']
    [generate-id() = generate-id(key('items', concat(generate-id(../..), '|', status))[1])]" />

基本上,这样做是查看客户的每个,并选择在为状态元素定义的键中首先出现的项。

然后,在匹配具有状态的项目的模板中,您可以获得具有相同状态的单个项目,如下所示:

<xsl:apply-templates select="key('items', concat(generate-id(../..), '|', status))/SKU" />

选择没有状态的项目要容易得多

<xsl:apply-templates select="order/item[not(status != '')]" />

这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text"/>
   <xsl:key name="items" match="item" use="concat(generate-id(../..), '|', status)" />

   <xsl:template match="customer">
      <xsl:apply-templates select="order/item[status != ''][generate-id() = generate-id(key('items', concat(generate-id(../..), '|', status))[1])]" />
      <xsl:apply-templates select="order/item[not(status != '')]" />
   </xsl:template>

   <xsl:template match="item[status !='']">
      <xsl:value-of select="../../name" />
      <xsl:apply-templates select="key('items', concat(generate-id(../..), '|', status))/SKU" />
      <xsl:value-of select="concat(' //', status, '&#13;')" />
   </xsl:template>

   <xsl:template match="item">
      <xsl:value-of select="concat(../../name, ' ', SKU, ' // no status&#13;')" />
   </xsl:template>

   <xsl:template match="SKU">
      <xsl:value-of select="concat(' ', .)" />
   </xsl:template>
</xsl:stylesheet>

当应用于XML时,输出以下内容:

Joe prod1 //special1
Joe prod3 //special2
Joe prod2 // no status
Joe prod4 // no status
Joe prod5 // no status
John Smith prod6 prod8 //special1
John Smith prod7 // no status
John Smith prod9 // no status
John Smith prod10 // no status

如果你使用的是XSLT2.0,它会变得有点简单,因为你可以让我们使用 xsl:for-each-group 来处理分组:

<xsl:for-each-group select="order/item[status != '']" group-by="status">

然后要使用该组获取项目,请使用当前组()功能

<xsl:apply-templates select="current-group()/SKU" />

这是完整的XSLT2.0样式表,它也应该输出相同的结果:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="text"/>

   <xsl:template match="customer">
      <xsl:for-each-group select="order/item[status != '']" group-by="status">
          <xsl:value-of select="../../name" />
          <xsl:apply-templates select="current-group()/SKU" />
          <xsl:value-of select="concat(' //', status, '&#13;')" />          
      </xsl:for-each-group>

      <xsl:apply-templates select="order/item[not(status != '')]" />
   </xsl:template>

   <xsl:template match="item">
      <xsl:value-of select="concat(../../name, ' ', SKU, ' // no status&#13;')" />
   </xsl:template>

   <xsl:template match="SKU">
      <xsl:value-of select="concat(' ', .)" />
   </xsl:template>
</xsl:stylesheet>