gpt4 book ai didi

xslt - 使用XPath选择具有特定属性值的连续元素

转载 作者:行者123 更新时间:2023-12-03 15:29:21 25 4
gpt4 key购买 nike

我有这样的 XML:

<span>1</span>
<span class="x">2</span>
<span class="x y">3</span>
<span class="x">4</span>
<span>5</span>
<span class="x">6</span>
<span>7</span>
<span class="x">8</span>

我想要的是使用 XSLT 样式表来放置所有元素的内容 class属性包含 x合二为一 <x>元素。所以输出应该是这样的:
1 <x>234</x> 5 <x>6</x> 7 <x>8</x>

(或者,理想情况下,
1 <x>2<y>3</y>4</x> 5 <x>6</x> 7 <x>8</x>

但是当我解决了这个问题时,这是一个需要解决的问题。)

这是我的 XSLT 的相关片段:
<xsl:template match="span[contains(@class,'x') and preceding-sibling::span[1][not(contains(@class,'x'))]]">
<x><xsl:for-each select=". | following-sibling::span[contains(@class,'x')]">
<xsl:value-of select="text()"/>
</xsl:for-each></x>
</xsl:template>

<xsl:template match="span[contains(@class,'x') and preceding-sibling::span[1][contains(@class,'x')]]">
</xsl:template>

<xsl:template match="span">
<xsl:value-of select="text()"/>
</xsl:template>

这产生的是:
1 <x>23468</x> 5 <x>68</x> 7 <x>8</x>

我很确定我必须在 XPath 表达式中使用计数,这样它就不会选择所有具有类 x 的以下元素,只选择连续的元素。但是我如何计算连续的呢?还是我这样做是错误的?

最佳答案

这很棘手,但可行(提前阅读,抱歉)。

就 XPath 轴(根据定义不连续)而言,“连续性”的关键是检查“首先满足条件”的相反方向上最近的节点是否也是“开始”手头系列的节点:

ab  <- first node to fulfill the condition, starts series 1b  <- series 1b  <- series 1ab  <- first node to fulfill the condition, starts series 2b  <- series 2b  <- series 2a

In your case, a series consists of <span> nodes that have the string x in their @class:

span[contains(concat(' ', @class, ' '),' x ')] 

请注意,我连接空格以避免误报。

A <span>开始一个系列(即“首先满足条件”的系列)可以定义为具有 x 的系列。在同类中,并且没有直接在另一个 <span> 之前还有一个 x :
not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])

我们必须在 <xsl:if> 中检查此条件以避免模板为系列中的节点生成输出(即模板只会为“起始节点”执行实际工作)。

现在到了棘手的部分。

我们必须从这些“起始节点”中的每一个中选择所有 following-sibling::span具有 x 的节点在他们的类(class)。还包括当前 span考虑只有一个元素的系列。好吧,很简单:
. | following-sibling::span[contains(concat(' ', @class, ' '),' x ')]

对于这些中的每一个,我们现在找出它们最接近的“起始节点”是否与模板正在处理的节点相同(即开始他们的系列)。这意味着:
  • 它们必须是一个系列的一部分(即它们必须跟随 spanx )
    preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')]
  • 现在删除任何 span其起始节点与当前系列起始节点不同。这意味着我们检查任何前兄弟 span (具有 x )本身没有直接在 span 前面与 x :
    preceding-sibling::span[contains(concat(' ', @class, ' '),' x ')][
    not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])
    ][1]
  • 然后我们使用 generate-id()检查节点身份。如果找到的节点与 $starter 相同,则当前跨度是属于连续系列的跨度。

  • 把它们放在一起:
    <xsl:template match="span[contains(concat(' ', @class, ' '),' x ')]">
    <xsl:if test="not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])">
    <xsl:variable name="starter" select="." />
    <x>
    <xsl:for-each select="
    . | following-sibling::span[contains(concat(' ', @class, ' '),' x ')][
    preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')]
    and
    generate-id($starter)
    =
    generate-id(
    preceding-sibling::span[contains(concat(' ', @class, ' '),' x ')][
    not(preceding-sibling::span[1][contains(concat(' ', @class, ' '),' x ')])
    ][1]
    )
    ]
    ">
    <xsl:value-of select="text()" />
    </xsl:for-each>
    </x>
    </xsl:if>
    </xsl:template>

    是的,我知道它不漂亮。有一个 <xsl:key>基于更有效的解决方案,Dimitre 的回答表明了这一点。

    使用您的示例输入,将生成以下输出:
    1
    <x>234</x>
    5
    <x>6</x>
    7
    <x>8</x>

    关于xslt - 使用XPath选择具有特定属性值的连续元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8961220/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com