简体   繁体   English

XPath/XSL:通过单个 for-each 循环生成一个干净的表

[英]XPath/XSL: produce a clean table through a single for-each loop

Probably a really simple question, but I want to produce a clean table through using a single for-each loop in my XSL file using Xpath.可能是一个非常简单的问题,但我想通过使用 Xpath 在我的 XSL 文件中使用单个 for-each 循环来生成一个干净的表。 I've come a long way, which lead to the resulting file and image you can see below.我已经走了很长一段路,这导致了您可以在下面看到的结果文件和图像。 像这样

XSL file XSL 文件

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
 <body>
    
  <h2>Provincies</h2>
  <table border="1">
    <tr>
      <th bgcolor="#9acd32" style="text-align:left">Provincie</th>
      <th bgcolor="#9acd32" style="text-align:left">Gemeente</th>
      <th bgcolor="#9acd32" style="text-align:left">Stad</th>
      <th bgcolor="#9acd32" style="text-align:left">Aantal bedrijven</th>
    </tr>
                
    <xsl:for-each select="bedrijven/provincie/gemeente/stad">
    <xsl:variable name="provincie" select="../../@naam"/>
    <xsl:variable name="gemeente" select="../@naam"/>
    <xsl:variable name="stad" select="@naam"/>
       <tr>
          <xsl:choose>
             <xsl:when test="preceding::*/text() != $provincie"><td></td></xsl:when>
             <xsl:otherwise><td><xsl:value-of select="$provincie"/></td></xsl:otherwise>
          </xsl:choose>
        
          <xsl:choose>
             <xsl:when test="preceding-sibling::*/text() != $gemeente"><td></td></xsl:when>
             <xsl:otherwise><td><xsl:value-of select="$gemeente"/></td></xsl:otherwise>
          </xsl:choose>
          <td><xsl:value-of select="$stad"/></td>
          <td><xsl:value-of select="count(child::bedrijf)"/></td>
       </tr>

   </xsl:for-each>
  </table>
 </body>
</html>
</xsl:template>
</xsl:stylesheet>

However, the issue is that it only prints the first Province and not the others.但是,问题在于它只打印第一个省份而不是其他省份。 I only want every province to be printed once, no repetition.我只希望每个省都印一次,不重复。 I've been trying to do this all day, and googling all over the place, but I can't seem to find an answer.我整天都在尝试这样做,并且到处搜索,但我似乎找不到答案。 So I hope someone here can help me.所以我希望这里有人可以帮助我。 Below is my XML file.下面是我的 XML 文件。

Example of XML structure XML结构示例

<bedrijven
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="file:///C:\Users\Boro\Documents\XML opdrachten\XML Opdrachten\Opdracht 1\db\test.xsd">
   <provincie naam="Drenthe">
       <gemeente naam="aa en Hunze">
           <stad naam="anloo">
              <bedrijf>
                  <naam>v.o.f. Anloo</naam>
                  <postcode>9468CG</postcode>
              </bedrijf>
           </stad>
           <stad naam="gasselternijveen">
              <bedrijf>
                  <naam>podiumdelen.nl</naam>
                  <postcode>9514BV</postcode>
              </bedrijf>
           </stad>
        </gemeente>
    </provincie>
    <provincie naam="Flevoland">
        <gemeente naam="almere">
           <stad naam="almere">
               <bedrijf>
                   <naam>mood media</naam>
                   <postcode>1322CE</postcode>
               </bedrijf>
               <bedrijf>
                   <naam>dutch Meat Service</naam>
                   <postcode>1311XC</postcode>
               </bedrijf>
               <bedrijf>
                   <naam>jossafety</naam>
                   <postcode>1338XX</postcode>
               </bedrijf>
           </stad>
        </gemeente>
    </provincie>
</bedrijven>

