3

同じ親の下に複数のノードをグループ化して「圧縮」する必要がある複数のタイプの xml メッセージがあります (同じ親は、同じノード名を共有し、宣言されたすべての属性も等しいことを意味します)。例えば:

<TopLevel CodeTL="Something">
    <Ratings>
          <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
          </Rating>
    </Ratings>
</TopLevel>
    <TopLevel CodeTL="Something">
    <Ratings>
          <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
          </Rating>
    </Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
    <Ratings>
          <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
          </Rating>
    </Ratings>
</TopLevel>
<TopLevel CodeTL="Something">
    <Ratings>
          <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
              <RatingByNumber Code="X" Rating="30" Number="3">
              <RatingByNumber Code="X" Rating="39" Number="4">
          </Rating>
    </Ratings>
</TopLevel>

それらがすべて同じ CodeTL 属性を共有し、最後の 2 つは同じ CodeA,Start および End 属性を共有していることに注意してください。必要なのは、xslt を使用して次の出力を生成することです。

<TopLevel CodeTL="Something">
    <Ratings>
          <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
          </Rating>
          <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
          </Rating>
          <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
              <RatingByNumber Code="X" Rating="10" Number="1">
              <RatingByNumber Code="X" Rating="19" Number="2">
              <RatingByNumber Code="X" Rating="30" Number="3">
              <RatingByNumber Code="X" Rating="39" Number="4">
          </Rating>
    </Ratings>
</TopLevel>

これははるかにクリーンであり、それを使用するアプリケーションによっては、処理時間とスペースを節約できます。

私が抱えている問題は、さまざまなノード名と属性 (および属性の数) を持つさまざまな種類の xml メッセージがあることですが、それらはすべて、ここに示している同じ構造を共有しています。それらすべてを処理する一般的な方法としては優れていますが、送信する必要があるすべての xml メッセージのカスタム コードを作成できるように、私が提供した例を XSLT に変換してくれるとありがたいです。

4

2 に答える 2

1

この一般的なXSLT2.0変換

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" exclude-result-prefixes="xs my">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
     <t>
       <xsl:sequence select="my:grouping(*)"/>
     </t>
 </xsl:template>

 <xsl:function name="my:grouping" as="node()*">
   <xsl:param name="pElems" as="element()*"/>

   <xsl:if test="$pElems">
       <xsl:for-each-group select="$pElems" group-by="my:signature(.)">
         <xsl:copy>
          <xsl:copy-of select="@*"/>

            <xsl:sequence select="my:grouping(current-group()/*)"/>
         </xsl:copy>
       </xsl:for-each-group>
   </xsl:if>
 </xsl:function>

 <xsl:function name="my:signature" as="xs:string">
  <xsl:param name="pElem" as="element()"/>

  <xsl:variable name="vsignAttribs" as="xs:string*">
      <xsl:for-each select="$pElem/@*">
       <xsl:sort select="name()"/>

       <xsl:value-of select="concat(name(), '=', .,'|')"/>
      </xsl:for-each>
  </xsl:variable>

  <xsl:sequence select=
  "concat(name($pElem), '|', string-join($vsignAttribs, ''))"/>
 </xsl:function>
</xsl:stylesheet>

提供されたXMLに適用した場合(単一の最上位要素にラップされて整形式のXMLドキュメントになります):

<t>
    <TopLevel CodeTL="Something">
        <Ratings>
              <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
                  <RatingByNumber Code="X" Rating="10" Number="1"/>
                  <RatingByNumber Code="X" Rating="19" Number="2"/>
              </Rating>
        </Ratings>
    </TopLevel>
        <TopLevel CodeTL="Something">
        <Ratings>
              <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
                  <RatingByNumber Code="X" Rating="10" Number="1"/>
                  <RatingByNumber Code="X" Rating="19" Number="2"/>
              </Rating>
        </Ratings>
    </TopLevel>
    <TopLevel CodeTL="Something">
        <Ratings>
              <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
                  <RatingByNumber Code="X" Rating="10" Number="1"/>
                  <RatingByNumber Code="X" Rating="19" Number="2"/>
              </Rating>
        </Ratings>
    </TopLevel>
    <TopLevel CodeTL="Something">
        <Ratings>
              <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
                  <RatingByNumber Code="X" Rating="30" Number="3"/>
                  <RatingByNumber Code="X" Rating="39" Number="4"/>
              </Rating>
        </Ratings>
    </TopLevel>
</t>

必要な正しい結果を生成します

<t>
   <TopLevel CodeTL="Something">
      <Ratings>
         <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
         </Rating>
         <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
         </Rating>
         <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
            <RatingByNumber Code="X" Rating="30" Number="3"/>
            <RatingByNumber Code="X" Rating="39" Number="4"/>
         </Rating>
      </Ratings>
   </TopLevel>
</t>

説明

  1. 実行されたグループ化は関数に実装され、my:grouping()再帰的です。

  2. 最上位の要素はそのレベルで単一であり、それ自体の浅いコピー以外のグループ化は必要ありません。次に、この浅いコピーの本体内で、下位レベルのグループ化が関数によって実行されますmy:grouping()

  3. この関数my:grouping()には、すぐ上のレベルのグループ内のすべての要素のすべての子要素である単一の引数があります。現在のレベルのすべてのグループを返します。

  4. 関数に引数として渡される要素のシーケンスは、それらの署名に基づいてグループ化されます。つまり、要素の名前と、その属性および対応する値のすべての名前と値のペアの連結であり、これらは適切な区切り文字を使用して区切られます。要素のシグネチャは、関数によって生成されますmy:signature()


