我真的认为这是不可能通过XSLT解决的,所以我将不得不用JS做一些事情,或者干脆不实现。但在放弃之前,我当然必须在这里发帖,看看我是否错了,XSLT是否可以做这种逻辑。
编辑:现在我开始看到这是可能的,越来越接近
第2版:我必须更正提供的XML代码中的一个错误。该解决方案应该能够处理具有相同值的多个节点。
其概念是,我需要重新格式化代码,包括反映不同值总数的空标记。很难解释,看代码更容易理解。
初始XML代码
<data>
<prot seq="AAA">
<node num="4">1345</node>
</prot>
<prot seq="BBB">
<node num="7">6666</node>
</prot>
<prot seq="CCC">
<node num="10">3e33</node>
</prot>
<prot seq="DDD">
<node num="4">1345</node>
</prot>
<prot seq="EEE">
<node num="10">3e33</node>
</prot>
</data>
并且希望输出
<root>
<prot seq="AAA">
<node num="4">1345</node><node num="7">-</node><node num="10">-</node>
</prot>
<prot seq="BBB">
<node num="4">-</node><node num="7">6666</node><node num="10">-</node>
</prot>
<prot seq="CCC">
<node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
</prot>
<prot seq="DDD">
<node num="4">1345</node><node num="7">-</node><node num="10">-</node>
</prot>
<prot seq="EEE">
<node num="4">-</node><node num="7">-</node><node num="10">3e33</node>
</prot>
</root>
知道吗?
谢谢!
编辑3:从Dimitri解决方案,我最终得到了这个简化的解决方案
<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="kNodeByNum" match="/data/prot/node" use="@num"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="/data/prot"/>
</root>
</xsl:template>
<xsl:template match="/data/prot">
<xsl:variable name="current_num" select="node/@num"/>
<xsl:variable name="current_value" select="node"/>
<prot seq="{@seq}">
<xsl:for-each select="/data/prot/node[
generate-id()
=
generate-id(key('kNodeByNum', @num)[1])
]">
<xsl:choose>
<xsl:when test="@num = $current_num">
<node num="{@num}"><xsl:value-of select="$current_value"/></node>
</xsl:when>
<xsl:otherwise>
<node num="{@num}">-</node>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</prot>
</xsl:template>
</xsl:stylesheet>
然而,这段代码也无法处理firefox中的节点数量,并且会进入一个永远类似的循环(我必须强制关闭firefox)。
但我认为这与节点数量无关,而是代码(?)
我还没有在FireFox中测试过,但它在Xalan和Saxon中有效。。。
编辑:在FireFox8.0中进行测试,并在样式表中添加注释
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!--Identity Template - Anything not matched by another template will be
copied without changes.-->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!--Change the name of the "data" element to "root" element.-->
<xsl:template match="data">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="prot">
<!--Save the current "num" attribute from the child "node" element.-->
<xsl:variable name="vCurrNum" select="node/@num"/>
<prot>
<!--This will pass all attributes of "prot" through the identity template.-->
<xsl:apply-templates select="@*"/>
<!--This will process the the current "node" element and also any "node" elements in other
"prot" elements that don't have the same "num" attribute as the current "node".-->
<xsl:apply-templates select="node|/*/prot/node[@num != $vCurrNum]">
<!--This passes the current "num" attribute to the "node" template that gets matched below.-->
<xsl:with-param name="pNum" select="$vCurrNum"/>
<!--This will sort the "node" elements based on their "num" attribute.-->
<xsl:sort select="@num" data-type="number" order="ascending"/>
</xsl:apply-templates>
</prot>
</xsl:template>
<xsl:template match="node">
<!--This is the "num" attribute value passed in the xsl:apply-templates above.-->
<xsl:param name="pNum"/>
<!--This will pass all attributes of "node" through the identity template.-->
<xsl:choose>
<!--If the current "num" attribute is the same as the "num" attribute passed to
the xsl:apply-templates ($pNum), output the value.-->
<xsl:when test="@num = $pNum">
<node>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates/>
</node>
</xsl:when>
<!--This ensures we only get one of the other "node" elements. (The first "node" with that "num".)-->
<xsl:when test="not(parent::prot[preceding-sibling::prot[node[@num = current()/@num]]])">
<node>
<xsl:apply-templates select="@*"/>
<xsl:text>-</xsl:text>
</node>
</xsl:when>
<!--Ignore any other repeated node/num combinations.-->
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
使用XML输入,它生成所需的输出:
<root>
<prot seq="AAA">
<node num="4">1345</node>
<node num="7">-</node>
<node num="10">-</node>
</prot>
<prot seq="BBB">
<node num="4">-</node>
<node num="7">6666</node>
<node num="10">-</node>
</prot>
<prot seq="CCC">
<node num="4">-</node>
<node num="7">-</node>
<node num="10">3e33</node>
</prot>
</root>
这里有一个简短而简单的(无xsl:choose
、xsl:when
和xsl:otherwise
)解决方案:
<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:template match="node()|@*">
<xsl:param name="pParent" select="/.."/>
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:with-param name="pParent" select="$pParent"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="prot">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="../prot/node">
<xsl:with-param name="pParent" select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="node/text()">
<xsl:param name="pParent" select="/.."/>
<xsl:variable name="vSameParent" select=
"boolean(not((../.. | $pParent)[2]))"/>
<xsl:value-of select=
"concat(substring('-', 1 +$vSameParent),
self::node()[$vSameParent]
)
"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于所提供的XML文档时:
<data>
<prot seq="AAA">
<node num="4">1345</node>
</prot>
<prot seq="BBB">
<node num="7">6666</node>
</prot>
<prot seq="CCC">
<node num="10">3e33</node>
</prot>
</data>
生成所需的正确结果:
<data>
<prot seq="AAA">
<node num="4">1345</node>
<node num="7">-</node>
<node num="10">-</node>
</prot>
<prot seq="BBB">
<node num="4">-</node>
<node num="7">6666</node>
<node num="10">-</node>
</prot>
<prot seq="CCC">
<node num="4">-</node>
<node num="7">-</node>
<node num="10">3e33</node>
</prot>
</data>
解释:
我们使用并覆盖身份规则的修改版本——一个接受并传递名为
$pParent
的参数的版本。参数
$pParent
包含发布xsl:apply-templates
的node
元素,其中一部分是当前节点的处理。我们有两个覆盖身份规则的模板。第一个覆盖模板匹配任何
prot
元素。它几乎与标识规则相同,但它为$pParent
参数设置了一个有意义的值(该节点本身)。第二个覆盖模板匹配作为任何
node
元素的子元素的任何文本节点。这里,根据$pParent
的值是否标识匹配的文本节点的祖父母,我们分别输出文本节点的值或仅输出"-"
。决定输出什么是在不使用任何显式条件指令的情况下完成的。相反,我们使用带有两个参数的XPath
concat()
函数,其中一个参数恰好是非空字符串。为了确保这一特性,我们使用布尔变量$vSameParent
,其指定方式是,当匹配文本节点的父节点与$pParent
中包含的节点相同时,其值恰好为true()
。最后,我们使用这样一个事实,即当用作数字时,true()
的布尔值转换为1
,false()
的布尔值转化为0
。
更新:这是一个新的解决方案——针对OP问题修改的:
<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="kNodeByNum" match="node" use="@num"/>
<xsl:template match="node()|@*">
<xsl:param name="pNum"/>
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:with-param name="pNum" select="$pNum"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="prot">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select=
"../prot/node
[generate-id()
=
generate-id(key('kNodeByNum', @num)[1])
]
">
<xsl:with-param name="pNum" select="node/@num"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="node/text()">
<xsl:param name="pNum" select="/.."/>
<xsl:variable name="vSameNum" select=
"../@num = $pNum"/>
<xsl:value-of select=
"concat(substring('-', 1 +$vSameNum),
self::node()[$vSameNum]
)
"/>
</xsl:template>
</xsl:stylesheet>
当此转换应用于所提供的XML文档时:
<data>
<prot seq="AAA">
<node num="4">1345</node>
</prot>
<prot seq="BBB">
<node num="7">6666</node>
</prot>
<prot seq="CCC">
<node num="10">3e33</node>
</prot>
<prot seq="DDD">
<node num="4">1345</node>
</prot>
<prot seq="EEE">
<node num="10">3e33</node>
</prot>
</data>
生成新的所需结果:
<data>
<prot seq="AAA">
<node num="4">1345</node>
<node num="7">-</node>
<node num="10">-</node>
</prot>
<prot seq="BBB">
<node num="4">-</node>
<node num="7">6666</node>
<node num="10">-</node>
</prot>
<prot seq="CCC">
<node num="4">-</node>
<node num="7">-</node>
<node num="10">3e33</node>
</prot>
<prot seq="DDD">
<node num="4">1345</node>
<node num="7">-</node>
<node num="10">-</node>
</prot>
<prot seq="EEE">
<node num="4">-</node>
<node num="7">-</node>
<node num="10">3e33</node>
</prot>
</data>
解释:与原始问题的主要思想相同,添加了Muenchian分组。