これは、私がここで提供したものとは異なるフレーバーのソリューションです。
一歩一歩進んでいく価値があると思いました。私は s が最初、次、最後@action
という論理的な順序で現れると仮定しました。同じことが複数回発生する可能性がありますが、ランダムではありません。これで、メイン ロジックを確認する準備が整いました。create
change
remove
@action
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
恒等変換を宣言し、いくつかの場所でインターセプトします。@id
同じ、parent @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
a だけでなく、 a が続くも無視します。change
create
change
remove
<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
and構文シュガーがあります: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>
私はそれが明確であることを願っています。この概念を拡張して、ノードを何らかの方法でマージする単純な述語として使用する再利用可能な関数の優れたライブラリを自分で構築できます。仕事をするのに最も効果的な方法である可能性は低いですが、少なくとも解決策を表現するためのきれいな方法です。