1

私はXSLTの用語で可能な限り機能的に考えようとしていますが、この場合、微調整せずにそれを行う方法が本当にわかりません。私は大まかにこのデータ構造を持っています:

<transactions>
  <trx>
    <text>abc</text>
    <text>def</text>

    <detail>
      <text>xxx</text>
      <text>yyy</text>
      <text>zzz</text>
    </detail>
  </trx>
</transactions>

これを大まかにこの形に平らにしたい

<row>abc</row>
<row>def</row>
<row>xxx</row>
<row>yyy</row>
<row>zzz</row>

ただし、注意が必要なのは、40行のテキスト行のチャンクを作成したいので、トランザクションをチャンクに分割してはならないということです。つまり、現在のチャンクにすでに38行ある場合、上記のトランザクションは次のチャンクに移動する必要があります。40を完了するには、現在のチャンクを2つの空の行で埋める必要があります。

<row/>
<row/>

命令型/手続き型プログラミングでは、それは非常に簡単です。40の倍数までカウントするグローバルイテレータ変数を作成し、必要に応じて空の行を挿入するだけです(XSLT / Xalanを微調整してそのような変数を許可する方法を示す回答を提供しました)。しかし、XSLTでそれを行う方法は?NB:処理しているデータのサイズを考えると、再帰は不可能だと思います...しかし、おそらく私はそれについて間違っています

4

3 に答える 3

3

I.これがXSLT1.0ソリューションです(XSLT 2.0ソリューションの方がはるかに簡単です)。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">

 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pChunkSize" select="8"/>
 <xsl:param name="vChunkSize" select="$pChunkSize+1"/>

 <xsl:variable name="vSheet" select="document('')"/>

 <xsl:variable name="vrtfEmptyChunk">
  <xsl:for-each select=
   "($vSheet//node())[not(position() > $pChunkSize)]">
    <row/>
  </xsl:for-each>
 </xsl:variable>

 <xsl:variable name="vEmptyChunk" select=
  "ext:node-set($vrtfEmptyChunk)/*"/>

 <xsl:variable name="vrtfDummy">
  <delete/>
 </xsl:variable>

 <xsl:variable name="vDummy" select="ext:node-set($vrtfDummy)/*"/>

 <xsl:template match="/*">
  <chunks>
   <xsl:call-template name="fillChunks">
    <xsl:with-param name="pNodes" select="trx"/>
    <xsl:with-param name="pCurChunk" select="$vDummy"/>
   </xsl:call-template>
  </chunks>
 </xsl:template>

 <xsl:template name="fillChunks">
  <xsl:param name="pNodes"/>
  <xsl:param name="pCurChunk"/>

  <xsl:choose>
    <xsl:when test="not($pNodes)">
     <chunk>
      <xsl:apply-templates mode="rename" select="$pCurChunk[self::text]"/>
      <xsl:copy-of select=
        "$vEmptyChunk[not(position() > $vChunkSize - count($pCurChunk))]"/>
     </chunk>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="vAvailable" select=
          "$vChunkSize - count($pCurChunk)"/>

      <xsl:variable name="vcurNode" select="$pNodes[1]"/>

      <xsl:variable name="vTrans" select="$vcurNode//text"/>

      <xsl:variable name="vNumNewNodes" select="count($vTrans)"/>

      <xsl:choose>
        <xsl:when test="not($vNumNewNodes > $vAvailable)">
         <xsl:variable name="vNewChunk"
              select="$pCurChunk | $vTrans"/>

         <xsl:call-template name="fillChunks">
           <xsl:with-param name="pNodes" select="$pNodes[position() > 1]"/>
           <xsl:with-param name="pCurChunk" select="$vNewChunk"/>
         </xsl:call-template>
        </xsl:when>

        <xsl:otherwise>
         <chunk>
          <xsl:apply-templates mode="rename" select="$pCurChunk[self::text]"/>
          <xsl:copy-of select=
            "$vEmptyChunk[not(position() > $vAvailable)]"/>
         </chunk>

         <xsl:call-template name="fillChunks">
          <xsl:with-param name="pNodes" select="$pNodes"/>
          <xsl:with-param name="pCurChunk" select="$vDummy"/>
         </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="text" mode="rename">
  <row>
   <xsl:value-of select="."/>
  </row>
 </xsl:template>
</xsl:stylesheet>

この変換が次のXMLドキュメントに適用される場合(提供されたものに基づいていますが、3つのtrx要素があります):

<transactions>
  <trx>
    <text>abc</text>
    <text>def</text>

    <detail>
      <text>xxx</text>
      <text>yyy</text>
      <text>zzz</text>
    </detail>
  </trx>
  <trx>
    <text>abc2</text>
    <text>def2</text>
  </trx>
  <trx>
    <text>abc3</text>
    <text>def3</text>

    <detail>
      <text>xxx3</text>
      <text>yyy3</text>
      <text>zzz3</text>
    </detail>
  </trx>
</transactions>

必要な正しい結果(サイズ8の2つのチャンク)が生成されます:

<chunks>
   <chunk>
      <row>abc</row>
      <row>def</row>
      <row>xxx</row>
      <row>yyy</row>
      <row>zzz</row>
      <row>abc2</row>
      <row>def2</row>
      <row/>
   </chunk>
   <chunk>
      <row>abc3</row>
      <row>def3</row>
      <row>xxx3</row>
      <row>yyy3</row>
      <row>zzz3</row>
      <row/>
      <row/>
      <row/>
   </chunk>
</chunks>

