2017-03-27 5 views
1

Je ne suis pas sûr de savoir comment décrire mon problème en anglais, alors j'espère que mon exemple éclaircira ce que je J'essaie de faire.XSL-T et XSL-FO: restructurer les données XML pour créer dynamiquement des séquences de pages pour chaque page

Disons que j'ai les données XML suivantes:

<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> 

Il n'y a aucune limite quant au nombre A-éléments peuvent être dans mes données XML ou le nombre ENTRÉE-éléments peuvent être à l'intérieur d'un A-Element . J'ai donc un fichier XSL existant qui place toutes les données dans une grande séquence de pages (XSL-FO). J'utilise Apache FOP pour traiter le XML et le XSL. Le format de sortie est PDF. Maintenant, je rencontre des problèmes de mémoire lorsque les données XML sont très volumineuses. J'ai beaucoup lu sur l'optimisation des performances et de la consommation de mémoire lorsque je traite des données volumineuses et j'essaie de scinder mes données en une seule page par page. Le problème auquel je suis confronté est que je ne sais pas comment diviser ou restructurer les données avant de les traiter dans ma feuille de style.

Maintenant, ma feuille de style correspondant aux nœuds pour A et l'entrée et formate les données dans des tables soigneusement conçues:

<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> 

Le tableau complet pour un Un élément peut étirer sur plusieurs centaines de pages (pire cas). Je sais combien d'éléments d'entrée vont tenir dans une page. En raison de l'en-tête de la table et de la ligne de la table de somme, la première et la dernière page d'un élément A entreront moins d'éléments ENTRY que les pages intermédiaires. Je dois diviser les données en morceaux appropriés. Comme je n'ai aucune influence sur la structure du fichier XML, je dois le faire directement dans la feuille de style. J'ai essayé certaines choses avec xsl: key parce qu'elles marchent bien quand je groupais des données, mais je ne sais pas si elles fonctionnent même pour ma forme "spéciale" de groupage et si oui, comment cela fonctionnera.

donc mon résultat devrait ressembler XSL comme ceci:

<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> 

S'il vous plaît noter que la page séquence du milieu doit être dans une boucle et bien sûr il peut y avoir plus d'un élément A. Je ne sais pas comment boucler correctement toutes les données pour toutes les séquences de pages.

Répondre

1
  1. Vous pouvez être en mesure d'obtenir avec le démarrage d'une nouvelle fo:page-sequence pour chaque A. Chaque séquence de pages serait « juste » jusqu'à plusieurs centaines de pages au lieu de jusqu'à multiples de plusieurs centaines de pages pour le document entier.
  2. Vous pouvez utiliser la récursivité pour sélectionner et traiter les éléments n suivants ENTRY. L'utilisation de XSLT 2.0 produirait probablement un code plus clair et plus concis, mais puisque vous parlez de l'utilisation de xsl: key pour faire du groupement, il semble que vous utilisiez XSLT 1.0
  3. Ne pas y penser En termes de boucle, pensez-y en termes de sélection et de traitement de ce qui est dans la source. Après tout, il n'y a pas de variables en boucle à mettre à jour dans XSLT 1.0 ou XSLT 2.0.

Avec un fo:page-sequence par A, votre modèle A devient:

<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> 

La solution récursive nécessite vraisemblablement chargé de l'affaire d'une seule page, ainsi que le cas de plusieurs pages :

<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

L'approche récursive a fonctionné comme un charme. J'ai simplement été trompé en pensant que je devais boucler les données. Merci beaucoup! – Kaweoosh

+0

Je n'avais pas considéré que la dernière page pouvait nécessiter moins de lignes que la page du milieu. J'ai également mis à jour le code pour réduire la duplication des FO littérales dans le XSLT en créant un modèle nommé pour le 'fo: page-sequence' qui prend trois paramètres pour le titre fantaisie, les lignes pour la table de la page, et la somme rangée. Le cas d'une seule page fournit les trois paramètres; le premier cas de page, le titre et les lignes; le cas de la page du milieu, les lignes seulement; et le dernier cas de page, les lignes et la somme. –

+0

Encore une fois, merci beaucoup! – Kaweoosh