2

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()"/>

4

3 回答 3

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>
于 2013-02-17T12:13:46.860 回答
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">

然后要获取包含该组的项目,请使用current-group()函数

<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>
于 2013-02-17T12:31:26.417 回答
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>
于 2013-02-17T07:52:22.933 回答