1

私はXSLTの基本に精通していますが、理解できない奇妙な状況に遭遇しました。ご不便をおかけして申し訳ございませんが、ご協力いただければ幸いです。

制御できないソフトウェア製品によって生成されたXMLの変換を使用しています。この製品は、次のようなデータをエクスポートします。

<header>
   <data>
   </data>
</header>
<transaction>
    <B1_PG1 ts='1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76'><data></data></B1_PG2>
    <B2_PG1 ts='1139977698718.77'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78'><data></data></B2_PG2>
    <B2_PG1 ts='1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81'><data></data></B3_PG1>
</transaction>

ソフトウェア製品がデータのページを受信した順序でエクスポートしている場合。それらのページを取得して、別のシステムに処理するためにカスタム順序で並べ替える必要があります。そこで、カスタムの並べ替え順序を定義するために、次のようなルックアップドキュメントを作成しました。

(PageSequences.xml)

<pages>
    <page id="B2_PG1" sequence="1000" />
    <page id="B2_PG2" sequence="1010" />
    <page id="B3_PG1" sequence="2000" />
    <page id="B1_PG1" sequence="3000" />
    <page id="B1_PG2" sequence="3010" />
</pages>

次に、要素名に基づいてそのシーケンスを検索し、それをタイムスタンプと連結し、次のXSLTを使用して要素に属性を挿入します。

<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:variable name='page-seqs' select='document("PageSequences.xml")/pages/page'/>

    <xsl:template match="node()|@*"> 
        <xsl:copy> 
            <xsl:apply-templates select="@*|node()">
            </xsl:apply-templates>
        </xsl:copy> 
    </xsl:template> 

    <xsl:template match="transaction">
        <transaction>

            <xsl:for-each select="child::node()">
                <xsl:variable name='localname' select='local-name()'/>
                <xsl:copy>
                    <xsl:attribute name="sequence">
                        <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/>
                    </xsl:attribute>
                    <xsl:apply-templates select="@*|node()">
                        <xsl:sort select="@sequence" />
                    </xsl:apply-templates>
                </xsl:copy> 
            </xsl:for-each>
        </transaction>
        </xsl:template> 

</xsl:stylesheet> 

私が遭遇している問題は、タグが機能していないように見えることです。私は次のことを期待します:

<transaction>
    <B2_PG1 ts='1139977698718.77' sequence='1000-1139977698718.77'><data></data></B2_PG1>
    <B2_PG1 ts='1139977698718.79' sequence='1000-1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78' sequence='1010-1139977698718.78'><data></data></B2_PG2>
    <B2_PG2 ts='1139977698718.80' sequence='1010-1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81' sequence='2000-1139977698718.81'><data></data></B3_PG1>
    <B1_PG1 ts='1139977698718.75' sequence='3000-1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76' sequence='3010-1139977698718.76'><data></data></B1_PG2>
</transaction>

しかし、私は得ています:

<transaction>
    <B1_PG1 ts='1139977698718.75' sequence='3000-1139977698718.75'><data></data></B1_PG1>
    <B1_PG2 ts='1139977698718.76' sequence='3010-1139977698718.76'><data></data></B1_PG2>
    <B2_PG1 ts='1139977698718.77' sequence='1000-1139977698718.77'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.78' sequence='1010-1139977698718.78'><data></data></B2_PG2>
    <B2_PG1 ts='1139977698718.79' sequence='1000-1139977698718.79'><data></data></B2_PG1>
    <B2_PG2 ts='1139977698718.80' sequence='1010-1139977698718.80'><data></data></B2_PG2>
    <B3_PG1 ts='1139977698718.81' sequence='2000-1139977698718.81'><data></data></B3_PG1>
</transaction>

また、私が間違った方法でこれに取り組んでいると思われる場合はお知らせください。変換を可能な限り移植可能に保つために、java / c#/ perl /etc...の使用を避けようとしています。また、パフォーマンス上の理由から、2つの変換を実行することは避けたいと思います。ありがとう!

4

2 に答える 2

1

作成した属性は、結果ツリーに配置する新しく構築された要素には存在しますが、並べ替えているソース ツリーの要素には存在しません。

もう 1 つの問題は、transaction 要素の子ではなく孫をソートしていることです。

あなたが望むのはこれだと思います:

           <xsl:for-each select="child::node()">
              <xsl:sort select="concat($page-seqs[@id=local-name(current())]/@sequence, '-', @ts)"/>
                <xsl:variable name='localname' select='local-name()'/>
                <xsl:copy>
                    <xsl:attribute name="sequence">
                        <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/>
                    </xsl:attribute>
                    <xsl:apply-templates select="@*|node()"/>
                </xsl:copy> 
            </xsl:for-each>

計算を繰り返さないようにする唯一の方法 (xsl:sort で 1 回、出力属性を生成するために 1 回) は、2 つのパスを実行することです。それを行う方法は XSLT 1.0 と 2.0 の間で異なります。2 パスのアプローチは非常に実現可能ですが、あなたの場合は、計算を繰り返すシングル パスの方が効率的であると思われます (調べるには両方を測定する必要があります)。

于 2012-07-15T08:18:48.397 に答える
0

あなたが抱えている問題は、このコードブロック内にあります

<xsl:copy> 
   <xsl:attribute name="sequence"> 
      <xsl:value-of select='$page-seqs[@id=$localname]/@sequence'/>-<xsl:value-of select='@ts'/> 
   </xsl:attribute> 
   <xsl:apply-templates select="@*|node()"> 
      <xsl:sort select="@sequence" /> 
   </xsl:apply-templates> 
</xsl:copy>  

特に、apply-templatesです。まず、この段階では、「B-PG」要素をすでにコピーして出力しており、apply-templatesが実行しているのはその子ノードの処理だけです。次に、並べ替えは入力ドキュメントでのみ機能し、出力ドキュメントに追加した追加の属性では機能しません。

次にできることは、トランザクションの子ノードをコピーした結果を変数に入れてから、その変数を並べ替えて反復処理することです。これは、同じXSLTドキュメント内の「2パス変換」になります。ただし、この場合は必要ありません。すべてのトランザクション子ノードを単純に照合し、ソートでシーケンス番号のルックアップを指定するだけです。

<xsl:sort select="$page-seqs[@id=local-name(current())]/@sequence" />

これが完全なXSLTです

<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:variable name="page-seqs" select="document('C:\lookup.xml')/pages/page"/>

   <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="transaction">
      <transaction>
         <xsl:apply-templates select="child::node()">
            <xsl:sort select="$page-seqs[@id=local-name(current())]/@sequence"/>
         </xsl:apply-templates>
      </transaction>
   </xsl:template>
</xsl:stylesheet>

サンプルドキュメントに適用すると(現在XMLサンプルは整形式ではないため、ヘッダー要素を除く)、次のように出力されます。

<transaction>
   <B2_PG1 ts="1139977698718.77">
      <data/>
   </B2_PG1>
   <B2_PG1 ts="1139977698718.79">
      <data/>
   </B2_PG1>
   <B2_PG2 ts="1139977698718.78">
      <data/>
   </B2_PG2>
   <B2_PG2 ts="1139977698718.80">
      <data/>
   </B2_PG2>
   <B3_PG1 ts="1139977698718.81">
      <data/>
   </B3_PG1>
   <B1_PG1 ts="1139977698718.75">
      <data/>
   </B1_PG1>
   <B1_PG2 ts="1139977698718.76">
      <data/>
   </B1_PG2>
</transaction>

for-eachではなくapply-templatesを使用することが望ましいことに注意してください。これは、私がここで行ったことです。

于 2012-07-15T08:14:44.557 に答える