2009-06-04 7 views
3

Ich habe einige hierarchisches XML wie diese bekommen:XSLT XML-Hierarchie zu HTML-Tabelle abflachen

<node text="a" value="1"> 
    <node text="gga" value="5"> 
    <node text="dh" value="9"> 
     <node text="tyfg" value="4"> 
     </node> 
    </node> 
    </node> 
    <node text="dfhgf" value="7"> 
    <node text="fdsg" value="2"> 
    </node> 
    </node> 
</node> 

Die Namen der Elemente sind die gleichen ganzen Weg nach unten („Knoten“), und die Tiefe die Hierarchie ist vorher nicht bekannt - in der obigen Probe ist das tiefste Blatt vier unten, aber es kann jede Tiefe haben.

Was ich tun muss, ist diese XML zu nehmen und es in eine HTML-Tabelle abzuflachen. Die Anzahl der Spalten in der Tabelle sollte der Tiefe des tiefsten Elements plus einer Spalte für das Wertattribut jedes Elements entsprechen. Der "Wert" sollte in der rechten Spalte der Tabelle angezeigt werden, damit die Ausgabezeilen keine ausgefransten Kanten haben können. Es sollte für jeden Knoten eine Zeile geben, unabhängig davon, auf welcher Ebene er sich befindet. Das obige Beispiel sollte in transformiert werden:

<table> 
    <tr> 
    <td>a</td> 
    <td></td> 
    <td></td> 
    <td></td> 
    <td>1</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td></td> 
    <td></td> 
    <td>5</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td>dh</td> 
    <td></td> 
    <td>9</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td>dh</td> 
    <td>tyfg</td> 
    <td>4</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>dfhgf</td> 
    <td></td> 
    <td></td> 
    <td>7</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>dfhgf</td> 
    <td>fdsg</td> 
    <td></td> 
    <td>2</td> 
    </tr> 
</table> 

Hat jemand einige clevere XSLT, die dies erreichen können?

+0

Es sieht aus wie Sie versuchen, um die Struktur zu visualisieren, die Kette von Eltern el wiederholen in jeder Reihe. Brauchst du es wirklich, um ein Tisch zu sein? Das Ergebnis scheint mir nicht wirklich tabellarisch zu sein. – Rytmis

+0

Ja, das Ziel ist es, die Struktur zu visualisieren. Ein Tisch schien mir die beste Wahl zu sein, aber mein Kopf ist offen für alternative Vorschläge. Wie du sagst, was ich wirklich brauche, ist eine Kette von Elementen in jeder Reihe. – Matt

Antwort

3

Es ist nicht ganz das, was man braucht (weil es einen gezackten Tisch verläßt), aber es wird

in html arbeitet noch
<xsl:template match="/"> 
    <html> 
     <head> 
     </head> 
     <body> 
      <table> 
       <xsl:apply-templates select="//node" mode="row" /> 
      </table> 
     </body> 
    </html> 
</xsl:template> 

<xsl:template match="node" mode="row"> 
    <tr> 
     <xsl:apply-templates select="ancestor-or-self::node" mode="popcell"/> 
     <xsl:apply-templates select="node[1]" mode="emptycell"/> 
    </tr> 
</xsl:template> 

<xsl:template match="node" mode="popcell"> 
    <td><xsl:value-of select="@text"/></td> 
</xsl:template> 

<xsl:template match="node" mode="emptycell"> 
    <td></td> 
    <xsl:apply-templates select="node[1]" mode="emptycell"/> 
</xsl:template> 

Version 2: Nun bin ich wesentlich weniger selbstzufrieden mit ihm : P, aber das Folgende entfernt die Zackigkeit:

<xsl:variable name="depth"> 
    <xsl:for-each select="//node"> 
     <xsl:sort select="count(ancestor::node)" data-type="number" order="descending"/> 
     <xsl:if test="position()=1"> 
      <xsl:value-of select="count(ancestor::node)+1"/> 
     </xsl:if> 
    </xsl:for-each> 
</xsl:variable> 

<xsl:template match="/"> 
    <html> 
     <head> 
     </head> 
     <body> 
      <table> 
       <xsl:apply-templates select="//node" mode="row" /> 
      </table> 
     </body> 
    </html> 
</xsl:template> 

