2009-04-21 6 views
2

Étant donné un fichier XML d'entrée avec la structure suivante:Muenchian? XSLT pour dénormaliser/pivoter/aplatir le fichier xml?

<root> 
    <record row="1" col="1" val="1" /> 
    <record row="1" col="2" val="2" /> 
    <record row="1" col="3" val="3" /> 
    <record row="1" col="n" val="4" /> 
    <record row="2" col="1" val="5" /> 
    <record row="2" col="3" val="6" /> 
    <record row="2" col="n" val="7" /> 
    <record row="n" col="2" val="8" /> 
    <record row="n" col="3" val="9" /> 
    <record row="n" col="n" val="10" /> 
</root> 

Comment puis-je produire la structure suivante en utilisant XSLT?

<root> 
    <row id="1"> 
    <col id="1">1</col> 
    <col id="2">2</col> 
    <col id="3">3</col> 
    <col id="n">4</col> 
    </row> 
    <row id="2"> 
    <col id="1">5</col> 
    <col id="2"></col> 
    <col id="3">6</col> 
    <col id="n">7</col> 
    </row> 
    <row id="n"> 
    <col id="1"></col> 
    <col id="2">8</col> 
    <col id="3">9</col> 
    <col id="n">10</col> 
    </row> 
</root> 

[Notez comment toutes les colonnes sont sorties, même s'il n'y a aucun élément lié à l'entrée]

EDIT: Je peux avoir provoqué la confusion par l'utilisation de chiffres et de lettres dans mon exemple. La solution que je recherche doit gérer les attributs de ligne et de colonne qui ne sont pas numériques.

+0

Souhaitez-vous inclure un exemple qui montre mieux vos données d'entrée? Si les attributs @id ne contiennent aucune valeur utilisable (dans le contexte de la question au moins), laissez-les simplement. – Tomalak

+0

Si les identifiants de colonne/ligne ne sont pas numériques, il ne sera pas nécessaire de "remplir les espaces", donc les deux solutions présentées seront encore plus simples. –

+0

Vous devez reformuler votre question - mieux commencer une nouvelle question, fournissant la définition plus précise avec des valeurs d'identification non numériques. –

Répondre

3

Les réponses à cette question montrent façons d'aborder le problème:

xslt: How could I use xslt to create a table with multiple columns and rows?


EDIT: Une solution qui intègre les techniques observées dans la question liée suit.

Je suppose:

  • vos @row et @col attributs sont des nombres incrémentant qui définissent la position de l'enregistrement dans la table, et ils ne peuvent pas vraiment contenir la chaîne "n". En tant que tels, ils ne sont pas uniques dans tout le document, ce qui les rend inappropriés comme attributs HTML @id. Je les ai remplacés par @title attributs dans ma sortie.
  • il n'y a pas de lignes vides implicites (les espaces dans la continuité @row ne produiront pas de lignes vides), seules les cellules vides implicites.
  • chaque combinaison @row et @col est unique.

Cette XSLT 1.0 transformation:

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 

    <!-- prepare some keys for later use --> 
    <xsl:key name="kRecordsByRow" match="record" use="@row" /> 
    <xsl:key name="kRecordsByPos" match="record" use="concat(@row, ',', @col)" /> 

    <!-- find out the highest @col number --> 
    <xsl:variable name="vMaxCol"> 
    <xsl:for-each select="/root/record"> 
     <xsl:sort select="@col" data-type="number" order="descending" /> 
     <xsl:if test="position() = 1"> 
     <xsl:value-of select="@col" /> 
     </xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 

    <!-- select the <record>s that are the first in their rows --> 
    <xsl:variable name="vRows" select=" 
    /root/record[ 
     generate-id() 
     = 
     generate-id(key('kRecordsByRow', @row)[1]) 
    ] 
    " /> 

    <!-- output basic table structure --> 
    <xsl:template match="/root"> 
    <table> 
     <xsl:for-each select="$vRows"> 
     <xsl:sort select="@row" data-type="number" /> 
     <tr title="{@row}"> 
      <xsl:call-template name="td" /> 
     </tr> 
     </xsl:for-each> 
    </table> 
    </xsl:template> 

    <!-- output the right number of <td>s in each row, empty or not --> 
    <xsl:template name="td"> 
    <xsl:param name="col" select="1" /> 

    <td title="{$col}"> 
     <xsl:value-of select="key('kRecordsByPos', concat(@row, ',', $col))/@val" /> 
    </td> 

    <xsl:if test="$col &lt; $vMaxCol"> 
     <xsl:call-template name="td"> 
     <xsl:with-param name="col" select="$col + 1" /> 
     </xsl:call-template> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

... quand il est appliqué à cette entrée (légèrement modifiée):

<root> 
    <record row="1" col="1" val="1" /> 
    <record row="1" col="2" val="2" /> 
    <record row="1" col="3" val="3" /> 
    <record row="1" col="4" val="4" /> 
    <record row="2" col="1" val="5" /> 
    <record row="2" col="3" val="6" /> 
    <record row="2" col="4" val="7" /> 
    <record row="3" col="2" val="8" /> 
    <record row="3" col="3" val="9" /> 
    <record row="3" col="4" val="10" /> 
</root> 

... produit:

<table> 
    <tr title="1"> 
    <td title="1">1</td> 
    <td title="2">2</td> 
    <td title="3">3</td> 
    <td title="4">4</td> 
    </tr> 
    <tr title="2"> 
    <td title="1">5</td> 
    <td title="2"></td> 
    <td title="3">6</td> 
    <td title="4">7</td> 
    </tr> 
    <tr title="3"> 
    <td title="1"></td> 
    <td title="2">8</td> 
    <td title="3">9</td> 
    <td title="4">10</td> 
    </tr> 
</table> 
  • groupement Muenchian c'est toi sed pour sélectionner le premier <record> s de chaque groupe @row
  • un <xsl:key> est utilisé pour localiser un enregistrement de sa position
  • récursion est utilisé pour produire un ensemble cohérent de <td> s, indépendamment de l'existence réelle d'une <record> au position nommée
+0

Une solution très agréable et complète! Je n'ai rien à ajouter. :) –

+0

Merci. :-) (Je suppose que j'aurais pu utiliser la technique "itérer sans récursion" que j'ai apprise de ta réponse dans la question que j'ai liée). – Tomalak

+0

Tomalak - Merci pour la solution; cependant, j'ai peut-être causé de la confusion en utilisant des chiffres et des lettres dans mon exemple. La solution que je recherche doit gérer les attributs de ligne et de colonne qui ne sont pas numériques. – eft

2

Un XSLT 2.0 solution

Cette transformation:

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" 
    > 

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

    <xsl:variable name="vDoc" as="document-node()" 
    select="/"/> 

    <xsl:key name="kColsByRow" match="@col" 
     use="../@row"/> 

    <xsl:key name="kRecByRowCol" match="record" 
     use="concat(@row,'+',@col)"/> 

    <xsl:template match="/*"> 
     <root> 
     <xsl:for-each-group select="*/@row" 
      group-by="."> 
      <xsl:sort select="current-grouping-key()" 
       data-type="number"/> 

      <xsl:variable name="vRow" 
       select="current-grouping-key()"/> 

      <row idd="{$vRow}"> 

       <xsl:for-each select= 
       "1 to max(key('kColsByRow',$vRow)/xs:integer(.))"> 

       <col idd="{.}"> 
        <xsl:value-of select= 
        "key('kRecByRowCol', 
         concat($vRow,'+',.), 
         $vDoc 
         ) 
        /
         @col 
       " 
        /> 
       </col> 
       </xsl:for-each> 
      </row>  
     </xsl:for-each-group> 
     </root> 
    </xsl:template> 
