2017-10-20 30 views
0

J'ai un problème avec le formatage du fichier XML en utilisant XSLT pour créer du HTML. Généralement, je veux récupérer les valeurs séquentielles de chaque attribut contenu dans l'élément XML. Actuellement, il est codé en dur dans XSLT et je me rends compte que si mon fichier XML change, XSLT ne fera pas son travail. J'ai essayé d'utiliser quelque chose comme <xsl:value-of select="@(name(@*[1]))" /> pour récupérer la première valeur d'attribut de l'élément, mais cela ne fonctionne pas.XSLT récupérant séquentiellement la valeur d'attribut à partir du fichier XML

Comment faire?

Merci d'avance.

Voici comment il ressemble au moment:

<xsl:for-each select="testsuites/testsuite/testcase"> 
    <xsl:if test="@failure='PASSED'"> 
     <tr style="color:green;font-weight:bold"> 
      <td style="text-align:center"> 
       <xsl:value-of select="@classname" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@name" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@Plate" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@Distance" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@Side" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@Angle" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="@failure" /> 
      </td> 
      <td style="text-align:center"> 
       <xsl:value-of select="failure/@message" /> 
      </td> 
     </tr> 
    </xsl:if> 
    ... and so one 

Voici une partie de mon fichier XML:

<testsuites disabled="0" errors="0" failures="1" passes="16" skipped="0"  tests="17" time="1"> 
    <testsuite disabled="0" id="0" name="Bok" time="1" tests="4"> 
     <testcase classname="XYZ" name="description" Plate="blah" Distance="A" Side="L" Angle="15" failure="PASSED"> 
      <system-out/> 
      <system-err/> 
     </testcase> 
     <testcase classname="XYZ" name="description" Plate="blah" Distance="A" Side="L" Angle="60" failure="PASSED"> 
      <system-out/> 
      <system-err/> 
     </testcase> 
     <testcase classname="XYZ" name="description" Plate="blah" Distance="A" Side="L" Angle="30" failure="PASSED"> 
      <system-out/> 
      <system-err/> 
     </testcase> 
     <testcase classname="XYZ" name="description" Plate="blah" Distance="A" Side="L" Angle="60" failure="PASSED"> 
      <system-out/> 
      <system-err/> 
     </testcase> 
    </testsuite> 
    ... and so one 

EDIT: Ok , comme @TimC a répondu que je ne suis pas besoin de parenthèses et le nom dans @(name(@*[1])) et ça va pour moi.

Maintenant, le problème est de savoir comment faire une boucle qui seront des éléments itération de 1 à disons 7, je veux dire quelque chose comme ceci:

<xsl:for-each select="$var=1 to 7"> 
    <td style="text-align:center"> 
     <xsl:value-of select="@*[$var]" /> 
    </td> 
</xsl:for-each> 
+0

Dans votre exemple, l'échec n'est pas le premier attribut, en réalité il n'y a pas d'échec mais des échecs. – derloopkat

+0

@derloopkat Les attributs ne sont pas ordonnés de toute façon, leur ordre dans le code source ne signifie rien. – Tomalak

+0

bien, vous avez dit que vous voulez la première valeur d'attribut – derloopkat

Répondre

1

J'ai eu un problème avec le fichier XML de mise en forme en utilisant XSLT pour faire HTML. Généralement, je veux récupérer les valeurs séquentielles de chaque attribut contenu dans l'élément XML. Currentlz il est codé en dur dans XSLT et je réaliser que si mon fichier XML va changer, XSLT ne fera pas son travail.

Et une modification du format de l'entrée XML est-elle vraiment quelque chose dont vous devez vous soucier et dont vous devez tenir compte maintenant? Pensez-y. Dans tous les cas, le fait que la transformation existante fasse correctement son travail dans le cas d'une modification du format XML d'entrée dépend de ce que le travail de la transformation est censé être et de la manière dont le changement se produit.

Si son travail consiste à présenter un ensemble particulier de champs, dans un ordre particulier, il me semble qu'il fera son travail admirablement. En particulier, il présentera les champs sélectionnés dans un ordre cohérent, indépendamment de l'ordre dans lequel ils apparaissent dans le document d'entrée, en émettant des cellules vides pour les attributs qui ne sont pas réellement présents. Si la sortie est destinée à la consommation humaine, alors tout ce qui est probablement un bon chose.

Je essayé d'utiliser quelque chose comme pour récupérer la première valeur d'attribut de l'élément, mais il ne fonctionne pas.

Ok, comme @TimC a répondu, je n'ai pas besoin de parenthèses et de nom dans @ (nom (@ * [1])) et ça me convient.

Maintenant, le problème est de savoir comment faire une boucle qui seront des éléments de 1 itération, disons, 7

