2010-04-06 9 views
3

Je dois transformer certaines données XML en une liste paginée de champs. Voici un exemple.XSLT: une variante du problème de pagination

XML Entrée:

<?xml version="1.0" encoding="UTF-8"?> 
<data> 
    <books> 
     <book title="t0"/> 
     <book title="t1"/> 
     <book title="t2"/> 
     <book title="t3"/> 
     <book title="t4"/> 
    </books> 
    <library name="my library"/> 
</data> 

sortie souhaitée:

<?xml version="1.0" encoding="UTF-8"?> 
<pages> 
    <page number="1"> 
     <field name="library_name" value="my library"/> 
     <field name="book_1" value="t0"/> 
     <field name="book_2" value="t1"/> 
    </page> 
    <page number="2"> 
     <field name="book_1" value="t2"/> 
     <field name="book_2" value="t3"/> 
    </page> 
    <page number="3"> 
     <field name="book_1" value="t4"/> 
    </page> 
</pages> 

Dans l'exemple ci-dessus, je suppose que je veux dans la plupart des 2 champs nommés book_n (avec n compris entre 1 et 2) par page . Les tags <page> doivent avoir un attribut number. Enfin, le champ library_name doit apparaître seulement le premier <page>.

Voici ma solution actuelle en utilisant XSLT:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="2.0" 
    exclude-result-prefixes="trx xs"> 

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

    <xsl:variable name="max" select="2"/> 

    <xsl:template match="//books"> 

     <xsl:for-each-group select="book" group-ending-with="*[position() mod $max = 0]"> 

      <xsl:variable name="pageNum" select="position()"/> 

      <page number="{$pageNum}"> 

       <xsl:for-each select="current-group()"> 
        <xsl:variable name="idx" select="if (position() mod $max = 0) then $max else position() mod $max"/> 
        <field value="{@title}"> 
         <xsl:attribute name="name">book_<xsl:value-of select="$idx"/> 
         </xsl:attribute> 
        </field>     
       </xsl:for-each> 

       <xsl:if test="$pageNum = 1"> 
        <xsl:call-template name="templateFor_library"/>    
       </xsl:if>  

      </page> 

     </xsl:for-each-group>  

    </xsl:template> 

    <xsl:template name="templateFor_library"> 
     <xsl:for-each select="//library"> 
      <field name="library_name" value="{@name}" /> 
     </xsl:for-each>   
    </xsl:template> 

</xsl:stylesheet> 

Y at-il une meilleure/façon plus simple d'effectuer cette transformation?

Répondre

4

Oui il y a.

<xsl:param name="pagesize" select="2" /> 

<xsl:template match="data"> 
    <pages> 
    <xsl:apply-templates mode="page" select=" 
     books/book[position() mod $pagesize = 1] 
    " /> 
    </pages> 
</xsl:template> 

<xsl:template match="book" mode="page"> 
    <page number="{position()}"> 
    <xsl:apply-templates select=" 
     . | following-sibling::book[position() &lt; $pagesize] 
    " /> 
    </page> 
</xsl:template> 

<xsl:template match="book"> 
    <field name="book_{position()}" value="{@title}" /> 
</xsl:template> 

EDIT # 1: Ce qui précède est conforme à XSLT 1.0. Vous pouvez toujours le modifier pour utiliser <xsl:for-each-group> de XSLT 2.0, si vous le souhaitez. Personnellement, je trouve des modèles séparés plus attrayants qu'un gros gros imbriqué pour chaque construction, YMMV.

EDIT # 2: Selon la demande dans les commentaires. Pour faire apparaître quelque chose pour la première page seulement, modifiez un modèle:

<xsl:template match="book" mode="page"> 
    <page number="{position()}"> 
    <xsl:if test="position() = 1"> 
     <xsl:attribute name="library_name`> 
     <xsl:value-of select="ancestor::data/library/@name" /> 
     </xsl:attribute> 
    </xsl:if> 
    <xsl:apply-templates select=" 
     . | following-sibling::book[position() &lt; $pagesize] 
    " /> 
    </page> 
</xsl:template> 
+0

Great! Je vous remercie! Et comment vous assurer que le champ nommé nom_bibliothèque apparaît uniquement sur le premier élément ? – MarcoS

+0

@MarcoS: Voir la solution modifiée. Je déconseillerais fortement cela, cependant. Le nom de la bibliothèque est une partie logique de l'élément '' et devrait aller * là *, au lieu de "sur le premier' élément ' ", ce qui n'a pas beaucoup de sens. – Tomalak

+0

Je suis d'accord que 'libray_name' fait logiquement partie de' ', mais l'exigence actuelle est de le mettre sur le premier' '. Je vous remercie. – MarcoS