<xsl:template match="node" mode="row"> 
    <tr> 
     <xsl:apply-templates select="ancestor-or-self::node" mode="popcell"/> 
     <xsl:call-template name="emptycells"> 
      <xsl:with-param name="n" select="($depth)-count(ancestor-or-self::node)"/> 
     </xsl:call-template> 
     <td><xsl:value-of select="@value"/></td> 
    </tr> 
</xsl:template> 

<xsl:template match="node" mode="popcell"> 
    <td><xsl:value-of select="@text"/></td> 
</xsl:template> 

<xsl:template name="emptycells"> 
    <xsl:param name="n" /> 
    <xsl:if test="$n &gt; 0"> 
     <td></td> 
     <xsl:call-template name="emptycells"> 
      <xsl:with-param name="n" select="($n)-1"/> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
+0

+1 Dies ist eine erstaunliche xslt :) –

+0

Danke :) Manchmal ist die Eleganz von XSLT wirklich erstaunlich. – annakata

+0

Hallo Annakata, danke dafür! Es ist nah an dem, was ich brauche, leider ist der gezackte Tisch ein Problem. In meinem ersten Entwurf der Frage habe ich mein Problem vereinfacht. Ich habe es jetzt geändert, deshalb sollte der Grund, warum ich keine gezackten Ränder haben kann, klar sein. – Matt

0

Diese XSLT 1.0 Lösung würde es tun.

  • erzeugt eine wohlgeformte HTML-Tabelle
  • keine Rekursion

XSLT-Code verwendet:

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

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

    <!-- some preparation --> 
    <xsl:variable name="vAllNodes" select="//node" /> 

    <!-- find out the deepest nested node --> 
    <xsl:variable name="vMaxDepth"> 
    <xsl:for-each select="$vAllNodes"> 
     <xsl:sort 
     select="count(ancestor::node)" 
     data-type="number" 
     order="descending" 
     /> 
     <xsl:if test="position() = 1"> 
     <xsl:value-of select="count(ancestor-or-self::node)" /> 
     </xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 

    <!-- select a list of nodes, merely to iterate over them --> 
    <xsl:variable name="vIteratorList" select=" 
    $vAllNodes[position() &lt;= $vMaxDepth] 
    " /> 

    <!-- build the table --> 
    <xsl:template match="/"> 
    <table> 
     <!-- the rows will be in document order --> 
     <xsl:apply-templates select="$vAllNodes" /> 
    </table> 
    </xsl:template> 

    <!-- build the rows --> 
    <xsl:template match="node"> 
    <xsl:variable name="self" select="." /> 
    <tr> 
     <!-- iteration instead of recursion --> 
     <xsl:for-each select="$vIteratorList"> 
     <xsl:variable name="vPos" select="position()" /> 
     <td> 
      <!-- the ancestor axis is indexed the other way around --> 
      <xsl:value-of select=" 
      $self/ancestor-or-self::node[last() - $vPos + 1]/@text 
      " /> 
     </td> 
     </xsl:for-each> 
     <td> 
     <xsl:value-of select="@value" /> 
     </td> 
    </tr> 
    </xsl:template> 

</xsl:stylesheet> 

Ausgang :

<table> 
    <tr> 
    <td>a</td> 
    <td></td> 
    <td></td> 
    <td></td> 
    <td>1</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td></td> 
    <td></td> 
    <td>5</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td>dh</td> 
    <td></td> 
    <td>9</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>gga</td> 
    <td>dh</td> 
    <td>tyfg</td> 
    <td>4</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>dfhgf</td> 
    <td></td> 
    <td></td> 
    <td>7</td> 
    </tr> 
    <tr> 
    <td>a</td> 
    <td>dfhgf</td> 
    <td>fdsg</td> 
    <td></td> 
    <td>2</td> 
    </tr> 
</table> 
+0

Danke für diesen Tomalak, es funktioniert sehr gut. Ich machte eine winzige Änderung der Ausgabe erhalten wollte ich - geändert: Sonst sind die Textzellen in der Zeile in umgekehrter Reihenfolge (dh Blattknoten Text ist in der ganz linken Spalte) – Matt

+0

Eigentlich sollte dies nicht passiert sein.$ vNodes wird aus der Vorgängerachse erstellt, was bedeutet, dass $ vNodes [1] das Blatt sein sollte. Welchen XSLT-Prozessor verwenden Sie? – Tomalak

+0

Visual Studio 2008 – Matt