2017-03-27 46 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