2010-11-16 6 views
2

Pour un xml donné, j'ai besoin de générer une table html pour représenter les valeurs dans le xml. J'ai besoin de la récursivité pour n'importe quelle cléN, si la valeurN est du texte alors il suffit de l'imprimer. Si valeurN est xml, imprimez une table (imbriquée) avec sa valeur. Je pense que mon manque de compréhension de la façon d'utiliser correctement la récursivité XSLT est la base de la question. Toute aide appréciée.Comment utiliser la récursivité XSLT pour convertir des données xml en vue table html:

Entrée:

<root> 
    <key1> Text Value </key1> 
<key2> 
    <a> aaa </a> 
    <b> bbb </b> 
</key2> 
<keyN> valueN </keyN> 
<root> 

Sortie:

<table border="1px"> 
    <tr> 
     <td> key1 </td> 
     <td> Text Value </td> 
    </tr> 

    <tr> 
     <td> key2 </td> 
     <td> 
      <table border="1px"> 
       <tr> <td> a </td> <td> aaa </td> </tr> 
       <tr> <td> b </td> <td> bbb </td> </tr> 
      </table> 
     </td> 
    </tr> 

    <tr> 
     <td> keyN </td> 
     <td> 
      valueN (if valueN is text) 
        OR 
      <table> ... </table> (if valueN is xml) 
     <td> 
    </tr> 
</table> 
+0

Bonne question, +1. Voyez ma solution pour une solution XSLT courte et très puissante, tout à fait dans l'esprit de XSLT. :) –

Répondre

5