</xsl:stylesheet> 

lorsqu'il est appliqué sur ce document XML:

<root> 
    <record row="1" col="1" val="1" /> 
    <record row="1" col="2" val="2" /> 
    <record row="1" col="3" val="3" /> 
    <record row="1" col="10" val="4" /> 
    <record row="2" col="1" val="5" /> 
    <record row="2" col="3" val="6" /> 
    <record row="2" col="10" val="7" /> 
    <record row="10" col="2" val="8" /> 
    <record row="10" col="3" val="9" /> 
    <record row="10" col="10" val="10" /> 
</root> 

produit le résultat recherché:

<root> 
    <row idd="1"> 
     <col idd="1">1</col> 
     <col idd="2">2</col> 
     <col idd="3">3</col> 
     <col idd="4"/> 
     <col idd="5"/> 
     <col idd="6"/> 
     <col idd="7"/> 
     <col idd="8"/> 
     <col idd="9"/> 
     <col idd="10">10</col> 
    </row> 
    <row idd="2"> 
     <col idd="1">1</col> 
     <col idd="2"/> 
     <col idd="3">3</col> 
     <col idd="4"/> 
     <col idd="5"/> 
     <col idd="6"/> 
     <col idd="7"/> 
     <col idd="8"/> 
     <col idd="9"/> 
     <col idd="10">10</col> 
    </row> 
    <row idd="10"> 
     <col idd="1"/> 
     <col idd="2">2</col> 
     <col idd="3">3</col> 
     <col idd="4"/> 
     <col idd="5"/> 
     <col idd="6"/> 
     <col idd="7"/> 
     <col idd="8"/> 
     <col idd="9"/> 
     <col idd="10">10</col> 
    </row> 
</root>