如何选择两个其他元素之间的所有元素

时间:2014-04-10 16:33:47

标签: xml xslt xpath xml-parsing

我希望能够显示具有特定值的其他元素之间的所有元素。 例如

<wd>abc</wd>
<wd>123</wd>
<wd>456</wd>
<wd>789</wd>
<wd>def</wd>

我希望代码在abc之后和def之前查找所有单词,然后显示它们。

到目前为止我尝试的是(命名空间是ss)

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:ss="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd">
<xsl:output method="text"/>

    <xsl:template match="/">



    <!-- Variable declarations -->

    <xsl:variable name="wds" select="//ss:wd"/>

    <!-- Variable declarations end-->

        <xsl:if test="preceding::ss:wd[contains(.,'7BB')">
            <xsl:if test="following::ss:wd[contains(.,SHIPMENT)">
                <xsl:for-each select="$wds"/>
                <xsl:value-of select="$wds"/>
            </xsl:if>
        </xsl:if>

    </xsl:template>
</xsl:stylesheet>

但这根本不起作用。
我该如何解决这个问题?

更新:回应迈克尔

除非我忽略了某些内容,否则您的代码应该能够被复制+粘贴到我的代码中。但是,当我这样做时,XSLT会执行,但不会返回任何数据。

这就是我所拥有的:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ss="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd">

<xsl:template match="/">



<!-- Variable declarations -->

<xsl:variable name="wds" select="//ss:wd"/>

<!-- Variable declarations end-->

        <xsl:for-each select="ss:document/ss:wd[preceding-sibling::ss:wd[.='7BB'] and following-sibling::ss:wd[.='SHIPMENT']]">
            <xsl:value-of select="." />
            <xsl:if test="position()!=last()">
                <xsl:text>/</xsl:text>  
            </xsl:if>
        </xsl:for-each>   


        Net Amount <xsl:value-of select="$wds[4]"/>
        <xsl:text>&#10;</xsl:text>  
        VAT Amount <xsl:value-of select="$wds[8]"/>
        <xsl:text>&#10;</xsl:text>  
        Total <xsl:value-of select="$wds[12]"/>


</xsl:template>

到目前为止,这是我的整个XSLT,我还附上了我的来源:dropbox

3 个答案:

答案 0 :(得分:3)

这是一种在XSLT 1.0中实现目标的天真且表现不佳的方法:

/*/*[.='abc'][1]/following-sibling::*[
    not(.='def' or preceding-sibling::*[.='def'])]

英文:

  

检索包含abc的第一个元素之后的所有同级元素,这些元素本身不是包含def的元素,并且没有包含def的前一个兄弟元素(即不包含的元素; t出现在包含def)的元素之后。

有些人会告诉你,你永远不应该这样做。我认为他们错了。有很多情况(特别是在小数据集上),这是最简单和最明显的解决方案。还有其他情况(特别是在大型数据集上),这种方法会崩溃。

检索两个节点集(尤其是大型数据集)的交集的更好技术是 Kayessian方法。它看起来像这样:

$ns1[count(.|$ns2)=count($ns2)]

英语(非正式):

  

从$ ns1获取所有节点,以便将该节点添加到$ ns2不会增加其大小

从技术上讲,如果节点a和集合$ns2的联合创建的集合与$ns2具有相同数量的元素,那么a必须已经在那一套。我们希望$ns1中的每个元素都是真的。

在我们的例子中,我们希望在包含abc的第一个节点之后的每个兄弟节点和每个包含def的第一个节点之前的每个兄弟节点的集合1)的交集。它看起来像这样(取决于输入的结构):

/*/*[.='abc'][1]/following-sibling::*[
    count(.| /*/*[.='def'][1]/preceding-sibling::*)=
    count(/*/*[.='def'][1]/preceding-sibling::*)]

以下是一个完整的例子:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes" />
    <xsl:strip-space elements="*" />
    <xsl:variable name="ns1" select="/*/*[.='abc'][1]/following-sibling::*" />
    <xsl:variable name="ns2" select="/*/*[.='def'][1]/preceding-sibling::*" />
    <xsl:template match="/">
        <xsl:copy-of select="$ns1[count(.|$ns2)=count($ns2)]" />
    </xsl:template>