II。一般的なXSLT1.0ソリューション

<xsl:stylesheet version="1.0"
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
         xmlns:ext="http://exslt.org/common"
         xmlns:my="my:my" exclude-result-prefixes="my ext">
         <xsl:output omit-xml-declaration="yes" indent="yes"/>
         <xsl:strip-space elements="*"/>

         <xsl:variable name="vrtfPass1">
          <xsl:apply-templates select="/*"/>
         </xsl:variable>

         <xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>

         <xsl:template match="/">
          <xsl:apply-templates select="$vPass1/*" mode="pass2"/>
         </xsl:template>

         <xsl:template match="/*" mode="pass2">
             <xsl:copy>
               <xsl:call-template name="my:grouping">
                <xsl:with-param name="pElems" select="*"/>
               </xsl:call-template>
             </xsl:copy>
         </xsl:template>

         <xsl:template name="my:grouping">
           <xsl:param name="pElems" select="/.."/>

           <xsl:if test="$pElems">
             <xsl:for-each select="$pElems">
              <xsl:variable name="vPos" select="position()"/>

              <xsl:if test=
               "not(current()/@my:sign
                   = $pElems[not(position() >= $vPos)]/@my:sign
                   )">

                 <xsl:element name="{name()}">
                  <xsl:copy-of select="namespace::*[not(. = 'my:my')]"/>
                  <xsl:copy-of select="@*[not(name()='my:sign')]"/>
                   <xsl:call-template name="my:grouping">
                    <xsl:with-param name="pElems" select=
                    "$pElems[@my:sign = current()/@my:sign]/*"/>
                   </xsl:call-template>
                 </xsl:element>
               </xsl:if>

             </xsl:for-each>
           </xsl:if>
         </xsl:template>

     <xsl:template match="/*">
             <xsl:copy>
               <xsl:apply-templates/>
             </xsl:copy>
     </xsl:template>

     <xsl:template match="*/*">
      <xsl:variable name="vSignature">
       <xsl:call-template name="signature"/>
      </xsl:variable>
      <xsl:copy>
       <xsl:copy-of select="@*"/>
       <xsl:attribute name="my:sign">
        <xsl:value-of select="$vSignature"/>
       </xsl:attribute>

       <xsl:apply-templates/>
      </xsl:copy>
     </xsl:template>

     <xsl:template name="signature">
       <xsl:variable name="vsignAttribs">
         <xsl:for-each select="@*">
          <xsl:sort select="name()"/>

                <xsl:value-of select="concat(name(), '=', .,'|')"/>
             </xsl:for-each>
        </xsl:variable>

        <xsl:value-of select=
          "concat(name(), '|', $vsignAttribs)"/>
     </xsl:template>
</xsl:stylesheet>

この変換が同じXMLドキュメント(上記)に適用されると、同じ正しい結果が生成されます。

<t>
   <TopLevel>
      <Ratings>
         <Rating CodeA="ABC" Start="1-1-2012" End="1-2-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
         </Rating>
         <Rating CodeA="ABC" Start="1-2-2012" End="1-3-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
         </Rating>
         <Rating CodeA="XYZ" Start="1-2-2012" End="1-3-2012">
            <RatingByNumber Code="X" Rating="10" Number="1"/>
            <RatingByNumber Code="X" Rating="19" Number="2"/>
            <RatingByNumber Code="X" Rating="30" Number="3"/>
            <RatingByNumber Code="X" Rating="39" Number="4"/>
         </Rating>
      </Ratings>
   </TopLevel>
</t>

説明

  1. これは2パス変換です。

  2. すべての要素の最初のパスで署名が計算され、それが新しい属性の値になりますmy:sign

  3. XSLT 2.0ソリューションの場合と同じ、再帰的なグループ化アルゴリズムが使用されます。

于 2012-07-10T05:02:24.213 に答える
1

この XSLT 1.0 スタイルシートは、目的の結果を生成します。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="byCodeTL" match="TopLevel" use="@CodeTL"/>
    <xsl:key name="byAttrs" match="Rating" 
             use="concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End)"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="TopLevel[generate-id()=
                                  generate-id(key('byCodeTL', @CodeTL)[1])]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <Ratings>
                <xsl:apply-templates 
                        select="key('byCodeTL', @CodeTL)/Ratings/*"/>
            </Ratings>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="Rating[generate-id()=
                                generate-id(key('byAttrs', 
            concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))[1])]">
        <xsl:copy>
            <xsl:apply-templates select="@*|key('byAttrs', 
                concat(../../@CodeTL, '|', @CodeA, '|', @Start, '|', @End))/*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="TopLevel"/>
    <xsl:template match="Rating"/>
</xsl:stylesheet>

すべてのTopLevel要素は、そのCodeTL属性によってグループ化されます。すべてのRating要素は、それらの属性とCodeTL対応するの属性の組み合わせによってグループ化されますTopLevel

于 2012-07-09T22:42:35.073 に答える