2012-11-21 26 views
0

我有一個樣式表,我正在使用一個只適用於XSLT 1.0的perl模塊。我想在JSON字典內創建一個JSON數組,所以我需要適當的逗號分隔元素。我正在解析第二個單元格中有一個或多個跨度的XHTML表。所以for-each select =「./ tr」,然後是for-each select =「./ td [1]/span」或類似的東西。嵌套的位置()和末尾()嵌套 - 每個xslt

改變了一點後,它的行爲如伊恩所說的那樣。

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text" version="1.0" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" doctype-public="-//W3C//DTD HTML 4.01//EN" encoding="UTF-8" /> 
    <xsl:template match="text()"> 
    </xsl:template> 
    <xsl:template match="table/tbody"> 
     <xsl:text>[</xsl:text> 
     <xsl:for-each select="./tr[not(@class='no-results')]"> 
      <xsl:text>{"</xsl:text> 
      <xsl:value-of select="normalize-space(.//strong)" /> 
      <xsl:text>":{"ingredients":{</xsl:text> 
      <xsl:for-each select=".//div[@class='reagent-list']//a[@class='item-link reagent']"> 
       <xsl:value-of select="substring(./@href, 14)" />:<xsl:value-of select="normalize-space(./span[1])" /> 
       <xsl:if test="position() != last()"> 
        <xsl:text>,</xsl:text> 
       </xsl:if> 
      </xsl:for-each> 
      <xsl:text>}</xsl:text> 
      <xsl:if test="position() != last()"> 
       <xsl:text>,</xsl:text> 
      </xsl:if> 
      <xsl:text>&#10;</xsl:text> 
     </xsl:for-each> 
     <xsl:text>]</xsl:text> 
    </xsl:template> 
</xsl:stylesheet> 

我意識到樣式不會在XML符合以下。實際的文件是巨大的。不過,我希望你明白我的意思。我只是做這件事:

<table> 
    <thead> 
    <tr> 
     <th>a</th> 
     <th>b</th> 
     <th>c</th> 
    </tr> 
    </thead> 
    <tbody> 
    <tr> 
     <td>foo</td> 
     <td><span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    <tr> 
     <td>foo</td> 
     <td><span>an element</span><span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    <tr> 
     <td>foo</td> 
     <td><span>an element</span><span>an element</span><span>an element</span><span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    </tbody> 
</table> 

=>

{ 
    "Row one":["an element"], 
    "Row two":["an element", "an element"], 
    "Row three":["an element", "an element", "an element", "an element"] 
} 

相反,我得到這個:

{ 
    "Row one":["an element",], 
    "Row two":["an element", "an element",], 
    "Row three":["an element" "an element" "an element" "an element"] 
} 

我一直在使用position()last()在測試標籤打印逗號並且它似乎對外循環正常工作,但是如何在打印用於分隔數組的逗號時告訴我的測試標籤使用內部for-each範圍?

+0

這就是'position()'和'last()'應該如何在內部循環中工作 - 究竟是什麼不能如你所期望的那樣工作? –

+0

逗號被添加,就好像它正在檢查外部循環元素的位置。我的問題描述得不好嗎?感謝您看看它。 –

+0

如果沒有看到實際的XSLT代碼,很難判斷髮生了什麼。如果你真的直接在內部的'for-each'中使用'position()'和'last()'函數(而不是在某個地方的'[]'謂詞中),那麼它應該像你期望的那樣工作... –

回答

2

正如@IanRoberts所說,很難在沒有看到您現有的XSLT外觀的情況下提供有針對性的幫助。

也就是說,這是一種面向推送(即沒有0​​)並且不需要last()的解決方案。

當這個XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:my="my" 
    exclude-result-prefixes="my" 
    version="1.0"> 
    <xsl:output omit-xml-declaration="no" indent="yes" method="text" /> 
    <xsl:strip-space elements="*" /> 

    <my:ones> 
    <num>one</num> 
    <num>two</num> 
    <num>three</num> 
    <num>four</num> 
    <num>five</num> 
    <num>six</num> 
    <num>seven</num> 
    <num>eight</num> 
    <num>nine</num> 
    </my:ones> 

    <xsl:template match="/*"> 
    <xsl:text>{&#10;</xsl:text> 
    <xsl:apply-templates select="tbody/tr" /> 
    <xsl:text>&#10;}</xsl:text> 
    </xsl:template> 

    <xsl:template match="tr"> 
    <xsl:variable name="vPos" select="position()" /> 
    <xsl:if test="$vPos &gt; 1">,&#10;</xsl:if> 
    <xsl:text>&#09;"Row </xsl:text> 
    <xsl:value-of select="document('')/*/my:ones/*[$vPos]" /> 
    <xsl:text>":[</xsl:text> 
    <xsl:apply-templates select="td[2]/span" /> 
    <xsl:text>]</xsl:text> 
    </xsl:template> 

    <xsl:template match="span"> 
    <xsl:if test="position() &gt; 1">, </xsl:if> 
    <xsl:value-of select="concat('&quot;', ., '&quot;')" /> 
    </xsl:template> 

</xsl:stylesheet> 

......是對所提供的XML運行:

<table> 
    <thead> 
    <tr> 
     <th>a</th> 
     <th>b</th> 
     <th>c</th> 
    </tr> 
    </thead> 
    <tbody> 
    <tr> 
     <td>foo</td> 
     <td> 
     <span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    <tr> 
     <td>foo</td> 
     <td> 
     <span>an element</span><span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    <tr> 
     <td>foo</td> 
     <td> 
     <span>an element</span><span>an element</span><span>an element</span><span>an element</span></td> 
     <td>bar</td> 
    </tr> 
    </tbody> 
</table> 

......想要的結果產生:

{ 
    "Row one":["an element"], 
    "Row two":["an element", "an element"], 
    "Row three":["an element", "an element", "an element", "an element"] 
} 

說明:

  • 第一模板的根元素相匹配。其目的是將模板應用於該元素的<tr>孫子,並在{}之間夾入那些結果(適當地添加換行符)。

  • 第二個模板匹配<tr>元素。它輸出行信息,並指示將模板應用於第二個<td>元素的所有<span>子元素(同樣,必要時將結果夾在大括號和其他文本之間)。

    • 注:而不是使用last(),你會看到的第一個元素輸出一個逗號,後跟一個換行符,如果這<tr>在目前情況下的位置是大於1。這有正確應用逗號的效果相同;這只是一種看待同樣問題的不同方式(而且是我使用的方式,因爲它對我來說似乎更有效;))。

    • 注:,使該解決方案更可擴展的,你會看到,我不是靜態輸出的話"one""two"等各一行。相反,在XSLT的頂部,我定義了一個<my:ones>元素來保存每個「ones」數字的文本值。當處理每個<tr>時,我在當前上下文中使用該<tr>的位置來檢索正確的<num>元素的值。 我已經將它作爲練習留給讀者,但確實可以定義<my:tens><my:hundreds>等,以便將此解決方案擴展到潛在的大量行。

  • 最終模板匹配<span>元素。同樣,它使用<xsl:if>元素來測試當前上下文中此<span>的位置是否大於1;如果是,輸出一個逗號(後跟一個空格)。之後,我們只需將兩個"符號與夾在中間的跨度值連接起來。

+0

這樣做很有意義,非常明確,但簡化測試甚至更好。感謝您的關注。 –

+0

@JeppeBoelsmand - 很樂意幫忙! – ABach