匹配当前节点和下一个具有特定标签名的节点 [英] Matching nodes between current node and next with a certain tag-name

查看:22
本文介绍了匹配当前节点和下一个具有特定标签名的节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的 XML 结构如下:

I have XML structured in the following way:

<content>
    <sh1>A</sh1>
    <sh2>A1</sh2>
    <sh2>A2</sh2>
    <sh1>B</sh1>
    <sh2>B1</sh2>
    <sh2>B2</sh2>
    <sh2>B3</sh2>
    <sh1>C</sh1>
    <sh2>C1</sh2>
    <sh2>C2</sh2>
    <sh1>D</sh1>
    <sh2>D1</sh2>
    <sh2>D2</sh2>
    <sh2>D3</sh2>
    <sh2>D4</sh2>
</content>

如您所见,这里有两个值得关注的标签名称:sh1sh2.这些代表标题和子标题,我需要通过嵌套它们的内容在输出 XML 中组织它们(你可以看到示例输出 此处).到目前为止,我的方法是匹配每个 sh1,然后尝试匹配它和下一个 sh1 之间的每个 sh2.

As you can see there are two tag names of concern here: sh1 and sh2. These represent headers and sub-headers, and I need to organize them as such in the output XML by nesting their contents (you can see example output here). My approach so far has been to match each sh1, and then try to match each sh2 between it and the next sh1.

我发现了这个超级有用的问题,这让我(我希望)大部分时间都在那里.我已经将以下 XSLT (1.0) 放在一起:

I found this super helpful question which has gotten me (I hope) most of the way there. I've thrown together the following XSLT (1.0):

<xsl:template match="content">
    <ul>
        <xsl:apply-templates select="//sh1" />
    </ul>
</xsl:template>

<xsl:template match="sh1">
    <li>
        <h1><xsl:value-of select="." /></h1>
        <ul>
            <xsl:apply-templates select="./following-sibling::sh1[1]/preceding-sibling::sh2[preceding-sibling::.]" />
        </ul>
    </li>
</xsl:template>

<xsl:template match="sh2">
    <li><xsl:value-of select="." /></li>
</xsl:template>

问题(可以预见)是我不能拥有 XPath ./following-sibling::sh1[1]/preceding-sibling::sh2[preceding-sibling::.]使用 . 表示 sh2 应该将当前节点作为前一个兄弟节点.我可以使用其他一些关键字吗?或者也许是另一种完全获得我想要的嵌套结构的方法?

The problem (predictably) is that I can't have the XPath ./following-sibling::sh1[1]/preceding-sibling::sh2[preceding-sibling::.] using . signify that the sh2 should have the current node as a preceding sibling. Is there some other keyword I could use? Or perhaps another approach entirely to get the nested structure I'm going for?

推荐答案

在 XSLT 2.0 中,我会使用 for-each-group 来解决这个问题:

In XSLT 2.0 I would approach the problem using for-each-group:

<xsl:template match="content">
    <ul>
        <xsl:for-each-group select="*" group-starting-with="sh1">
            <li>
                <h1><xsl:value-of select="." /></h1>
                <xsl:if test="current-group()[2]">
                    <ul>
                        <xsl:apply-templates select="current-group() except ." />
                    </ul>
                </xsl:if>
            </li>
        </xsl:for-each-group>
    </ul>
</xsl:template>

<xsl:template match="sh2">
    <li><xsl:value-of select="." /></li>
</xsl:template>

如果您仅限于 1.0,那么基于密钥的方法正如 michael.hor257k 所建议的 可能是最有效的,但对现有方法的最简单修复是

If you're restricted to 1.0 then a key based approach as suggested by michael.hor257k is probably the most efficient, but the simplest fix to your existing approach would be something like

<xsl:apply-templates select="following-sibling::sh2[
     generate-id(preceding-sibling::sh1[1]) = generate-id(current())]" />

preceding-sibling::sh1[1] 是距离我们当时正在过滤的节点最近的 sh1 前面的 current() 是模板正在操作的 sh1 元素.比较它们的 generate-id 值是 XSLT 1.0 确定两个表达式是否选择同一个节点的方法(如果你只是比较这些值而不是它们生成的 ID,那么你就有风险具有相同文本的两个不同 sh1 元素的误报将被视为相等).在 2.0 中,您可以只使用 is 运算符来执行此操作(preceding-sibling::sh1[1] is current()).

preceding-sibling::sh1[1] is the nearest preceding sh1 to the node we're filtering at the time, and current() is the sh1 element that the template is operating on. Comparing their generate-id values is the XSLT 1.0 way to determine whether the two expressions select the same node (if you just compared the values rather than their generated IDs then you risk false positives as two different sh1 elements with the same text would be considered equal). In 2.0 you could just use the is operator to do this (preceding-sibling::sh1[1] is current()).

或者,您可以使用一种我听说过的称为兄弟递归"的技术来模拟while 循环":仅将模板应用于此标题下的第一个 sh2,并拥有该模板递归到下一个兄弟节点,依此类推,直到用完合适的节点:

Alternatively, you can simulate a "while loop" using a technique I've heard referred to as "sibling recursion": apply templates to just the first sh2 under this heading, and have that template recurse to the next sibling, and so on until we run out of appropriate nodes:

<ul>
    <xsl:apply-templates select="following-sibling::*[1][self::sh2]" />
</ul>

和模板:

<xsl:template match="sh2">
    <li><xsl:value-of select="." /></li>
    <xsl:apply-templates select="following-sibling::*[1][self::sh2]" />
</xsl:template>

只要 following-sibling::*[1] 不是 sh2 元素,循环"就会终止.

The "loop" will terminate as soon as following-sibling::*[1] is not an sh2 element.

这篇关于匹配当前节点和下一个具有特定标签名的节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