Non Si vous voulez vraiment faire cela, alors arrêter de penser comme un programmeur de procédure. XSLT n'a pas de boucles, en soi. Son mode opérationnel implique la sélection d'un ou de plusieurs nœuds, puis l'utilisation de chacun d'eux comme contexte pour l'instanciation d'un modèle. En l'absence de directives de tri, il traitera les noeuds dans l'ordre des documents. Vous essayez de le rendre beaucoup plus difficile que ce qu'il doit être.

Par exemple, si vous voulez traiter tous les attributs de chaque < testcase> nœud de contexte, tout ce que vous avez besoin est quelque chose comme ceci:

<xsl:for-each select="@*"> 
    <td style="text-align:center"> 
     <xsl:value-of select="." /> 
    </td> 
</xsl:for-each> 

Si vous voulez traiter uniquement le premier 7, quel que soit de combien il y a, puis spécifiez dans la sélection:

<xsl:for-each select="@*[position() < 8]"> 
    <td style="text-align:center"> 
     <xsl:value-of select="." /> 
    </td> 
</xsl:for-each> 

Si, d'autre part, vous voulez vous assurer que vous émettez exactement 7 < td> éléments pour chaque testcase, s'il y a que de nombreux attributs ou pas, alors soit vous sont de retour à quelque chose le long des lignes de votre origine: XSL

<td style="text-align:center"> 
    <xsl:value-of select="@*[1]" /> 
</td> 
<td style="text-align:center"> 
    <xsl:value-of select="@*[2]" /> 
</td> 
<!-- ... --> 

ou bien vous avez besoin d'écrire un modèle pour effectuer l'itération, peut-être quelque chose comme ceci:

<xsl:call-template name="iterate-testcase-attributes"> 
    <xsl:with-param name="up-to" select="7"/> 
</xsl:call-template> 

...

<xsl:template name="iterate-testcase-attributes"> 
    <xsl:param name="current" select="1"/> 
    <xsl:param name="up-to" select="1"/><!-- the select value is only a default --> 
    <td style="text-align:center"> 
    <!-- will produce nothing if the context node has no such attribute --> 
    <xsl:value-of select="@*[position() = $current]" /> 
    </td> 
    <xsl:if test="$current < $up-to"> 
    <xsl:call-template name="iterate-testcase-attributes"> 
     <xsl:with-param name="current" select="$current + 1"/> 
     <xsl:with-param name="up-to" select="$up-to"/> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
+0

Merci pour vos conseils, je dois les regarder de près, généralement je ne suis pas très familier avec le codage XSLT c'est pourquoi je pose la question;) – asdator1213

+0

Mon intention était de créer un XSLT qui n'aurait pas besoin de beaucoup de changements après que quelqu'un a ajouté plus ou moins de nouveaux attributs à XML. En tout cas merci encore pour votre aide. – asdator1213

+0

@ asdator1213, je pense que vous devez clarifier, au moins pour vous-même, quelle sortie vous voulez produire dans le cas où (1) un des attributs que vous attendez actuellement n'est pas fourni, ou (2) un attribut supplémentaire est fourni ou (3) les attributs sont présentés dans un ordre différent de celui que vous avez l'habitude de voir. Sans cela, vous n'avez aucun moyen d'évaluer les feuilles de style candidates. –

0

Comme mentionné dans les commentaires, les attributs en XML sont non-ordonnée . Si vous voulez voulez obtenir tous les attributs quel que soit l'ordre, vous pouvez le faire ...

<xsl:for-each select="@*"> 
    <td style="text-align:center"> 
    <xsl:value-of select="." /> 
    </td> 
</xsl:for-each> 

Mais, avec plusieurs éléments testcase, il n'y a aucune garantie que les attributs de chaque rangée seront dans le même ordre. Mais si vous voulez vraiment rendre votre XSLT aussi générique que possible, vous pouvez baser la commande sur la première ligne.

Essayez cette XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:template match="/"> 
    <table border="1"> 
     <xsl:variable name="attrs" select="testsuites/testsuite[1]/testcase[1]/@*" /> 
     <tr> 
     <xsl:for-each select="$attrs"> 
      <th> 
      <xsl:value-of select="local-name()" /> 
      </th> 
     </xsl:for-each> 
     </tr> 
     <xsl:for-each select="testsuites/testsuite/testcase[@failure='PASSED']"> 
     <xsl:variable name="currentCase" select="." />  
     <tr style="color:green;font-weight:bold"> 
      <xsl:for-each select="$attrs"> 
      <xsl:variable name="currentAttr" select="local-name()" />  
      <td style="text-align:center"> 
       <xsl:value-of select="$currentCase/@*[local-name() = $currentAttr]" /> 
      </td> 
      </xsl:for-each> 
     </tr> 
     </xsl:for-each> 
    </table> 
    </xsl:template> 
</xsl:stylesheet> 
+0

Merci pour votre réponse;) – asdator1213