2017-03-27 26 views
1

我真的不知道如何用英語描述我的問題,所以我希望我的例子會清楚我是什麼我正在努力去做。XSL-T&XSL-FO:重組XML數據動態創建頁面序列,每

比方說,我有以下XML數據:

<ROOT> 
    <A> 
    <ID>A1</ID> 
    <DATA> 
     <ENTRY> 
     <ENTRYID>Entry1</ENTRYID> 
     <ITEM1>Item1</ITEM1> 
     <ITEM2>Item2</ITEM2> 
     <ITEM3>Item3</ITEM3> 
     </ENTRY> 
     <ENTRY> 
     <ENTRYID>Entry2</ENTRYID> 
     <ITEM1>Item2_1</ITEM1> 
     <ITEM2>Item2_1</ITEM2> 
     <ITEM3>Item2_3</ITEM3> 
     </ENTRY> 
     ... even more entries... 
    </DATA> 
    </A> 
    <A> 
    <ID>A2</ID> 
    <DATA> 
     <ENTRY> 
     <ENTRYID>Entry1</ENTRYID> 
     <ITEM1>foo</ITEM1> 
     <ITEM2>bar</ITEM2> 
     <ITEM3>andsoon</ITEM3> 
     </ENTRY> 
     <ENTRY> 
     <ENTRYID>Entry2</ENTRYID> 
     <ITEM1>even</ITEM1> 
     <ITEM2>more</ITEM2> 
     <ITEM3>items</ITEM3> 
     </ENTRY> 
     ... even more entries... 
    </DATA> 
    </A> 
    <A> 
    .. as many A-Elements as you can think of... 
    </A> 
</ROOT> 

沒有限制爲A-元素有多少可以在我的XML數據,或者有多少ENTRY-元素可以是A-元素中。

所以我有一個現有的XSL-文件放在一個大的頁面序列(XSL-FO)內的所有數據。我正在使用Apache FOP來處理XML和XSL。輸出格式是PDF。現在,當XML數據非常大時,我遇到了內存問題。 我讀過很多關於在處理大數據時調整性能和內存消耗的問題,並試圖將我的數據按頁面分割成一個頁面序列。 我面臨的問題是,我不知道如何在我的樣式表中處理它們之前拆分或重構數據。現在

我的樣式表中的數據匹配A和ENTRY節點和格式化爲一些設計整齊的表格:

<xsl:template match="A"> 
    ... print fancy title for table with A/ID ... 
    <fo:table> 
    <fo:table-header> 
     ... fancy table header here ... 
    </table-header> 
    <fo:table-body> 
     <xsl:apply-templates select="DATA/ENTRY"/> 
     <fo:table-row> 
     ... do some calculating for each A and create a sum table row ... 
     </fo:table-row> 
    </fo:table-body> 
    </fo:table> 
</xsl:template> 

<xsl:template match="ENTRY"> 
    <fo:table-row> 
    ... print Entry data in table cells ... 
    </fo:table-row> 
</xsl:template> 

完整的表爲一個A元素可以跨越數百頁(最壞情況)。我知道有多少個Entry-Elements可以放入一個頁面。由於表頭和總錶行,一個A元素的第一頁和最後一頁將適合較少的ENTRY元素作爲它們之間的頁面。 我需要將數據分成合適的塊。由於我對XML文件的結構沒有影響,因此我需要直接在樣式表中執行此操作。

我嘗試了一些東西以xsl:關鍵,因爲他們分組數據時工作得很好,但我不知道如果這些甚至對分組的我的「特殊」的形式,如果是,這將如何攜手。

所以我得到的XSL應該是這樣的:

<xsl:template match="/"> 
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> 
    <fo:layout-master-set>...</fo:layout-master-set> 

     <fo:page-sequence master-reference="{$master}"> 

     <fo:flow flow-name="xsl-region-body" font-size="10pt"> 

      <xsl:apply-templates select="A"/> 
      <xsl:apply-templates select="ENTRY elemnts for first page"/> 

     </fo:flow> 

     </fo:page-sequence> 

     <fo:page-sequence master-reference="{$master}"> 

     <fo:flow flow-name="xsl-region-body" font-size="10pt"> 

      <xsl:apply-templates select="ENTRY elemnts for pages in between"/> 

     </fo:flow> 

     </fo:page-sequence> 

     <fo:page-sequence master-reference="{$master}"> 

     <fo:flow flow-name="xsl-region-body" font-size="10pt"> 

      <xsl:apply-templates select="ENTRY elemnts for last page"/> 

     </fo:flow> 

     </fo:page-sequence> 

    </fo:root> 
</xsl:template> 

請注意,中間頁序必須是在一個循環,當然可以有多於一個元素。 我不確定如何循環適用於所有頁面序列的所有數據。

回答

1
  1. 您可以開始與每個A.每個頁面序列將「只是」是多達幾百頁,而不是長達幾百頁整個文檔倍數新fo:page-sequence度日。
  2. 您可以使用遞歸來選擇和處理下一個ñ entry元素。
  3. 使用XSLT 2.0可能會產生更清晰,更簡潔的代碼,但是因爲您正在討論如何使用xsl:key進行分組,所以看起來您正在使用XSLT 1.0
  4. 不要考慮循環術語,從選擇和處理源代碼的角度考慮它。畢竟,在XSLT 1.0或XSLT 2.0中沒有更新循環變量。

