2009-04-21 3 views
2

eine Eingabe XML-Datei mit folgendem Aufbau Gegeben:Muenchian? XSLT zum Denormalisieren/Pivotieren/Flachen der XML-Datei?

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

Wie kann ich mit Hilfe von XSLT die folgenden Struktur ausgegeben?

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

[Beachten Sie, wie alle Spalten ausgegeben werden, auch wenn es in der Eingabe nicht verwandtes Element]

EDIT: Ich kann verursacht Verwirrung durch die Verwendung von Zahlen und Buchstaben in meinem Beispiel. Die Lösung, nach der ich suche, muss Zeilen- und Spaltenattribute behandeln, die nicht numerisch sind.

+0

Möchten Sie ein Beispiel einfügen, das Ihre Eingabedaten besser darstellt? Wenn die @id-Attribute überhaupt keinen nutzbaren Wert haben (zumindest im Fragekontext), lassen Sie sie einfach weg. – Tomalak

+0

Falls die Spalten-/Zeilen-IDs nicht-numerisch sind, müssen keine Lücken gefüllt werden, so dass beide dargestellten Lösungen sogar einfacher sind. –

+0

Sie müssen Ihre Frage neu formulieren - starten Sie am besten eine neue Frage und geben Sie die genauere Definition mit nicht-numerischen ID-Werten. –

Antwort

3

Die Antworten auf diese Frage zeigen mögliche Wege, das Problem zu nähern:

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


EDIT: Eine Lösung, die die Techniken in die verknüpfte Frage folgt gesehen enthält.

Ich gehe davon aus:

  • Ihre @row und @col Attribute Nummern Erhöhen, der die Position des Datensatzes in der Tabelle zu definieren, und sie können nicht wirklich die Zeichenfolge "n" enthalten. Daher sind sie im gesamten Dokument nicht eindeutig, was sie als HTML-Attribute ungeeignet macht. Ich habe sie durch @title Attribute in meiner Ausgabe ersetzt.
  • Es gibt keine implizit leeren Zeilen (Lücken in @row Kontinuität wird keine leeren Zeilen erzeugen), nur implizite leere Zellen.
  • jede @row und @col Kombination ist einzigartig.

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

... wenn sie auf diese aufgebracht (leicht modifiziert) Eingabe:

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

... produziert:

<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> 
  • Muench-Gruppierung bist du? sed die ersten <record> s jedes @row Gruppe
  • ein <xsl:key> wird verwendet, um auszuwählen, einen Datensatz zu lokalisieren, indem er Position ist
  • Rekursion verwendet, um eine konsistente Menge von <td> s, unabhängig von der tatsächlichen Existenz eines <record> bei der produzieren benannte Position
+0

Eine sehr schöne und komplette Lösung! Ich habe nichts hinzuzufügen. :) –

+0

Danke. :-) (Ich denke, ich hätte die Technik "Iterieren ohne Rekursion" verwenden können, die ich aus Ihrer Antwort in der Frage gelernt habe, die ich verlinkt habe). – Tomalak

+0

Tomalak - Danke für die Lösung; Allerdings habe ich in meinem Beispiel durch die Verwendung von Zahlen und Buchstaben Verwirrung stiften können. Die Lösung, nach der ich suche, muss Zeilen- und Spaltenattribute behandeln, die nicht numerisch sind. – eft

2

Ein XSLT 2.0 Lösung

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

wenn auf diesem XML-Dokument angewendet:

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

erzeugt das gewünschte Ergebnis:

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