此问题有3种情况:
第一种可能性: 输入:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Orange</color>
<year>2012</year>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Red</color>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Blue</color>
<condition>good</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
预期产出:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Blue</color>
<year>2012</year>
<condition>good</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
第二种可能性: 输入:
<root>
<node id="N1">
<car id="1">
<bmw id="i" action="change">
<attribute>
<color>Blue</color>
<owner>a</owner>
</attribute>
</bmw>
<bmw id="i" action="change">
<attribute>
<color>Yellow</color>
<status>avaailable</status>
</attribute>
</bmw>
</car>
</node>
</root>
预期产出:
<root>
<node id="N1">
<car id="1">
<bmw id="i" action="change">
<attribute>
<color>Yellow</color>
<owner>a</owner>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
第三种情景:
<root>
<node id="N1">
<car id="1">
<bmw id="j" action="delete">
<attribute>
<color>Blue</color>
<year>2000</year>
</attribute>
</bmw>
<bmw id="j" action="delete">
<attribute>
<color>Pink</color>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
预期产出:
<root>
<node id="N1">
<car id="1">
<bmw id="j" action="delete">
<attribute>
<color>Pink</color>
<year>2000</year>
<status>available</status>
</attribute>
</bmw>
</car>
</node>
</root>
关于第二和第三种情况的说明:
我希望解释清楚。
请告诉我有关此问题的XSLT解决方案。 谢谢。
亲切的问候, 约翰答案 0 :(得分:1)
与我给你的here相比,这是一种不同风味的解决方案。
我认为值得一步一步走。我假设@action
出现在逻辑顺序中 - 首先create
,然后是change
,最后是remove
。同一@action
可能会出现多次,但不会是随机的。现在我们准备好了解主要逻辑:
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
我们宣布身份转换,然后在几个地方拦截它。我们只停留在具有相同@id
,父@id
和@action
的节点的唯一匹配位置:
<xsl:template match="node/*/*[a:is-primary(.)]" priority="1">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="attribute" mode="consolidate-most-recent"/>
</xsl:copy>
</xsl:template>
我们忽略了“重复”:
<xsl:template match="node/*/*[not(a:is-primary(.))]"/>
并忽略create
跟随change
以及所有create
和change
后跟remove
。{/ p>
<xsl:template match="node/*/*[@action = 'change'][a:preceded-by(., 'create')]" priority="2"/>
<xsl:template match="node/*/*[@action = 'create' or action='change'][a:followed-by(., 'remove')]" priority="2"/>
当唯一的@action
没有跟随另一个会让我们忽略它的@action
被捕获时,我们做了一件简单的事情 - 收集具有相同@id
个忽略的元素的所有属性@action
并使用他们最“最近”的值(文档顺序中最后出现的值)。
<xsl:template match="attribute" mode="consolidate-most-recent">
<xsl:copy>
<xsl:for-each-group
select="/root/node/*/*[a:matches(current()/parent::*, ., 'any')]/attribute/*"
group-by="local-name()">
<!-- take the last in the document order -->
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
就是这样。现在让我们看一下使其工作的函数:
我们有一个key
来简化查找
<xsl:key name="entity" match="/root/node/*/*" use="concat(parent::*/@id, '_', @id, '_', @action)"/>
检查节点是否是唯一出现的函数(我们可以将它直接添加到模板match
谓词中,但是因为我们开始使用这些函数,所以让我们保持相同):
<xsl:function name="a:is-primary" as="xs:boolean">
<xsl:param name="ctx"/>
<!-- need to establish "focus"(context) for the key() function to work -->
<xsl:for-each select="$ctx">
<xsl:sequence select="generate-id($ctx) = generate-id(key('entity', concat($ctx/parent::*/@id, '_', $ctx/@id, '_', $ctx/@action))[1])"/>
</xsl:for-each>
</xsl:function>
一个matches
函数,它将为我们进行所有类型的比较(同样,可以将它全部放在谓词中,但这样我们将在真实模板中保持良好和干净):
<xsl:function name="a:matches" as="xs:boolean">
<xsl:param name="src"/>
<xsl:param name="target"/>
<!-- can be one of the following:
'any' - only match the @id(s) and ignore @action
'same' - match by @id(s) and expect $src/@action to match $target/@action
a certain value - match by @id(s) and expect @action to match this value
-->
<xsl:param name="action"/>
<xsl:value-of select="
($src/local-name() = $target/local-name()) and
($src/parent::*/@id = $target/parent::*/@id) and
($src/@id = $target/@id) and
(if ($action = 'any')
then true()
else if ($action = 'same')
then ($target/@action = $src/@action)
else ($target/@action = $action))"/>
</xsl:function>
preceded-by
和followed-by
语法糖位于“原始”matches
函数之上:
<xsl:function name="a:preceded-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/preceding::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:followed-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/following::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<强>概要强>
这是一个完整的转变:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:a="http://a.com">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="entity" match="/root/node/*/*" use="concat(parent::*/@id, '_', @id, '_', @action)"/>
<xsl:function name="a:is-primary" as="xs:boolean">
<xsl:param name="ctx"/>
<!-- need to establish "focus"(context) for the key() function to work -->
<xsl:for-each select="$ctx">
<xsl:sequence select="generate-id($ctx) = generate-id(key('entity', concat($ctx/parent::*/@id, '_', $ctx/@id, '_', $ctx/@action))[1])"/>
</xsl:for-each>
</xsl:function>
<xsl:function name="a:preceded-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/preceding::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:followed-by" as="xs:boolean">
<xsl:param name="ctx"/>
<xsl:param name="action"/>
<xsl:value-of select="count($ctx/following::*[a:matches($ctx, ., $action)]) > 0"/>
</xsl:function>
<xsl:function name="a:matches" as="xs:boolean">
<xsl:param name="src"/>
<xsl:param name="target"/>
<!-- can be one of the following:
'any' - only match the @id(s) and ignore @action
'same' - match by @id(s) and expect $src/@action to match $target/@action
a certain value - match by @id(s) and expect @action to match this value
-->
<xsl:param name="action"/>
<xsl:value-of select="
($src/local-name() = $target/local-name()) and
($src/parent::*/@id = $target/parent::*/@id) and
($src/@id = $target/@id) and
(if ($action = 'any')
then true()
else if ($action = 'same')
then ($target/@action = $src/@action)
else ($target/@action = $action))"/>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node/*/*[a:is-primary(.)]" priority="1">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="attribute" mode="consolidate-most-recent"/>
</xsl:copy>
</xsl:template>
<xsl:template match="attribute" mode="consolidate-most-recent">
<xsl:copy>
<xsl:for-each-group
select="/root/node/*/*[a:matches(current()/parent::*, ., 'any')]/attribute/*"
group-by="local-name()">
<!-- take the last in the document order -->
<xsl:apply-templates select="current-group()[last()]"/>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="node/*/*[not(a:is-primary(.))]"/>
<!-- assume a remove is never followed by a change or create -->
<xsl:template match="node/*/*[@action = 'change'][a:preceded-by(., 'create')]" priority="2"/>
<xsl:template match="node/*/*[@action = 'create' or action='change'][a:followed-by(., 'remove')]" priority="2"/>
</xsl:stylesheet>
应用于文档时:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="create">
<attribute>
<color>Orange</color>
<year>2012</year>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Red</color>
<something>!!</something>
</attribute>
</orange>
<orange id="x" action="change">
<attribute>
<color>Blue</color>
<condition>good</condition>
</attribute>
</orange>
<orange id="x" action="remove">
<attribute>
<condition>awesome</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
产生以下结果:
<root>
<node id="N1">
<fruit id="1" action="aaa">
<orange id="x" action="remove">
<attribute>
<color>Blue</color>
<year>2012</year>
<something>!!</something>
<condition>awesome</condition>
</attribute>
</orange>
</fruit>
</node>
</root>
我希望它很清楚。您可以扩展这个概念,并为自己构建一个很好的可重用函数库,然后将其用作简单谓词以某种方式合并节点。不太可能是最有效的工作方式,但至少是一种表达解决方案的简洁方式。