I get a correct output with this piece of code:我用这段代码得到了正确的 output :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="bedrijven">
    <html>
      <body>
        <h2>Provincies</h2>
        <table border="1">
          <tr>
            <th bgcolor="#9acd32" style="text-align:left">Provincie</th>
            <th bgcolor="#9acd32" style="text-align:left">Gemeente</th>
            <th bgcolor="#9acd32" style="text-align:left">Stad</th>
            <th bgcolor="#9acd32" style="text-align:left">Aantal bedrijven</th>
          </tr>
          <xsl:for-each select="provincie">
            <xsl:variable name="provincie" select="@naam"/>
            <xsl:for-each select="gemeente">
              <xsl:variable name="gemeente" select="@naam"/>
              <xsl:for-each select="stad">
                <xsl:variable name="stad" select="@naam"/>
                <tr>
                  <td>
                    <xsl:if 
                      test="count(preceding::stad[
                        ancestor::provincie/@naam = $provincie
                      ]) = 0">
                      <xsl:value-of select="$provincie"/>
                    </xsl:if>
                  </td>
                  <td>
                    <xsl:if 
                      test="count(preceding::stad[
                        ancestor::gemeente/@naam = $gemeente
                      ]) = 0">                       
                      <xsl:value-of select="$gemeente"/>
                    </xsl:if>
                  </td>
                  <td>
                    <xsl:value-of select="$stad"/>
                  </td>
                  <td>
                    <xsl:value-of select="count(child::bedrijf)"/>
                  </td>
                </tr>
              </xsl:for-each>
            </xsl:for-each>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

I use three levels of <xsl:for-each> to avoid recomputing $provincie and $gemeente in each iteration.我使用三个级别的<xsl:for-each>来避免在每次迭代中重新计算$provincie$gemeente

I use a simple test to see if the field provincie or gemeente corresponds to the first stad inside of a provincie or a gemeente .我使用一个简单的测试来查看字段provinciegemeente是否对应于provinciegemeente内部的第一个stad If it is the first occurence (which means the count of the preceding ones is zero), I print its value.如果它是第一次出现(这意味着前面的计数为零),我打印它的值。

Martin Ronnen suggests in its comment above to take advantage from the HTML attribute rowspan in outputting the results. Martin Ronnen 在上面的评论中建议在输出结果时利用 HTML 属性行rowspan If you like that idea, you could modify the code in this way:如果你喜欢这个想法,你可以这样修改代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="bedrijven">
    <html>
      <body>
        <h2>Provincies</h2>
        <table border="1">
          <tr>
            <th bgcolor="#9acd32" style="text-align:left">Provincie</th>
            <th bgcolor="#9acd32" style="text-align:left">Gemeente</th>
            <th bgcolor="#9acd32" style="text-align:left">Stad</th>
            <th bgcolor="#9acd32" style="text-align:left">Aantal bedrijven</th>
          </tr>
          <xsl:for-each select="provincie">
            <xsl:variable name="provincie" select="@naam"/>
            <xsl:variable name="steden-per-provincie" 
              select="count(gemeente/stad)"/>
            <xsl:for-each select="gemeente">
              <xsl:variable name="gemeente" select="@naam"/>
              <xsl:variable name="steden-per-gemeente" select="count(stad)"/>
              <xsl:for-each select="stad">
                <xsl:variable name="stad" select="@naam"/>
                <tr>
                  <xsl:if 
                    test="count(preceding::stad[
                      ancestor::provincie/@naam = $provincie
                    ]) = 0">
                    <td rowspan="{$steden-per-provincie}">
                      <xsl:value-of select="$provincie"/>
                    </td>
                  </xsl:if>
                  <xsl:if 
                    test="count(preceding::stad[
                      ancestor::gemeente/@naam = $gemeente
                    ]) = 0">                       
                  <td rowspan="{$steden-per-gemeente}">
                      <xsl:value-of select="$gemeente"/>
                    </td>
                  </xsl:if>
                  <td>
                    <xsl:value-of select="$stad"/>
                  </td>
                  <td style="text-align:right">
                    <xsl:value-of select="count(child::bedrijf)"/>
                  </td>
                </tr>
              </xsl:for-each>
            </xsl:for-each>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Here is a screenshot of the result using the rowspan attribute:这是使用rowspan属性的结果的屏幕截图: XSLT 输出