</xsl:stylesheet>

在此输入上:

<root>
    <wd>abc</wd>
    <wd>123</wd>
    <wd>456</wd>
    <wd>789</wd>
    <wd>def</wd>
</root>

你得到这个输出:

<wd>123</wd>
<wd>456</wd>
<wd>789</wd>

答案 1 :(得分:2)

这是一个非常的简单方法:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ss="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd">

<xsl:output method="text" encoding="UTF-8"/>

<xsl:template match="/">
    <xsl:for-each select="ss:root/ss:wd[preceding-sibling::ss:wd[.='abc'] and following-sibling::ss:wd[.='def']]">
        <xsl:value-of select="." />
        <xsl:if test="position()!=last()">
            <xsl:text>/</xsl:text>  
        </xsl:if>
    </xsl:for-each>           
</xsl:template>

</xsl:stylesheet>

请注意,假设输入的形式为:

<root xmlns="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd">
    <wd>001</wd>
    <wd>002</wd>
    <wd>abc</wd>
    <wd>123</wd>
    <wd>456</wd>
    <wd>789</wd>
    <wd>def</wd>
    <wd>998</wd>
    <wd>999</wd>
</root>

根据此输入,应用上述转换的结果是:

123/456/789

您没有提供(完整)输入或所需输出,因此您需要进行必要的调整。

重要: 我们还假设整个节点集中只出现一次<wd>abc</wd><wd>def</wd>。否则就不那么简单了。

-

关于性能的说明:如果不在您将使用的实际处理器上进行测试,很难预测性能。一般来说,显式代码比默认代码更快:最好说ss:root/ss:wd而不是//ss:wd,而ss:wd优于*


编辑:

您链接到的文档的结构与您的问题中的示例有很大不同。具体来说,<wd l="1675" t="4243" r="1939" b="4358">7BB</wd>节点没有以下兄弟节点,因为它是其<ln>父节点的最后一个子节点。另请注意,值<wd>的{​​{1}}会出现两次。

然而,我针对它运行了以下测试样式表:

SHIPMENT

并获得以下结果:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ss="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd"
exclude-result-prefixes="ss">

<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<test>
    <xsl:for-each select="//ss:wd[preceding::ss:wd[.='7BB'] and following::ss:wd[.='SHIPMENT']]">
        <wd>
            <xsl:value-of select="." />
        </wd>
     </xsl:for-each>   
</test>
</xsl:template>

</xsl:stylesheet>

希望这是你可以使用的东西。

答案 2 :(得分:1)

XSLT 2.0的运算符<<>><<需要在XSLT样式表中写为&lt;&lt;),这有助于检查文档顺序,以便{ {1}}可能会这样做。并且//ss:wd[. >> //ss:wd[. = 'abc'] and . &lt;&lt; //ss:wd[. = 'def']]也可以提供帮助。

您链接的XML没有for-each-group group-starting-with/group-ending-with兄弟,元素处于更深层次。您可能想尝试是否

wd

为您提供您想要的结果,对我而言,它输出

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ss="http://www.scansoft.com/omnipage/xml/ssdoc-schema3.xsd">

<xsl:template match="/">



<!-- Variable declarations -->

<xsl:variable name="wds" select="//ss:wd"/>

<!-- Variable declarations end-->

        <xsl:for-each select="$wds[preceding::ss:wd[.='7BB'] and following::ss:wd[.='SHIPMENT']]">
            <xsl:value-of select="." />
            <xsl:if test="position()!=last()">
                <xsl:text>/</xsl:text>  
            </xsl:if>
        </xsl:for-each>   


        Net Amount <xsl:value-of select="$wds[4]"/>
        <xsl:text>&#10;</xsl:text>  
        VAT Amount <xsl:value-of select="$wds[8]"/>
        <xsl:text>&#10;</xsl:text>  
        Total <xsl:value-of select="$wds[12]"/>


</xsl:template>

</xsl:stylesheet>