2011-09-23 8 views
0

J'ai des données XHTML faiblement structurées et j'ai besoin de les convertir en XML mieux structuré.Une transformation XSLT complexe

Voici l'exemple:

<tbody> 
<tr> 
    <td class="header"><img src="http://www.abc.com/images/icon_apples.gif"/><img src="http://www.abc.com/images/flag/portugal.gif" alt="Portugal"/> First Grade</td> 
</tr> 
<tr> 
    <td>Green</td> 
    <td>Round shaped</td> 
    <td>Tasty</td> 
</tr> 
<tr> 
    <td>Red</td> 
    <td>Round shaped</td> 
    <td>Bitter</td> 
</tr> 
<tr> 
    <td>Pink</td> 
    <td>Round shaped</td> 
    <td>Tasty</td> 
</tr> 
<tr> 
    <td class="header"><img src="http://www.abc.com/images/icon_strawberries.gif"/><img src="http://www.abc.com/images/flag/usa.gif" alt="USA"/> Fifth Grade</td> 
</tr> 
<tr> 
    <td>Red</td> 
    <td>Heart shaped</td> 
    <td>Super tasty</td> 
</tr> 
<tr> 
    <td class="header"><img src="http://www.abc.com/images/icon_bananas.gif"/><img src="http://www.abc.com/images/flag/congo.gif" alt="Congo"/> Third Grade</td> 
</tr> 
<tr> 
    <td>Yellow</td> 
    <td>Smile shaped</td> 
    <td>Fairly tasty</td> 
</tr> 
<tr> 
    <td>Brown</td> 
    <td>Smile shaped</td> 
    <td>Too sweet</td> 
</tr> 

Je suis en train de réaliser la structure suivante:

<data> 
    <entry> 
     <type>Apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Green</color> 
     <shape>Round shaped</shape> 
     <taste>Tasty</taste> 
    </entry> 
    <entry> 
     <type>Apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Red</color> 
     <shape>Round shaped</shape> 
     <taste>Bitter</taste> 
    </entry> 
    <entry> 
     <type>Apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Pink</color> 
     <shape>Round shaped</shape> 
     <taste>Tasty</taste> 
    </entry> 
    <entry> 
     <type>Strawberries</type> 
     <country>USA</country> 
     <rank>Fifth Grade</rank> 
     <color>Red</color> 
     <shape>Heart shaped</shape> 
     <taste>Super</taste> 
    </entry> 
    <entry> 
     <type>Bananas</type> 
     <country>Congo</country> 
     <rank>Third Grade</rank> 
     <color>Yellow</color> 
     <shape>Smile shaped</shape> 
     <taste>Fairly tasty</taste> 
    </entry> 
    <entry> 
     <type>Bananas</type> 
     <country>Congo</country> 
     <rank>Third Grade</rank> 
     <color>Brown</color> 
     <shape>Smile shaped</shape> 
     <taste>Too sweet</taste> 
    </entry> 
</data> 

Tout d'abord je besoin d'extraire le type de fruit de la tbody/tr/td/img [1]/@ src, d'autre part le pays de tbody/tr/td/img [2]/@ alt attribut et fina lly le grade de tbody/tr/td lui-même.

Ensuite, j'ai besoin de remplir toutes les entrées sous chaque catégorie tout en incluant ces valeurs (comme montré ci-dessus). Mais ... Comme vous pouvez le voir, les données qui m'ont été données sont très faiblement structurées. Une catégorie est simplement un td et après cela viennent tous les éléments de cette catégorie. Pour aggraver les choses, dans mes jeux de données, le nombre d'éléments sous chaque catégorie varie entre 1 et 100 ...

J'ai essayé quelques approches mais je n'arrive pas à l'obtenir. Toute aide est grandement appréciée. Je sais que XSLT 2.0 introduit xsl: for-each-group, mais je suis limité à XSLT 1.0.

Répondre

3