注意してください

  1. 最初の2つのトランザクションのtext要素の総数は7であり、1つの8桁のチャンクに収まります。

  2. 3番目のトランザクションには5つtextの要素があり、最初のチャンクの残りのスペースに収まりません。新しいチャンクに配置されます。

II。XSLT 2.0ソリューション(FXSLを使用)

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:dvc-foldl-func="dvc-foldl-func"
exclude-result-prefixes="f dvc-foldl-func"
>

   <xsl:import href="../f/func-dvc-foldl.xsl"/>
   <xsl:output omit-xml-declaration="yes" indent="yes"/>

   <xsl:param name="pChunkSize" select="8"/>

   <dvc-foldl-func:dvc-foldl-func/>

   <xsl:variable name="vPadding">
    <row/>
   </xsl:variable>

   <xsl:variable name="vFoldlFun" select="document('')/*/dvc-foldl-func:*[1]"/>

    <xsl:template match="/">

      <xsl:variable name="vpaddingChunk" select=
       "for $i in 1 to $pChunkSize
         return ' '
       "/>

      <xsl:variable name="vfoldlResult" select=
          "f:foldl($vFoldlFun, (), /*/trx),
           $vpaddingChunk
          "/>
      <xsl:variable name="vresultCount"
           select="count($vfoldlResult)"/>
      <xsl:variable name="vFinalResult"
       select="subsequence($vfoldlResult, 1,
                           $vresultCount - $vresultCount mod $pChunkSize
                           )"/>
      <result>
       <xsl:for-each select="$vFinalResult">
        <row>
          <xsl:value-of select="."/>
        </row>
       </xsl:for-each>
       <xsl:text>&#xA;</xsl:text>
      </result>
    </xsl:template>

    <xsl:template match="dvc-foldl-func:*" mode="f:FXSL">
         <xsl:param name="arg1"/>
         <xsl:param name="arg2"/>

         <xsl:variable name="vCurCount" select="count($arg1)"/>

         <xsl:variable name="vNewCount" select="count($arg2//text)"/>

         <xsl:variable name="vAvailable" select=
         "$pChunkSize - $vCurCount mod $pChunkSize"/>

         <xsl:choose>
           <xsl:when test="$vNewCount le $vAvailable">
             <xsl:sequence select="$arg1, $arg2//text"/>
           </xsl:when>
           <xsl:otherwise>
             <xsl:sequence select="$arg1"/>
             <xsl:for-each select="1 to $vAvailable">
              <xsl:sequence select="$vPadding/*"/>
              </xsl:for-each>
              <xsl:sequence select="$arg2//text"/>
           </xsl:otherwise>
         </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

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

<result>
   <row>abc</row>
   <row>def</row>
   <row>xxx</row>
   <row>yyy</row>
   <row>zzz</row>
   <row>abc2</row>
   <row>def2</row>
   <row/>
   <row>abc3</row>
   <row>def3</row>
   <row>xxx3</row>
   <row>yyy3</row>
   <row>zzz3</row>
   <row> </row>
   <row> </row>
   <row> </row>
</result>

注意してください

  1. 関数の使用f:foldl()

  2. の特別なDVC(Divide and Conquer)バリアントによりf:foldl()、すべての実用的な目的で再帰スタックオーバーフローが回避されます。たとえば、1000000(1M)trx要素の最大再帰スタック深度はわずか19です。

于 2011-11-01T18:11:43.903 に答える
1

Javaで必要に応じて、完全なXMLデータ構造を構築します。次に、準備されたXMLに対してXSLで単純な反復を実行します。

多くの労力を節約し、保守可能なソリューションを提供することができます。

于 2011-11-01T10:55:06.940 に答える
0

約束されたように、Xalanを微調整してそのようなグローバルイテレータをインクリメントできるようにする方法を示す簡単な例の回答:

<xsl:stylesheet version="1.0" xmlns:f="xalan://com.example.Functions">
  <!-- the global row counter variable -->
  <xsl:variable name="row" select="0"/>

  <xsl:template match="trx">
    <!-- wherever needed, the $row variable can be globally incremented -->
    <xsl:variable name="iteration" value="f:increment('row')"/>

    <!-- based upon this variable, calculations can be made -->
    <xsl:variable name="remaining-rows-in-chunk" 
                  value="40 - (($iteration - 1) mod 40) "/>
    <xsl:if test="count(.//text) &gt; $remaining-rows-in-chunk">
      <xsl:call-template name="empty-row">
        <xsl:with-param name="rows" select="$remaining-rows-in-chunk"/>
      </xsl:call-template>
    </xsl:if>

    <!-- process transaction now, that previous chunk has been filled [...] -->
  </xsl:template>

  <xsl:template name="empty-row">
    <xsl:param name="rows"/>

    <xsl:if test="$rows &gt; 0">
      <row/>
      <xsl:variable name="dummy" select="f:increment('row')"/>

      <xsl:call-template name="empty-row">
        <xsl:with-param name="rows" select="$rows - 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

そしての内容com.example.Functions

public class Functions {
  public static String increment(ExpressionContext context, String nodeName) {
    XNumber n = null;

    try {
      // Access the $row variable
      n = ((XNumber) context.getVariableOrParam(new QName(nodeName)));

      // Make it "mutable" using this tweak. I feel horrible about
      // doing this, though ;-)
      Field m_val = XNumber.class.getDeclaredField("m_val");
      m_val.setAccessible(true);

      // Increment it
      m_val.setDouble(n, m_val.getDouble(n) + 1.0);
    } catch (Exception e) {
      log.error("Error", e);
    }

    return n == null ? null : n.str();
  }
}
于 2011-11-01T10:11:58.010 に答える