我有一个xml文档,在层次结构的内部有分隔符。
<A>
<B>
<C id='1'/>
<separator/>
<C id='2'/>
</B>
<B>
<C id='3'/>
<separator/>
</B>
<B>
<C id='4'/>
</B>
</A>
我想向上移动分隔符,保持元素有序。所以期望的输出是
<A>
<B>
<C id='1'/>
</B>
</A>
<separator/>
<A>
<B>
<C id='2'/>
</B>
<B>
<C id='3'/>
</B>
</A>
<separator/>
<A>
<B>
<C id='4'/>
</B>
</A>
如何只使用xslt 1.0?可以在没有for-each
的情况下使用模板匹配吗?
更新: 我实际上得到了4个不同级别的一般性的精彩答案,谢谢你们。
答案 0 :(得分:4)
此转化:
<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:key name="kFollowing" match="C"
use="generate-id(preceding::separator[1])"/>
<xsl:template match="/">
<xsl:apply-templates select="*/*/separator"/>
</xsl:template>
<xsl:template match="separator" name="commonSep">
<separator/>
<xsl:call-template name="genAncestors">
<xsl:with-param name="pAncs" select="ancestor::*"/>
<xsl:with-param name="pLeaves"
select="key('kFollowing', generate-id())"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="separator[not(preceding::separator)]">
<xsl:call-template name="genAncestors">
<xsl:with-param name="pAncs" select="ancestor::*"/>
<xsl:with-param name="pLeaves"
select="key('kFollowing', '')"/>
</xsl:call-template>
<xsl:call-template name="commonSep"/>
</xsl:template>
<xsl:template name="genAncestors">
<xsl:param name="pAncs" select="ancestor::*"/>
<xsl:param name="pLeaves" select="."/>
<xsl:choose>
<xsl:when test="not($pAncs[2])">
<xsl:apply-templates select="$pLeaves" mode="gen"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$pAncs[1]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:call-template name="genAncestors">
<xsl:with-param name="pAncs" select="$pAncs[position()>1]"/>
<xsl:with-param name="pLeaves" select="$pLeaves"/>
</xsl:call-template>
</xsl:copy>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="C" mode="gen">
<xsl:variable name="vCur" select="."/>
<xsl:for-each select="..">
<xsl:copy>
<xsl:copy-of select="@*|$vCur"/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档:
<A>
<B>
<C id='1'/>
<separator/>
<C id='2'/>
</B>
<B>
<C id='3'/>
<separator/>
</B>
<B>
<C id='4'/>
</B>
</A>
生成想要的正确结果:
<A>
<B>
<C id="1"/>
</B>
</A>
<separator/>
<A>
<B>
<C id="2"/>
</B>
<B>
<C id="3"/>
</B>
</A>
<separator/>
<A>
<B>
<C id="4"/>
</B>
</A>
答案 1 :(得分:2)
此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kCByFollSep" match="C"
use="generate-id(following::separator[1])"/>
<xsl:template match="A">
<xsl:for-each select="B/separator|B[last()]/*[last()]">
<A>
<xsl:apply-templates
select="key('kCByFollSep',
substring(generate-id(),
1 div boolean(self::separator)))"/>
</A>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="C">
<B>
<xsl:copy-of select="."/>
</B>
</xsl:template>
</xsl:stylesheet>
输出:
<A>
<B>
<C id="1" />
</B>
</A>
<separator />
<A>
<B>
<C id="2" />
</B>
<B>
<C id="3" />
</B>
</A>
<separator />
<A>
<B>
<C id="4" />
</B>
</A>
注意:按照separator
进行分组,在不关注C
的情况下为有效separator
添加最后一个第三级元素。
编辑:更多拉式,更多架构无关,此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kCByFollSep" match="C"
use="generate-id(following::separator[1])"/>
<xsl:template match="text()"/>
<xsl:template match="separator|*[not(*)][not(following::*)]">
<A>
<xsl:apply-templates
select="key('kCByFollSep',
substring(generate-id(),
1 div boolean(self::separator)))"
mode="output"/>
</A>
<xsl:copy-of select="self::separator"/>
</xsl:template>
<xsl:template match="C" mode="output">
<B>
<xsl:copy-of select="."/>
</B>
</xsl:template>
</xsl:stylesheet>
编辑2 :更一般的解决方案(我不信任的一件事,ja!)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pRemains"/>
<xsl:copy>
<xsl:apply-templates select="node()[descendant-or-self::node()
[not(self::separator)]
[count(following::separator)
= $pRemains]
][1]|@*">
<xsl:with-param name="pRemains" select="$pRemains"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()
[descendant-or-self::node()
[not(self::separator)]
[count(following::separator)
= $pRemains]
][1]">
<xsl:with-param name="pRemains" select="$pRemains"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vRemains" select="last()-position()"/>
<xsl:for-each select="$vCurrent">
<xsl:copy>
<xsl:apply-templates
select="node()[descendant::node()
[not(self::separator)]
[count(following::separator)
= $vRemains]
][1]">
<xsl:with-param name="pRemains" select="$vRemains"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>
注意:主要是细粒度的遍历。楼层次结构规则(在本例中为根元素)复制自身和分隔符(最后一组的虚拟节点,没有跟随分隔符)传递剩余分隔符以处理具有足够后续分隔符的第一个子进程。修改后的细粒度遍历标识规则,复制自身并再次处理第一个子节点并跟随兄弟节点使用足够的后续分隔符进行处理。最后,一个分隔符规则打破了这个过程。
编辑3 :其他更通用的解决方案,现在使用递归标识规则
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
use="generate-id((descendant::separator|following::separator)[1])"/>
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pGroup"/>
<xsl:copy>
<xsl:apply-templates
select="node()[descendant-or-self::node()[count(.|$pGroup)
= count($pGroup)]]|@*">
<xsl:with-param name="pGroup" select="$pGroup"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vGroup"
select="key('kNodeByFolSep',generate-id(self::separator))"/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pGroup" select="$vGroup"/>
</xsl:call-template>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>
编辑4 :现在和以前一样,但是使用密钥测试而不是节点集交集。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="kNodeByFolSep" match="node()[not(self::separator)]"
use="concat(generate-id(),'+',
generate-id((descendant::separator|
following::separator)[1]))"/>
<xsl:template match="node()|@*" name="identity">
<xsl:param name="pSeparator"/>
<xsl:copy>
<xsl:apply-templates
select="@*|node()[descendant-or-self::node()
[key('kNodeByFolSep',
concat(generate-id(),
'+',
$pSeparator))]]">
<xsl:with-param name="pSeparator" select="$pSeparator"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="descendant::separator|node()[last()]">
<xsl:variable name="vSeparator"
select="generate-id(self::separator)"/>
<xsl:for-each select="$vCurrent">
<xsl:call-template name="identity">
<xsl:with-param name="pSeparator" select="$vSeparator"/>
</xsl:call-template>
</xsl:for-each>
<xsl:copy-of select="self::separator"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="separator"/>
</xsl:stylesheet>