Dans ce cas, vous ne regroupez pas les éléments. C'est plus comme les dégrouper.

Pour ce faire, vous pouvez utiliser une clé xsl: key pour rechercher la ligne "header" de chaque ligne de détail.

<xsl:key name="fruity" 
    match="tr[not(td[@class='header'])]" 
    use="generate-id(preceding-sibling::tr[td[@class='header']][1])"/> 

i.e Pour chaque ligne de détail, obtenez la ligne d'en-tête la plus précédente.

Ensuite, vous pouvez alors correspondre à toutes vos lignes d'en-tête comme ceci:

<xsl:apply-templates select="tr/td[@class='header']"/> 

Dans le modèle correspondant, vous pouvez alors extraire le type, le pays et le rang. Ensuite, pour obtenir les lignes de détail associées, il est un cas simple de regarder la clé de la ligne parente:

<xsl:apply-templates select="key('fruity', generate-id(..))"> 

Voici XSLT globale

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:key name="fruity" 
     match="tr[not(td[@class='header'])]" 
     use="generate-id(preceding-sibling::tr[td[@class='header']][1])"/> 

    <xsl:template match="/tbody"> 
     <data> 
     <!-- Match header rows --> 
     <xsl:apply-templates select="tr/td[@class='header']"/> 
     </data> 
    </xsl:template> 

    <xsl:template match="td"> 
     <!-- Match associated detail rows --> 
     <xsl:apply-templates select="key('fruity', generate-id(..))"> 
     <!-- Extract relevant parameters from the td cell --> 
     <xsl:with-param name="type" select="substring-before(substring-after(img[1]/@src, 'images/icon_'), '.gif')"/> 
     <xsl:with-param name="country" select="img[2]/@alt"/> 
     <xsl:with-param name="rank" select="normalize-space(text())"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="tr"> 
     <xsl:param name="type"/> 
     <xsl:param name="country"/> 
     <xsl:param name="rank"/> 
     <entry> 
     <type> 
      <xsl:value-of select="$type"/> 
     </type> 
     <country> 
      <xsl:value-of select="$country"/> 
     </country> 
     <rank> 
      <xsl:value-of select="$rank"/> 
     </rank> 
     <color> 
      <xsl:value-of select="td[1]"/> 
     </color> 
     <shape> 
      <xsl:value-of select="td[2]"/> 
     </shape> 
     <taste> 
      <xsl:value-of select="td[3]"/> 
     </taste> 
     </entry> 
    </xsl:template> 
</xsl:stylesheet> 

Appliqué à votre document d'entrée, le la sortie suivante est générée:

<data> 
    <entry> 
     <type>apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Green</color> 
     <shape>Round shaped</shape> 
     <taste>Tasty</taste> 
    </entry> 
    <entry> 
     <type>apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Red</color> 
     <shape>Round shaped</shape> 
     <taste>Bitter</taste> 
    </entry> 
    <entry> 
     <type>apples</type> 
     <country>Portugal</country> 
     <rank>First Grade</rank> 
     <color>Pink</color> 
     <shape>Round shaped</shape> 
     <taste>Tasty</taste> 
    </entry> 
    <entry> 
     <type>strawberries</type> 
     <country>USA</country> 
     <rank>Fifth Grade</rank> 
     <color>Red</color> 
     <shape>Heart shaped</shape> 
     <taste>Super tasty</taste> 
    </entry> 
    <entry> 
     <type>bananas</type> 
     <country>Congo</country> 
     <rank>Third Grade</rank> 
     <color>Yellow</color> 
     <shape>Smile shaped</shape> 
     <taste>Fairly tasty</taste> 
    </entry> 
    <entry> 
     <type>bananas</type> 
     <country>Congo</country> 
     <rank>Third Grade</rank> 
     <color>Brown</color> 
     <shape>Smile shaped</shape> 
     <taste>Too sweet</taste> 
    </entry> 
</data> 
+0

+1 pour une bonne réponse. –