0

この問題には 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>

2番目の可能性: 入力:

<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>

3 番目のシナリオ:

<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>

2 番目と 3 番目のシナリオの説明:

  • 「action=change」を持つ 2 つ以上のノードは、「action=change」を持つ 1 つのノードにマージされます
  • 「action=delete」を持つ 2 つ以上のノードは、「action=delete」を持つ 1 つのノードにマージされます
  • マージ中は、最後のノードの値のみを保持して更新し、最初のノードを保持して、新しいノードを追加します。

説明が明確であることを願っています。

この問題の XSLT ソリューションについて教えてください。ありがとうございました。

敬具、ジョン

4

1 に答える 1

1

これは、私がここで提供したものとは異なるフレーバーのソリューションです。

一歩一歩進んでいく価値があると思いました。私は s が最初、次、最後@actionという論理的な順序で現れると仮定しました。同じことが複数回発生する可能性がありますが、ランダムではありません。これで、メイン ロジックを確認する準備が整いました。createchangeremove@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(.))]"/>

また、すべての とそれに続くcreatea だけでなく、 a が続くも無視します。changecreatechangeremove

<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-byand構文シュガーがあります:followed-bymatches

<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>

私はそれが明確であることを願っています。この概念を拡張して、ノードを何らかの方法でマージする単純な述語として使用する再利用可能な関数の優れたライブラリを自分で構築できます。仕事をするのに最も効果的な方法である可能性は低いですが、少なくとも解決策を表現するためのきれいな方法です。

于 2012-05-07T18:59:08.187 に答える