选择有条件的节点

Selecting nodes with conditions

我正在尝试转换 XML 个文件,但我被阻止了。这个想法是聚合从 <ore:Aggregation> 节点到下一个节点的每个元素。这是某种分项。但是每次创建 dc:item.

我不能得到超过 1 edm:WebResource

XML :

<rdf:RDF>
    <ore:Aggregation rdf:about="id1">
        <some:crap/>
    </ore:Aggregation>
    <edm:ProvidedCHO rdf:about="id1">
        <some:crap/>
    </edm:ProvidedCHO>
    <edm:WebResource rdf:about="some/random/url"> 
        <some:crap/>
    </edm:WebResource>
            ...
               (n 'edm:WebResource' nodes)
            ...
    <edm:WebResource rdf:about="some/random/url">
        <some:crap/>    
    </edm:WebResource>

    <ore:Aggregation rdf:about="id2">
        <some:crap/>
    </ore:Aggregation>
    <edm:ProvidedCHO rdf:about="id2">
        <some:crap/>
    </edm:ProvidedCHO>
    <edm:WebResource rdf:about="some/random/url"> 
        <some:crap/>
    </edm:WebResource>
            ...
               (n 'edm:WebResource' nodes)
            ...
    <edm:WebResource rdf:about="some/random/url">
        <some:crap/>    
    </edm:WebResource>

        ... and on and on ...
</rdf:RDF>

XSL

<xsl:template match="/">
    <xsl:apply-templates select="/rdf:RDF/ore:Aggregation"/>
</xsl:template>

<xsl:template match="/rdf:RDF/ore:Aggregation">
    <rdf:RDF>
    <xsl:for-each select=".">
            <dc:item>
                <xsl:attribute name="rdf:about">
                    <xsl:value-of select="concat($fileName, '_item', position())"/>
                </xsl:attribute>

                <xsl:copy-of select="."/>
                <xsl:copy-of select="following-sibling::edm:ProvidedCHO[1]"/>
                <xsl:copy-of select="following-sibling::edm:WebResource[1]"/>

                <!-- WHERE IT SUCKS -->
                <xsl:if test="local-name(following-sibling::*[3]) = 'edm:WebResource'">
                    <xsl:copy-of select="following-sibling::*[3]"/>
                </xsl:if>                    
                <!-- ./WHERE IT SUCKS -->


            </dc:item>
    </xsl:for-each>
    </rdf:RDF>
</xsl:template>

另一次尝试带来了太多节点:

<!-- WHERE IT SUCKS -->
<xsl:copy-of select="following-sibling::*[local-name (preceding::*[1]) = 'ore:Aggregation']"/>
<!-- ./WHERE IT SUCKS -->

预期输出

<!-- ITEM N1 -->
<rdf:RDF>
    <dc:item rdf:about="some.concat.string"/>
    <ore:Aggregation rdf:about="id1">
        <some:crap/>
    </ore:Aggregation>
    <edm:ProvidedCHO rdf:about="id1">
        <some:crap/>
    </edm:ProvidedCHO>
    <edm:WebResource rdf:about="some/random/url"> 
        <some:crap/>
    </edm:WebResource>
</rdf:RDF>

<!-- ITEM N2 -->
<rdf:RDF>
     <dc:item rdf:about="some.concat.string"/>
     <ore:Aggregation rdf:about="id1">
     <etc/>

我已经 运行 处理过几次这种情况,我想出的最佳解决方案涉及集合操作。

在某种程度上这是有道理的,因为您想要每个 ore:Aggregation 之后的所有节点直到下一个 ore:Aggregation。

换句话说,您需要以下节点中的 所有 除了 用于下一个 ore:Aggregation 它后面的所有内容。

值得庆幸的是,在 XSLT 2.0 中,我们设置了操作运算符,因此我们不必像在 XSLT 1.0 中那样跳过复杂的环节。

试试这个 XPATH

following-sibling::node() except 
  (following-sibling::ore:Aggregation | 
   following-sibling::ore:Aggregation/following-sibling::node())

这应该会为您提供您期望的节点集。

每次您尝试将平面元素列表排序为某种结果结构时,此模式通常都有效。例如,我遇到的问题是找到所有具有特定属性的元素,然后将它们组合成一个。

所以,一般的解决方案是(在伪代码中)

如果我们可以同时指定终端标签和它的后续兄弟,这会更简单一些,但这实际上读起来还不错。

following-sibling::node() except 
  (following-sibling::{next-node-selection-criteria} | 
   following-sibling::{next-node-selection-criteria}/following-sibling::node())

在 XSLT 2.0 中,这看起来像是 xsl:for-each-group 的工作(参见 http://www.xml.com/pub/a/2003/11/05/tr.html)。特别是,将其与 group-starting-with

一起使用
 <xsl:for-each-group select="*" group-starting-with="ore:Aggregation">

这将在定位到父 rdf:RDF 元素时完成,并将所有子元素分组,ore:Aggregration 是每个组的开始。 xsl:for-each-group 中的代码会针对每个 ore:Aggregation 元素调用一次,然后您可以使用 current-group() 函数访问组中的所有元素。

初学者试试这个 XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:rdf="rdf" xmlns:ore="ore" xmlns:dc="dc">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="rdf:RDF">
        <xsl:for-each-group select="*" group-starting-with="ore:Aggregation">
            <rdf:RDF xmlns:edm="edm" xmlns:ore="ore" xmlns:some="some">
                <dc:item rdf:about="{concat('item', position())}" />
                <xsl:apply-templates select="current-group()" />
            </rdf:RDF>
        </xsl:for-each-group>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet> 

请注意,此生成的输出 XML 格式不正确,因为它缺少单个根元素。如果添加一个会更好,不仅仅是为了使其格式正确,而且名称空间声明也会放在一个地方:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:rdf="rdf" xmlns:ore="ore" xmlns:dc="dc">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

    <xsl:template match="rdf:RDF">
        <rdf:root xmlns:edm="edm" xmlns:ore="ore" xmlns:some="some">
            <xsl:for-each-group select="*" group-starting-with="ore:Aggregation">
                <rdf:RDF>
                    <dc:item rdf:about="{concat('item', position())}" />
                    <xsl:apply-templates select="current-group()" />
                </rdf:RDF>
            </xsl:for-each-group>
        </rdf:root>
    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet> 

另请注意 Attribute Value Templates 在创建 rdf:about 时的使用,这进一步减少了所需的代码量。