当许多同名 XML 元素通过 <xsl:for-each> 循环中的条件 <xsl:if> 语句时控制唯一输出

Controlling unique output when many identically named XML elements pass a conditional <xsl:if> statement held within a <xsl:for-each> loop

我开发了一个简单的网页,它是使用基本的 XMLXSLT (1.0) 文件组合生成的方法(没有 JavaScript)。

<xsl:for-each> 循环的帮助下,我的 XSLT 文件构建了一个(两列)HTML table,其中包含一个新的 table 行(<tr><td></td><td></td></tr>) 对于每个唯一 <bookstore/book/promotions/promotionDate> XML 元素值,但重要的是,只有在 <promotionDate>值传递一个条件语句<xsl:if test>,它嵌套在循环中。

条件语句

首先,我在 <xsl:for-each> 循环 (<xsl:variable name ="our-date" select="promotionDate"></xsl:variable>) 中设置了一个变量,使用 <promotionDate> 值(例如 20160915)作为我的 select参数.

然后我将我的(促销日期)变量与今天日期 (<xsl:if test="$our-date &gt;= 20160212">) 的免费数字表示进行交叉引用。

如果 $our-date 变量 大于或等于今天的日期 我们 运行 循环的其余部分,生成一个新行并填充细胞.

太棒了!嗯差不多……

如您在我的 XML 文件和 浏览器输出 (下方)中所见,'Harry Potter and the Philosopher's Stone' <book> 属性,我有两个单独的 <promotions> 属性,其中 <promotionDate> 都大于今天的日期。因此,当 <xsl:for-each> 循环逻辑 运行s 时,这个特定 <book> 属性的行被输出两次。不好!

XSLT:

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

<xsl:output indent="yes"/>

<xsl:key name="prom-by-id" match="bookstore/promotion" use="promotionID"/>
<xsl:key name="location-by-id" match="bookstore/locations" use="locationID"/>

  <xsl:template match ="/">
    <table class="upcomingpromotions" border="1" style="width: 680px">
    <thead>
        <tr>
          <th width='45%' class='noselect'>Title</th>
          <th width='15%' class='noselect'>Location(s)</th>
        </tr>
    </thead>
    <xsl:for-each select="bookstore/book/promotions[not(promotionDate = preceding-sibling::promotions/promotionDate)]">
            <xsl:variable name ="our-date" select="promotionDate"></xsl:variable>
            <xsl:if test="$our-date &gt;= 20160212">
              <tr>
                <td>
                  <xsl:value-of select="../title"/><br/>Date: <xsl:value-of select="$our-date"/>*
                </td>
                <td>
                  <xsl:for-each select="key('location-by-id', key('prom-by-id', ../promotions/promotionNum)/promotionLocation)">
                    <xsl:value-of select="location"/><br/>
                   </xsl:for-each>
                </td>
              </tr>
            </xsl:if>
</xsl:for-each>
</table>
<br/>
<p>*Just for reference, proving XPath <em>'bookstore/book/promotions/promotionDate'</em> passes the logic.</p>

  </xsl:template>

</xsl:stylesheet>

XML:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="books.xsl" ?>
<bookstore>
  <book>
    <title>Harry Potter and the Philosopher's Stone</title>
    <author>J K. Rowling</author>
    <year>1997</year>
    <price>3.99</price>
    <publisher>Bloomsbury (UK)</publisher>
    <synopsis>
         Harry Potter and the Philosopher's Stone is the first novel in the Harry Potter series and J. K. Rowling's debut novel.

         The plot follows Harry Potter, a young wizard who discovers his magical heritage as he makes close friends and a few enemies in his first year at the Hogwarts School of Witchcraft and Wizardry.

         With the help of his friends, Harry faces an attempted comeback by the dark wizard Lord Voldemort, who killed Harry's parents, but failed to kill Harry when he was just a year old.
   </synopsis>
    <promotions>
      <promotionDate>20160915</promotionDate>
      <promotionCode>1</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>3</promotionNum>
   </promotions>
    <promotions>
      <promotionDate>20160917</promotionDate>
      <promotionCode>2</promotionCode>
      <promotionNum>7</promotionNum>
   </promotions>
 </book>
 <book>
    <title>The Girl with the Dragon Tattoo</title>
    <author>Stieg Larsson</author>
    <year>2005</year>
    <price>5.99</price>
    <publisher>Norstedts Förlag (SWE)</publisher>
    <synopsis>
         In Stockholm, Sweden, journalist Mikael Blomkvist, co-owner of Millennium magazine, has lost a libel case brought against him by businessman Hans-Erik Wennerström. Lisbeth Salander, a brilliant but troubled investigator and hacker, compiles an extensive background check on Blomkvist for business magnate Henrik Vanger, who has a special task for him. 

         In exchange for the promise of damning information about Wennerström, Blomkvist agrees to investigate the disappearance and assumed murder of Henrik's grandniece, Harriet, 40 years ago. 

         After moving to the Vanger family's compound, Blomkvist uncovers a notebook containing a list of names and numbers that no one has been able to decipher.
    </synopsis>
    <promotions>
      <promotionDate>20160117</promotionDate>
      <promotionCode>3</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>4</promotionNum>
      <promotionNum>6</promotionNum>
      <promotionNum>4</promotionNum>
   </promotions>
  </book>
   <book>
    <title>Grandpa's Great Escape</title>
    <author>David Walliams</author>
    <year>2015</year>
    <price>5.00</price>
    <publisher>Harper Collins Children's Books</publisher>
    <synopsis>
         An exquisite portrait of the bond between a small boy and his beloved Grandpa – this book takes readers on an incredible journey with Spitfires over London and Great Escapes through the city in a high octane adventure full of comedy and heart. Illustrated by the award-winning Tony Ross.
    </synopsis>
    <promotions>
      <promotionDate>20160117</promotionDate>
      <promotionCode>4</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>4</promotionNum>
      <promotionNum>6</promotionNum>
      <promotionNum>4</promotionNum>
   </promotions>
 </book>
