选择有条件的节点
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
时的使用,这进一步减少了所需的代码量。
我正在尝试转换 XML 个文件,但我被阻止了。这个想法是聚合从 <ore:Aggregation>
节点到下一个节点的每个元素。这是某种分项。但是每次创建 dc:item
.
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
时的使用,这进一步减少了所需的代码量。