If you really need a single for-each loop, try this, but it will be less optimal:如果你真的需要一个for-each循环,试试这个,但它会不太理想:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="bedrijven">
    <html>
      <body>
        <h2>Provincies</h2>
        <table border="1">
          <tr>
            <th bgcolor="#9acd32" style="text-align:left">Provincie</th>
            <th bgcolor="#9acd32" style="text-align:left">Gemeente</th>
            <th bgcolor="#9acd32" style="text-align:left">Stad</th>
            <th bgcolor="#9acd32" style="text-align:left">Aantal bedrijven</th>
          </tr>
          <xsl:for-each select="//stad">
            <xsl:variable name="provincie" select="ancestor::provincie/@naam"/>
            <xsl:variable name="gemeente" select="ancestor::gemeente/@naam"/>
            <xsl:variable name="stad" select="@naam"/>
            <tr>
              <xsl:if 
                test="count(preceding::stad[
                  ancestor::provincie/@naam = $provincie
                ]) = 0">
              <td rowspan="{count(following::stad[
                  ancestor::provincie/@naam = $provincie
                ]) + 1}">
                  <xsl:value-of select="$provincie"/>
                </td>
              </xsl:if>
              <xsl:if 
                test="count(preceding::stad[
                  ancestor::gemeente/@naam = $gemeente
                ]) = 0">                       
              <td rowspan="{count(following::stad[
                  ancestor::gemeente/@naam = $gemeente
                ]) + 1}">
                  <xsl:value-of select="$gemeente"/>
                </td>
              </xsl:if>
              <td>
                <xsl:value-of select="$stad"/>
              </td>
              <td style="text-align:right">
                <xsl:value-of select="count(child::bedrijf)"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

And, finally, if you want to use the xsl:number feature, here you have a version of the code which is perhaps easier to read:最后,如果您想使用xsl:number功能,这里有一个可能更易于阅读的代码版本:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="bedrijven">
    <html>
      <body>
        <h2>Provincies</h2>
        <table border="1">
          <tr>
            <th bgcolor="#9acd32" style="text-align:left">Provincie</th>
            <th bgcolor="#9acd32" style="text-align:left">Gemeente</th>
            <th bgcolor="#9acd32" style="text-align:left">Stad</th>
            <th bgcolor="#9acd32" style="text-align:left">Aantal bedrijven</th>
          </tr>
          <xsl:for-each select="//stad">
            <xsl:variable name="provincie" select="ancestor::provincie/@naam"/>
            <xsl:variable name="gemeente" select="ancestor::gemeente/@naam"/>
            <xsl:variable name="stad" select="@naam"/>
            <xsl:variable name="gemeente-nr">
              <xsl:number count="gemeente" from="provincie"/>
            </xsl:variable>
            <xsl:variable name="stad-nr">
              <xsl:number count="stad" from="gemeente"/>
            </xsl:variable>
            <tr>
              <xsl:if test="($gemeente-nr = 1) and ($stad-nr = 1)">
                <td rowspan="{count(ancestor::provincie//stad)}">
                  <xsl:value-of select="$provincie"/>
                </td>
              </xsl:if>
              <xsl:if test="$stad-nr = 1">                       
                <td rowspan="{count(ancestor::gemeente//stad)}">
                  <xsl:value-of select="$gemeente"/>
                </td>
              </xsl:if>
              <td><xsl:value-of select="$stad"/></td>
              <td style="text-align:right">
                <xsl:value-of select="count(child::bedrijf)"/>
              </td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

You can use xsl:number :您可以使用xsl:number

<xsl:variable name="gemeente-pos">
    <xsl:number level="any" from="provincie"/>
</xsl:variable>

<xsl:variable name="stad-pos">
    <xsl:number/>
</xsl:variable>

   <tr>
      <xsl:choose>
         <xsl:when test="$gemeente-pos != 1"><td></td></xsl:when>
         <xsl:otherwise><td><xsl:value-of select="$provincie"/></td></xsl:otherwise>
      </xsl:choose>
    
      <xsl:choose>
         <xsl:when test="$stad-pos != 1"><td></td></xsl:when>
         <xsl:otherwise><td><xsl:value-of select="$gemeente"/></td></xsl:otherwise>
      </xsl:choose>
      <td><xsl:value-of select="$stad"/></td>
      <td><xsl:value-of select="count(child::bedrijf)"/></td>
   </tr>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM