首页 文章

XSLT有点困惑

提问于
浏览
2

在XSLT上遇到一点麻烦..我想我可能会以完全错误的方式解决这个问题 . 尝试用一行中具有特殊状态的物品的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

    你最简单的选择只是一个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>
    
  • 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>
    
  • 0

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

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

    然后,对于您匹配的每个 customer 元素,您将获得每个 item 的不同 status 元素,如下所示:

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

    基本上,这样做是为客户查看每个 item ,并选择在为status元素定义的密钥中首先出现的那个 .

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

    <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>
    

相关问题