Cette feuille de style:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/*//*[1]"> 
     <table border="1"> 
      <xsl:call-template name="makeRow"/> 
     </table> 
    </xsl:template> 
    <xsl:template match="*" name="makeRow"> 
     <tr> 
      <td> 
       <xsl:value-of select="name()"/> 
      </td> 
      <td> 
       <xsl:apply-templates select="node()[1]"/> 
      </td> 
     </tr> 
     <xsl:apply-templates select="following-sibling::node()[1]"/> 
    </xsl:template> 
    <xsl:template match="/*"> 
     <xsl:apply-templates select="node()[1]"/> 
    </xsl:template> 
</xsl:stylesheet> 

Sortie:

<table border="1"> 
    <tr> 
     <td>key1</td> 
     <td> Text Value </td> 
    </tr> 
    <tr> 
     <td>key2</td> 
     <td> 
      <table border="1"> 
       <tr> 
        <td>a</td> 
        <td> aaa </td> 
       </tr> 
       <tr> 
        <td>b</td> 
        <td> bbb </td> 
       </tr> 
      </table></td> 
    </tr> 
    <tr> 
     <td>keyN</td> 
     <td> valueN </td> 
    </tr> 
</table> 

Remarque: Cette utilisation du boniment traversal à grains fins. Trois règles: "premier enfant descendant de l'élément racine", sortie table et appel makeRow; makeRow (correspond à tout élément n'étant pas le premier descendant de l'enfant ni l'élément racine) afficher tr et les cellules de tableau avec le nom et la première application enfant, puis appliquer les modèles au frère suivant; règle de l'élément racine, lancez la traversée à grain fin.

+0

+1 pour une bonne solution. –

+0

@Alejandro, très belle solution. –

+0

+1 C'est génial! Je voudrais savoir xslt comme vous :) – sanjayav

0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="/"> 
     <html> 
      <body> 
       <!-- apply root as a table --> 
       <xsl:apply-templates select="root" mode="table"/> 
      </body> 
     </html> 
    </xsl:template> 

    <xsl:template match="node()[not(self::text())]" mode="table"> 
     <table border="1px"> 
      <!-- create table and process children as rows --> 
      <xsl:apply-templates select="node()" mode="row"/> 
     </table> 
    </xsl:template> 

    <xsl:template match="node()[not(self::text())]" mode="row"> 
     <tr> 
      <!-- make decision here. 
       If row contains only text - apply is as a simple row 
       In case there are some other nodes proces them as a table. 
      --> 
      <xsl:choose> 
       <xsl:when test=". = text()"> 
        <xsl:apply-templates select="." mode="column"/> 
       </xsl:when> 
       <xsl:otherwise> 
        <td><xsl:value-of select="name()"/></td> 
        <td><xsl:apply-templates select="." mode="table"/></td> 
       </xsl:otherwise> 
      </xsl:choose> 
     </tr> 
    </xsl:template> 

    <xsl:template match="node()" mode="column"> 
     <td><xsl:value-of select="name()"/></td> 
     <td><xsl:value-of select="."/></td> 
    </xsl:template> 
</xsl:stylesheet> 

Sortie:

<html> 
    <body> 
     <table border="1px"> 
      <tr> 
       <td>key1</td> 
       <td>Text Value</td> 
      </tr> 

      <tr> 
       <td>key2</td> 
       <td> 
       <table border="1px"> 
        <tr> 
         <td>a</td> 
         <td>aaa</td> 
        </tr> 

        <tr> 
         <td>b</td> 
         <td>bbb</td> 
        </tr> 
       </table> 
       </td> 
      </tr> 

      <tr> 
       <td>keyN</td> 
       <td>valueN</td> 
      </tr> 

     </table> 
    </body> 
</html> 

BTW il sera préférable de réécrire xsl: choisir comme deux modèles distincts.

+0

merci pour cette solution. – sanjayav

0

Cela sonne comme devoirs :-)

Vous travaillez avec 2 choses ici:

  1. éléments Arbitrairement nommés que vous devez appliquer un modèle XSLT, et
  2. Déterminer quel type du contenu d'un élément (texte ou fragment XML).

Vous souhaitez utiliser un modèle qui peut correspondre arbitrairement à des éléments de votre fichier XML (c'est-à-dire pas des éléments avec un nom spécifique). <xsl:template match="*"> correspond à tous les éléments de votre document XML.

Lorsque vous rencontrez un élément, vous devez créer une table:

<xsl:template match="*"> 
    <table border="1px"> 
    </table> 
</xsl:template> 

Maintenant, nous voulons travailler si nous avons affaire à un fragment XML (un élément) ou un morceau de texte. Pour ce faire, nous faisons correspondre node(). Rappelez-vous qu'un nœud peut être un élément, un texte, un espace, une instruction de traitement ou un commentaire dans un document XML. Lorsque vous avez un nœud jumelé, vous souhaitez créer une nouvelle ligne de table et d'afficher le nom du noeud courant:

<xsl:template match="node()"> 
    <tr> 
    <td> 
     <xsl:value-of select="local-name()"/> 
    </td> 
    </tr> 
</xsl:template> 

Vous devez ensuite travailler si le nœud est un nœud de texte ou non. Vous pouvez utiliser un <xsl:if> ou un <xsl:choose>. J'ai tendance à préférer ce dernier. S'il s'agit d'un nœud de texte, affichez la valeur du texte, sinon traitez le nœud comme un fragment XML et appelez à nouveau notre modèle initial (ceci est la partie récursive).

... 
<xsl:choose> 
    <xsl:when test="current() = text()"> 
    <td> 
     <xsl:value-of select="." /> 
    </td> 
    </xsl:when> 
    <xsl:otherwise> 
    <td> 
     <xsl:apply-templates select="*" mode="table"/> 
    </td> 
    </xsl:otherwise> 
</xsl:choose> 
... 

est ici la solution finale.

<xsl:template match="/root"> 
    <xsl:apply-templates select="*" mode="table" /> 
</xsl:template> 

<xsl:template match="*" mode="table"> 
    <table border="1px"> 
    <xsl:apply-templates select="." mode="table-row" /> 
    </table> 
</xsl:template> 

<xsl:template match="node()" mode="table-row"> 
    <tr> 
    <td> 
     <xsl:value-of select="local-name()"/> 
    </td> 
    <xsl:choose> 
     <xsl:when test="current() = text()"> 
     <td> 
      <xsl:value-of select="." /> 
     </td> 
     </xsl:when> 
     <xsl:otherwise> 
     <td> 
      <xsl:apply-templates select="*" mode="table"/> 
     </td> 
     </xsl:otherwise> 
    </xsl:choose> 
    </tr> 
</xsl:template> 

J'utilise l'attribut mode sur le modèle parce qu'un élément est également un noeud dans un document XML et <xsl:apply-templates select="*"/> vous trouverez aussi bien <xsl:template match="*"> ainsi que <xsl:template match="node()">. L'utilisation de l'attribut mode supprime l'ambiguïté.

+0

Eh bien c'est le travail, pas le travail à domicile :). Merci pour la réponse. – sanjayav

3

Cette transformation:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/*"> 
    <table border="1px"> 
    <xsl:apply-templates/> 
    </table> 
</xsl:template> 

<xsl:template match="*[*][parent::*]"> 
    <tr> 
     <td><xsl:value-of select="name()"/></td> 
     <td> 
     <table border="1px"> 
      <xsl:apply-templates/> 
     </table> 
     </td> 
    </tr> 
</xsl:template> 

<xsl:template match="*[not(*)]"> 
    <tr> 
     <td><xsl:value-of select="name()"/></td> 
    <td><xsl:value-of select="."/></td> 
    </tr> 
</xsl:template> 
</xsl:stylesheet> 

lorsqu'il est appliqué sur le document XML fourni:

<root> 
    <key1> Text Value </key1> 
    <key2> 
     <a> aaa </a> 
     <b> bbb </b> 
    </key2> 
    <keyN> valueN </keyN> 
</root> 

produit le résultat souhaité, correct:

<table border="1px"> 
    <tr> 
     <td>key1</td> 
     <td> Text Value </td> 
    </tr> 
    <tr> 
     <td>key2</td> 
     <td> 
     <table border="1px"> 
      <tr> 
       <td>a</td> 
       <td> aaa </td> 
      </tr> 
      <tr> 
       <td>b</td> 
       <td> bbb </td> 
      </tr> 
     </table> 
     </td> 
    </tr> 
    <tr> 
     <td>keyN</td> 
     <td> valueN </td> 
    </tr> 
</table> 

Do noter la puissance de XSLT:

  1. Pas de récursion explicite.

  2. Aucun conditionnel dans aucun modèle.

  3. Complètement pousser-style de traitement.

+1

la table après ' key2' devrait être enveloppée dans le '' lui-même. Très belle solution. –

+0

@Alex Nikolaenkov: Merci d'avoir remarqué cela. Fixé maintenant –

+0

J'aime le style push. Bien joué! –

0

AJOUTÉE POUR LES QUALITÉS FONCTIONNALITÉ PRESENTANT

<xsl:stylesheet version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
     <xsl:template match="/*//*[1]"> 
      <table border="1"> 
       <xsl:call-template name="makeRow" /> 
      </table> 
     </xsl:template> 
     <xsl:template match="*" name="makeRow"> 
      <tr> 
       <td><xsl:value-of select="name()" /> <xsl:if 
        test="count(@*) > 0"> 
        <table> 
         <xsl:apply-templates select="@*" /> 
        </table> 
       </xsl:if></td> 
       <td><xsl:apply-templates select="node()[1]" /></td> 
      </tr> 
      <xsl:apply-templates select="following-sibling::node()[1]" /> 
     </xsl:template> 
     <xsl:template match="/*"> 
      <xsl:apply-templates select="node()[1]" /> 
     </xsl:template> 
     <xsl:template match="@*"> 
      <tr> 
       <td>@<xsl:value-of select="name()" /></td> 
       <td><xsl:value-of select="." /></td> 
      </tr> 
     </xsl:template> 
    </xsl:stylesheet> 
+0

Veuillez également poster une explication de la réponse. –

Questions connexes