<book>
    <title>A Brief History of Seven Killings</title>
    <author>Marlon James</author>
    <year>2015</year>
    <price>6.29</price>
    <publisher>Oneworld Publications</publisher>
    <synopsis>
         From the acclaimed author of The Book of Night Women comes a dazzling display of masterful storytelling exploring this near-mythic event. Spanning three decades and crossing continents, A Brief History of Seven Killings chronicles the lives of a host of unforgettable characters – slum kids, one-night stands, drug lords, girlfriends, gunmen, journalists, and even the CIA. 

         Gripping and inventive, ambitious and mesmerising, A Brief History of Seven Killings is one of the most remarkable and extraordinary novels of the twenty-first century.
    </synopsis>
    <promotions>
      <promotionDate>20160617</promotionDate>
      <promotionCode>5</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>4</promotionNum>
      <promotionNum>6</promotionNum>
      <promotionNum>4</promotionNum>
   </promotions>
  </book>
  <book>
    <title>Leading</title>
    <author>Sir Alex Ferguson</author>
    <year>2015</year>
    <price>7.99</price>
    <publisher>Hodder and Stoughton</publisher>
     <synopsis>
         In this revelatory new book, Sir Alex Ferguson dissects his 38 record-breaking years of management - first in Scotland and then, of course, at Manchester United - to reveal the key tools he used to deliver sustained success on and off the field. From tactics to teamwork, from hiring to firing, from dealing with the boardroom to responding to failure, LEADING investigates the pivotal leadership decisions of an astonishing career.
    </synopsis>
    <promotions>
      <promotionDate>20160117</promotionDate>
      <promotionCode>6</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>4</promotionNum>
      <promotionNum>6</promotionNum>
      <promotionNum>4</promotionNum>
   </promotions>
  </book>
  <book>
    <title>The Girl on the Train</title>
    <author>Paula Hawkins</author>
    <year>2015</year>
    <price>6.99</price>
    <publisher>Doubleday</publisher>
    <synopsis>
          Rachel catches the same commuter train every morning. She knows it will wait at the same signal each time, overlooking a row of back gardens. She’s even started to feel like she knows the people who live in one of the houses. ‘Jess and Jason’, she calls them. Their life – as she sees it – is perfect. If only Rachel could be that happy.

        And then she sees something shocking. It’s only a minute until the train moves on, but it’s enough. 

        Now everything’s changed. Now Rachel has a chance to become a part of the lives she’s only watched from afar.

        Now they’ll see; she’s much more than just the girl on the train…
    </synopsis>
    <promotions>
      <promotionDate>20160417</promotionDate>
      <promotionCode>7</promotionCode>
      <promotionNum>1</promotionNum>
      <promotionNum>4</promotionNum>
      <promotionNum>6</promotionNum>
      <promotionNum>4</promotionNum>
   </promotions>
  </book>

  <promotion>
    <promotionID>1</promotionID>
    <percentageOff>10</percentageOff>
    <promotionalMerchandise>No</promotionalMerchandise>
    <promotionStartDate>2015-10-14T00:00:00</promotionStartDate>
    <promotionEndDate>2015-10-19T00:00:00</promotionEndDate>
    <promotionLocation>1</promotionLocation>
  </promotion>
  <promotion>
    <promotionID>2</promotionID>
    <percentageOff>15</percentageOff>
    <promotionalMerchandise>No</promotionalMerchandise>
    <promotionStartDate>2015-10-11T00:00:00</promotionStartDate>
    <promotionEndDate>2015-10-16T00:00:00</promotionEndDate>
    <promotionLocation>2</promotionLocation>
  </promotion>
  <promotion>
    <promotionID>3</promotionID>
    <percentageOff>30</percentageOff>
    <promotionalMerchandise>Yes</promotionalMerchandise>
    <promotionStartDate>2015-09-02T00:00:00</promotionStartDate>
    <promotionEndDate>2015-09-07T00:00:00</promotionEndDate>
    <promotionLocation>2</promotionLocation>
  </promotion>
  <promotion>
    <promotionID>4</promotionID>
    <percentageOff>5</percentageOff>
    <promotionalMerchandise>Yes</promotionalMerchandise>
    <promotionStartDate>2015-11-22T00:00:00</promotionStartDate>
    <promotionEndDate>2015-11-27T00:00:00</promotionEndDate>
    <promotionLocation>3</promotionLocation>
 </promotion>
  <promotion>
    <promotionID>5</promotionID>
    <percentageOff>50</percentageOff>
    <promotionalMerchandise>No</promotionalMerchandise>
    <promotionStartDate>2015-08-13T00:00:00</promotionStartDate>
    <promotionEndDate>2015-08-18T00:00:00</promotionEndDate>
    <promotionLocation>1</promotionLocation>
  </promotion>
  <promotion>
     <promotionID>6</promotionID>
     <percentageOff>80</percentageOff>
    <promotionalMerchandise>No</promotionalMerchandise>
    <promotionStartDate>2015-07-01T00:00:00</promotionStartDate>
    <promotionEndDate>2015-07-05T00:00:00</promotionEndDate>
    <promotionLocation>1</promotionLocation>
 </promotion>
    <promotion>
    <promotionID>7</promotionID>
    <percentageOff>80</percentageOff>
    <promotionalMerchandise>No</promotionalMerchandise>
    <promotionStartDate>2015-07-01T00:00:00</promotionStartDate>
    <promotionEndDate>2015-07-05T00:00:00</promotionEndDate>
    <promotionLocation>4</promotionLocation>
  </promotion>

  <locations>
     <locationID>1</locationID>
    <location>York</location>
  </locations>
     <locations>
      <locationID>2</locationID>
    <location>London</location>
  </locations>
    <locations>
      <locationID>3</locationID>
    <location>Glasgow</location>
  </locations>
      <locations>
      <locationID>4</locationID>
    <location>Cardiff</location>
  </locations>