隨着每一個一fo:page-sequence,你的模板就變成了:

<xsl:template match="A"> 
    <fo:page-sequence master-reference="{$master}"> 
    <fo:flow flow-name="xsl-region-body" font-size="10pt"> 
     ... print fancy title for table with A/ID ... 
     <fo:table> 
     <fo:table-header> 
      ... fancy table header here ... 
     </table-header> 
     <fo:table-body> 
      <xsl:apply-templates select="DATA/ENTRY"/> 
      <fo:table-row> 
      ... do some calculating for each A and create a sum table row ... 
      </fo:table-row> 
     </fo:table-body> 
     </fo:table> 
    </fo:flow> 
    </fo:page-sequence> 
</xsl:template> 

遞歸的解決方案可能需要處理單頁的情況下,以及多頁的情況下:

<xsl:param name="single-page-count" select="1" /> 
<xsl:param name="first-page-count" select="2" /> 
<xsl:param name="middle-page-count" select="3" /> 
<xsl:param name="last-page-count" select="2" /> 

<xsl:template match="ROOT"> 
    <fo:root> 
    <fo:layout-master-set> 
     <fo:simple-page-master master-name="a"> 
     <fo:region-body/> 
     </fo:simple-page-master> 
    </fo:layout-master-set> 
    <xsl:apply-templates select="A" /> 
    </fo:root> 
</xsl:template> 

<xsl:template match="A"> 
    <xsl:variable name="count" 
       select="count(DATA/ENTRY)" /> 

    <xsl:variable name="title"> 
    <xsl:call-template name="title" /> 
    </xsl:variable> 

    <xsl:variable name="sum-row"> 
    <xsl:call-template name="sum-row" /> 
    </xsl:variable> 

    <xsl:choose> 
    <xsl:when test="$count &lt;= $single-page-count"> 
     <xsl:call-template name="page"> 
     <xsl:with-param name="title" select="$title" /> 
     <xsl:with-param name="rows"> 
      <xsl:apply-templates select="DATA/ENTRY"/> 
     </xsl:with-param> 
     <xsl:with-param name="sum-row" select="$sum-row" /> 
     </xsl:call-template> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:call-template name="page"> 
     <xsl:with-param name="title" select="$title" /> 
     <xsl:with-param name="rows"> 
      <xsl:apply-templates 
       select="DATA/ENTRY[position() &lt;= $first-page-count]"/> 
     </xsl:with-param> 
     </xsl:call-template> 
     <xsl:call-template name="other-pages"> 
     <xsl:with-param name="entries" 
         select="DATA/ENTRY[position() > $first-page-count]" /> 
     <xsl:with-param name="sum-row" select="$sum-row" /> 
     </xsl:call-template> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<xsl:template name="other-pages"> 
    <xsl:param name="entries" /> 
    <xsl:param name="sum-row" /> 
    <xsl:variable name="count" 
       select="count($entries)" /> 

    <xsl:choose> 
    <xsl:when test="$count &lt;= $last-page-count"> 
     <xsl:call-template name="page"> 
     <xsl:with-param name="rows"> 
      <xsl:apply-templates select="$entries"/> 
     </xsl:with-param> 
     <xsl:with-param name="sum-row" select="$sum-row" /> 
     </xsl:call-template> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:call-template name="page"> 
     <xsl:with-param name="rows"> 
      <xsl:apply-templates 
       select="$entries[position() &lt;= $middle-page-count]"/> 
     </xsl:with-param> 
     </xsl:call-template> 
     <xsl:call-template name="other-pages"> 
     <xsl:with-param name="entries" 
         select="$entries[position() > $middle-page-count]" /> 
     <xsl:with-param name="sum-row" select="$sum-row" /> 
     </xsl:call-template> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<xsl:template name="page"> 
    <xsl:param name="title" /> 
    <xsl:param name="rows" /> 
    <xsl:param name="sum-row" /> 

    <fo:page-sequence master-reference="a"> 
    <fo:flow flow-name="xsl-region-body"> 
     <xsl:copy-of select="$title" /> 
     <fo:table> 
     <fo:table-header> 
      ... fancy table header here ... 
     </fo:table-header> 
     <fo:table-body> 
      <xsl:copy-of select="$rows" /> 
      <xsl:copy-of select="$sum-row" /> 
     </fo:table-body> 
     </fo:table> 
    </fo:flow> 
    </fo:page-sequence> 
</xsl:template> 

<xsl:template name="title"> 
    ... print fancy title for table <xsl:value-of select="ID"/> ... 
</xsl:template> 

<xsl:template name="sum-row"> 
    <fo:table-row> 
    ... do some calculating for each A and create a sum table row ... 
    </fo:table-row> 
</xsl:template> 
+0

遞歸方法就像一個魅力。我只是因爲認爲必須循環數據而被誤導。非常感謝! – Kaweoosh

+0

我沒有考慮到最後一頁可能需要比中間頁面更少的行。我還更新了代碼,以減少XSLT中文字FO的重複,方法是爲'fo:page-sequence'製作一個命名模板,該模板包含三個花式標題參數,頁面表的行和總和行。單頁案例提供了所有三個參數;第一頁案例,標題和行;中間頁面的情況下,只有行;和最後一頁的情況下,行和總和。 –

+0

再次,非常感謝! – Kaweoosh