XSL-T & XSL-FO:重组 XML 数据以动态创建每个页面的 page-sequences
XSL-T & XSL-FO: restructure XML data to dynamically create page-sequences for every page
我不太确定如何用英语描述我的问题,所以我希望我的例子能清楚地说明我想做什么。
假设我有以下 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>
对于我的 XML 数据中可以有多少 A-Elements 或 A-Element.[=17= 中可以有多少 ENTRY-Elements 没有限制]
所以我有一个现有的 XSL-File,它将所有数据放在一个大页面序列 (XSL-FO) 中。我正在使用 Apache FOP 处理 XML 和 XSL。输出格式为PDF。现在,当 XML 数据非常大时,我遇到了内存问题。
我已经阅读了很多关于在处理大数据时调整性能和内存消耗的内容,并且正在尝试将我的数据拆分为每页一个页面序列。
我面临的问题是,在我的样式表中处理数据之前,我不知道如何拆分或重组数据。
现在我的样式表匹配 A 和 ENTRY 的节点并将数据格式化为一些设计巧妙的 tables:
<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 元素的完整 table 可以跨越数百页(最坏的情况)。我知道有多少 Entry-Elements 适合一页。由于 table header 和总和 table 行,一个 A 元素的第一页和最后一页将适合较少的 ENTRY 元素,因为它们之间的页面。
我需要将数据分成适当的块。由于我对 XML 文件的结构没有影响,因此我需要直接在样式表中执行此操作。
我用 xsl:key 尝试了一些东西,因为它们在分组数据时工作正常,但我不知道这些是否适用于我的 'special' 分组形式,如果是,这将如何工作。
所以我生成的 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>
请注意,中间的page-sequence必须在一个循环中,当然可以有多个A元素。
我不确定如何适当地遍历所有 page-sequence 的所有数据。
- 您可以为每个 A 开始一个新的
fo:page-sequence
。每个页面序列将 'just' 达到数百页,而不是数百页的倍数整个文档。
- 您可以使用递归 select 并处理接下来的 n 个 ENTRY 元素。
- 使用 XSLT 2.0 可能会产生更清晰、更简洁的代码,但由于您正在谈论使用 xsl:key 进行分组,因此看起来您正在使用 XSLT 1.0
- 不要从循环的角度来考虑,要从select编译和处理源中的内容的角度来考虑。毕竟,在 XSLT 1.0 或 XSLT 2.0 中没有要更新的循环变量。
每个 A 一个 fo:page-sequence
,您的 A 模板变为:
<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 <= $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() <= $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 <= $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() <= $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>
我不太确定如何用英语描述我的问题,所以我希望我的例子能清楚地说明我想做什么。
假设我有以下 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>
对于我的 XML 数据中可以有多少 A-Elements 或 A-Element.[=17= 中可以有多少 ENTRY-Elements 没有限制]
所以我有一个现有的 XSL-File,它将所有数据放在一个大页面序列 (XSL-FO) 中。我正在使用 Apache FOP 处理 XML 和 XSL。输出格式为PDF。现在,当 XML 数据非常大时,我遇到了内存问题。 我已经阅读了很多关于在处理大数据时调整性能和内存消耗的内容,并且正在尝试将我的数据拆分为每页一个页面序列。 我面临的问题是,在我的样式表中处理数据之前,我不知道如何拆分或重组数据。
现在我的样式表匹配 A 和 ENTRY 的节点并将数据格式化为一些设计巧妙的 tables:
<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 元素的完整 table 可以跨越数百页(最坏的情况)。我知道有多少 Entry-Elements 适合一页。由于 table header 和总和 table 行,一个 A 元素的第一页和最后一页将适合较少的 ENTRY 元素,因为它们之间的页面。 我需要将数据分成适当的块。由于我对 XML 文件的结构没有影响,因此我需要直接在样式表中执行此操作。
我用 xsl:key 尝试了一些东西,因为它们在分组数据时工作正常,但我不知道这些是否适用于我的 'special' 分组形式,如果是,这将如何工作。
所以我生成的 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>
请注意,中间的page-sequence必须在一个循环中,当然可以有多个A元素。 我不确定如何适当地遍历所有 page-sequence 的所有数据。
- 您可以为每个 A 开始一个新的
fo:page-sequence
。每个页面序列将 'just' 达到数百页,而不是数百页的倍数整个文档。 - 您可以使用递归 select 并处理接下来的 n 个 ENTRY 元素。
- 使用 XSLT 2.0 可能会产生更清晰、更简洁的代码,但由于您正在谈论使用 xsl:key 进行分组,因此看起来您正在使用 XSLT 1.0
- 不要从循环的角度来考虑,要从select编译和处理源中的内容的角度来考虑。毕竟,在 XSLT 1.0 或 XSLT 2.0 中没有要更新的循环变量。
每个 A 一个 fo:page-sequence
,您的 A 模板变为:
<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 <= $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() <= $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 <= $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() <= $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>