0

我有一个与 perl 模块一起使用的样式表,该模块仅适用于 XSLT 1.0。我想在 JSON 字典中创建一个 JSON 数组,所以我需要对元素进行适当的逗号分隔。我正在解析一个 XHTML 表,其中第二个单元格中有 1 个或多个跨度。所以 for-each select="./tr" 然后 for-each select="./td[1]/span" 或类似的东西。

稍作更改后,它的行为与 Ian 所说的一样。

<?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()andlast()在测试标签中打印逗号,它似乎在外部循环中正常工作,但是如何告诉我的测试标签在打印分隔数组的逗号时使用内部 for-each 范围?

4

1 回答 1

2

正如@IanRoberts 所提到的,如果不查看现有 XSLT 的外观,就很难提供有针对性的帮助。

也就是说,这是一个面向推送(即 no <xsl:for-each>)并且不需要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>孙子元素,并将这些结果夹在{and之间}(根据需要添加换行符)。

  • 第二个模板匹配<tr>元素。它输出行信息并被指示将模板应用于<span>第二个元素的所有子<td>元素(同样,根据需要将结果夹在大括号和其他文本之间)。

    • 注意:如果 this在当前上下文中的位置大于 1 last(),您将看到第一个元素输出逗号而不是使用,然后是换行符。这与正确应用逗号具有相同的效果;<tr>这只是看待同一问题的不同方式(并且是我使用的方式,因为它对我来说似乎更有效;))。

    • 注意:为了使这个解决方案更具可扩展性,您会看到我没有在每一行中静态输出单词"one","two"等。相反,在 XSLT 的顶部,我定义了一个<my:ones>元素来保存每个“ones”数字的文本值。在处理 each<tr>时,我使用它<tr>在当前上下文中的位置来检索正确<num>元素的值。我把它作为练习留给了读者,但确实可以定义<my:tens>,<my:hundreds>等来将此解决方案扩展到潜在的大量行。

  • 最终模板匹配<span>元素。再次,它使用一个<xsl:if>元素来测试 this<span>在当前上下文中的位置是否大于 1;如果是这样,则输出一个逗号(后跟一个空格)。之后,我们只需将两个"符号连接起来,并将跨度值夹在中间。

于 2012-11-21T16:37:55.273 回答