</bookstore>

浏览器输出:

问题:

所以,当你遇到这样的情况时,一个属性 (<book>) 可以包含许多同名元素 (<promotions/promotionDate>),并且这些元素的值理论上可以全部传递嵌套在 <xsl:for-each> 循环中的条件 <xsl:if> 语句,如何管理数据以确保每个 <book> 属性只生成一行?

只需 运行 您的 book 元素代码即可:

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

<xsl:param name="date" select="20160212"/>

<xsl:output indent="yes"/>

<xsl:key name="prom-by-date" match="bookstore/book/promotions" use="promotionDate"/>

<xsl:key name="prom-by-id" match="bookstore/promotion" use="promotionID"/>
<xsl:key name="location-by-id" match="bookstore/locations" use="locationID"/>

  <xsl:template match ="/">
    <table class="upcomingpromotions" border="1" style="width: 680px">
    <thead>
        <tr>
          <th width='45%' class='noselect'>Title</th>
          <th width='15%' class='noselect'>Location(s)</th>
        </tr>
    </thead>
    <xsl:for-each select="bookstore/book[promotions[generate-id() = generate-id(key('prom-by-date', promotionDate)[1])][promotionDate >= $date]]">

              <tr>
                <td>
                  <xsl:value-of select="title"/><br/>
                </td>
                <td>
                  <xsl:for-each select="key('location-by-id', key('prom-by-id', promotions/promotionNum)/promotionLocation)">
                    <xsl:value-of select="location"/><br/>
                   </xsl:for-each>
                </td>
              </tr>
</xsl:for-each>
</table>
<br/>
<p>*Just for reference, proving XPath <em>'bookstore/book/promotions/promotionDate'</em> passes the logic.</p>

  </xsl:template>

</xsl:stylesheet>