如何使用XSLT1按标签折叠一组选定的(相邻)标签? [英] How to fold by a tag a group of selected (neighbor) tags with XSLT1?
问题描述
我有一组顺序节点,这些顺序节点必须包含在一个新元素中.示例:
I have a set of sequential nodes that must be enclosed into a new element. Example:
<root>
<c>cccc</c>
<a gr="g1">aaaa</a> <b gr="g1">1111</b>
<a gr="g2">bbbb</a> <b gr="g2">2222</b>
</root>
必须用fold
标记括起来,导致(在XSLT之后):
that must be enclosed by fold
tags, resulting (after XSLT) in:
<root>
<c>cccc</c>
<fold><a gr="g1">aaaa</a> <b gr="g1">1111</b></fold>
<fold><a gr="g2">bbbb</a> <b gr="g2">2222</b></fold>
</root>
因此,我有一个分组标签"(@gr
),但无法想象如何产生正确的折叠标签.
So, I have a "label for grouping" (@gr
) but not imagine how to produce correct fold tags.
我正在尝试使用此问题或这另一个 ...但是我有一个分组标签",因此我知道我的解决方案不需要使用key()
函数.
I am trying to use the clues of this question, or this other one... But I have a "label for grouping", so I understand that my solution not needs the use of key()
function.
我的一般解决方案是:
<xsl:template match="/">
<root>
<xsl:copy-of select="root/c"/>
<fold><xsl:for-each select="//*[@gr='g1']">
<xsl:copy-of select="."/>
</xsl:for-each></fold>
<fold><xsl:for-each select="//*[@gr='g2']">
<xsl:copy-of select="."/>
</xsl:for-each></fold>
</root>
</xsl:template>
我需要一个通用的解决方案(!),通过所有@gr循环并应对(标识)所有没有@gr的上下文...也许使用 identity transform .
I need a general solution (!), looping by all @gr and coping (identity) all context that not have @gr... perhaps using identity transform.
另一个(未来)的问题是通过折叠的方式递归执行此操作.
Another (future) problem is to do this recursively, with fold of foldings.
推荐答案
在XSLT 1.0中,处理此类问题的标准技术称为Muenchian分组,它涉及使用 key 来定义如何将节点分组以及使用generate-id
技巧仅提取每个组中的第一个节点作为整个组的代理.
In XSLT 1.0 the standard technique to handle this sort of thing is called Muenchian grouping, and involves the use of a key that defines how the nodes should be grouped and a trick using generate-id
to extract just the first node in each group as a proxy for the group as a whole.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*" />
<xsl:output indent="yes" />
<xsl:key name="elementsByGr" match="*[@gr]" use="@gr" />
<xsl:template match="@*|node()" name="identity">
<xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
</xsl:template>
<!-- match the first element with each @gr value -->
<xsl:template match="*[@gr][generate-id() =
generate-id(key('elementsByGr', @gr)[1])]" priority="2">
<fold>
<xsl:for-each select="key('elementsByGr', @gr)">
<xsl:call-template name="identity" />
</xsl:for-each>
</fold>
</xsl:template>
<!-- ignore subsequent ones in template matching, they're handled within
the first element template -->
<xsl:template match="*[@gr]" priority="1" />
</xsl:stylesheet>
这可以实现您想要的分组,但是就像您的非一般性解决方案一样,它不会保留缩进和a
和b
元素之间的空格文本节点,即会给您>
This achieves the grouping you're after, but just like your non-general solution it doesn't preserve the indentation and the whitespace text nodes between the a
and b
elements, i.e. it will give you
<root>
<c>cccc</c>
<fold>
<a gr="g1">aaaa</a>
<b gr="g1">1111</b>
</fold>
<fold>
<a gr="g2">bbbb</a>
<b gr="g2">2222</b>
</fold>
</root>
请注意,如果您能够使用XSLT 2.0,则整个过程将变成一个for-each-group
:
<xsl:template match="root">
<xsl:for-each-group select="*" group-adjacent="@gr">
<xsl:choose>
<!-- wrap each group in a fold -->
<xsl:when test="@gr">
<fold><xsl:copy-of select="current-group()" /></fold>
</xsl:when>
<!-- or just copy as-is for elements that don't have a @gr -->
<xsl:otherwise>
<xsl:copy-of select="current-group()" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
这篇关于如何使用XSLT1按标签折叠一组选定的(相邻)标签?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!