1

与此问题类似(有更多相关条目,但是作为新用户,我只能发布一个 URL): Xpath Get elements that are between 2 elements

我有一个关于选择“其他/定界”元素之间出现的元素集的问题。尝试使用 XSLT 将平面 HTML 表转换为分层 XML 结构时会出现这种情况。我尝试在模板中使用递归,但 saxon 拒绝接受它,因为它导致了死锁,很可能是我的错,但让我们从头开始。

首先源数据是 HTML 表:

<table >
    <thead>
        <tr>
            <th>Column 1</th>
            <th>Column 2</th>
            <th>Column 3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <th colspan="3" >Group 1</th>
        </tr>
        <tr>
            <td>attribute 1.1.1</td>
            <td>attribute 1.1.3</td>
            <td>attribute 1.1.2</td>
        </tr>
        <tr>
            <td>attribute 1.2.1</td>
            <td>attribute 1.2.2</td>
            <td>attribute 1.2.3</td>
        </tr>
        <tr>
            <td>attribute 1.3.1</td>
            <td>attribute 1.3.2</td>
            <td>attribute 1.3.3</td>
        </tr>
        <tr>
            <th colspan="3" >Group 2</th>
        </tr>
        <tr>
            <td>attribute 2.1.1</td>
            <td>attribute 2.1.3</td>
            <td>attribute 2.1.2</td>
        </tr>
        <tr>
            <td>attribute 2.2.1</td>
            <td>attribute 2.2.2</td>
            <td>attribute 2.2.3</td>
        </tr>
        <tr>
            <td>attribute 2.3.1</td>
            <td>attribute 2.3.2</td>
            <td>attribute 2.3.3</td>
        </tr>
    </tbody>
</table>

XML 中的目标输出将是:

 <groups>
    <group name="Group 1">
        <item attribute1="attribute 1.1.1" attribute2="attribute 1.1.3" attribute3="attribute 1.1.2"/>
        <item attribute1="attribute 1.2.1" attribute2="attribute 1.2.2" attribute3="attribute 1.2.3"/>
        <item attribute1="attribute 1.3.1" attribute2="attribute 1.3.2" attribute3="attribute 1.3.3"/>
    </group>
    <group name="Group 2">
        <item attribute1="attribute 2.1.1" attribute2="attribute 2.1.3" attribute3="attribute 2.1.2"/>
        <item attribute1="attribute 2.2.1" attribute2="attribute 2.2.2" attribute3="attribute 2.2.3"/>
        <item attribute1="attribute 2.3.1" attribute2="attribute 2.3.2" attribute3="attribute 2.3.3"/>
    </group>
</groups>

所以我想拥有所有项目条目(TR元素)并将它们添加到一个组中。这基本上归结为选择所有后续兄弟 TR 元素,直到我们遇到一个具有 TH 元素作为子元素的元素。如果我只能确定具有 TH 孩子的第一个 TR 的位置,指示组的新标题,则可以通过以下方式完成:

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:for-each select="following-sibling::tr[position() < $positionOfNextThElement]">            
                <xsl:call-template name="item"/>
            </xsl:for-each>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

但是,我无法确定第一个遇到的 TR/TH 标签的位置。

如前所述,我尝试在模板中使用递归:始终调用“项目”模板,并在此模板中确定我们是否也想在下一个项目上调用它。我认为问题在于从模板中调用模板。上下文中的项目不增加?我应该交出一个参数来确定我们正在处理的项目吗?

无论如何,这就是我想出的:

<xsl:for-each select="tbody/tr">
    <xsl:if test="th">
        <xsl:element name="group">
            <xsl:attribute name="name"><xsl:value-of select="th"/></xsl:attribute>
            <xsl:call-template name="item"/>
        </xsl:element>
    </xsl:if>
</xsl:for-each>

<xsl:template name="item">
    <xsl:element name="item">
        <xsl:attribute name="attribute1"><xsl:value-of select="following-sibling::tr[1]/td[1]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[2]"/></xsl:attribute>
        <xsl:attribute name="attribute2"><xsl:value-of select="following-sibling::tr[1]/td[3]"/></xsl:attribute>
    </xsl:element>
    <!-- When the next element has not got a TH tag, continue with invoking this template -->
    <xsl:if test="count(following-sibling::tr[1]/th) != 1">
        <xsl:call-template name="item"/>
    </xsl:if>
</xsl:template>

欢迎就如何实现这一点提出任何建议!

4

2 回答 2

1

递归调用模板“item”时上下文不增加的原因是 xs:call-template 总是将当前上下文项作为上下文传递。因此,您可能已经看到,变换只是进入无限递归。

假设您总是需要恰好产生三个属性,您甚至不需要递归。

试试这个:

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

    <xsl:template match="table">
        <groups>
            <xsl:apply-templates select="tbody/tr[th]"/>
        </groups>
    </xsl:template>

    <xsl:template match="tr[th]">
        <xsl:variable name="id" select="generate-id(.)"/>
        <group name="{string(th)}">
            <xsl:apply-templates
                select="following-sibling::tr[not(th)][generate-id(preceding-sibling::tr[th][1]) = $id]"/>
        </group>
    </xsl:template>

    <xsl:template match="tr">
        <item attribute1="{td[1]}" attribute2="{td[2]}" attribute3="{td[3]}" />                    
    </xsl:template>

</xsl:stylesheet>

这通过将模板应用于每个标题行来工作。这些模板中的每一个都使用该复杂的 xpath 来调用“其”后续行,这些行是具有该特定行的任何后续兄弟行,因为它是具有标题的第一行。

当然,如果属性的数量不同,那么您将需要在那里递归并增加传递一个指示位置的参数。

XSLT 分组有几种既定的方法,其中一种是递归的,就像您正在做的那样。另一种方法称为 Muenchian 分组。一个很好的文章是here

于 2009-10-10T14:43:08.563 回答
0

另一种解决方案,适用于无递归的可变属性计数。

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

  <xsl:template match="table">
    <groups>
      <xsl:apply-templates select="tbody/tr[th]"/>
    </groups>
  </xsl:template>

  <xsl:template match="tr[th]">
    <group name="{th}">
      <xsl:apply-templates select="
        following-sibling::tr[not(th)][
          generate-id(preceding-sibling::tr[th][1]) = generate-id(current())
        ]
      "/>
    </group>
  </xsl:template>

  <xsl:template match="tr">
    <item>
     <xsl:apply-templates select="td" />
    </item>
  </xsl:template>

  <xsl:template match="td">
    <xsl:attribute name="attribute{position()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>
于 2009-10-11T10:14:30.583 回答