67 votes

Dans quel ordre les modèles d'un document XSLT s'exécutent-ils, et correspondent-ils au XML source ou à la sortie en mémoire tampon ?

Voici une chose qui m'a toujours mystifié à propos de XSLT :

  1. Dans quel ordre les modèles s'exécutent-ils, et
  2. Lorsqu'ils s'exécutent, correspondent-ils (a) au XML source original ou (b) à la sortie actuelle du XSLT à ce stade ?

Exemple :

<person>
  <firstName>Deane</firstName>
  <lastName>Barker</lastName>
</person>

Voici un fragment de XSLT :

<!-- Template #1 -->
<xsl:template match="/">
  <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="/person/firstName">
  First Name: <xsl:value-of select="firstName"/>
</xsl:template>

Deux questions à ce sujet :

  1. Je suppose que le modèle n°1 s'exécutera en premier. Je ne sais pas pourquoi je suppose cela - est-ce simplement parce qu'il apparaît en premier dans le document ?
  2. Le modèle n°2 sera-t-il exécuté ? Il correspond à un nœud dans le XML source, mais le temps que nous arrivions à ce modèle (en supposant qu'il s'exécute en second), le nœud "firstName" ne sera pas dans l'arbre de sortie.

Ainsi, les modèles "ultérieurs" sont-ils redevables de ce qui s'est produit dans les modèles "antérieurs", ou opèrent-ils sur le document source, sans tenir compte de ce qui a été transformé "avant" eux ? (Tous ces mots sont entre guillemets, parce que je trouve difficile de discuter de questions temporelles quand je n'ai vraiment aucune idée de la façon dont l'ordre des modèles est déterminé en premier lieu...)

Dans l'exemple ci-dessus, nous avons un modèle qui correspond au nœud racine ("/") et qui, lorsqu'il a fini de s'exécuter, a essentiellement supprimé tous les nœuds de la sortie. Dans ce cas, cela empêcherait-il tous les autres modèles de s'exécuter puisqu'il n'y a rien à mettre en correspondance une fois que le premier modèle est terminé ?

Jusqu'à présent, je me suis préoccupé du fait que les modèles ultérieurs ne s'exécutent pas parce que les nœuds sur lesquels ils ont opéré n'apparaissent pas dans la sortie, mais qu'en est-il de l'inverse ? Un modèle "antérieur" peut-il créer un nœud sur lequel un modèle "postérieur" peut agir ?

Sur le même XML que ci-dessus, considérez cette XSL :

<!-- Template #1 -->
<xsl:template match="/">
  <fullName>
    <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
  </fullName>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="//fullName">
  Full Name: <xsl:value-of select="."/>
</xsl:template>

Le modèle n° 1 crée un nouveau nœud appelé "fullName". Le modèle n°2 correspond à ce même nœud. Le modèle n° 2 s'exécutera-t-il parce que le nœud "fullName" existe dans la sortie au moment où nous arrivons au modèle n° 2 ?

Je réalise que je suis profondément ignorant du "zen" de XSLT. Jusqu'à présent, mes feuilles de style ont consisté en un modèle correspondant au nœud Root, puis sont complètement procédurales à partir de là. J'en ai assez de faire cela. Je préférerais comprendre correctement le XSLT, d'où ma question.

84voto

Evan Lenz Points 2793

J'aime votre question. Vous vous exprimez très bien sur ce que vous ne comprenez pas encore. Vous avez juste besoin de quelque chose pour relier les choses ensemble. Je vous recommande de lire "Comment fonctionne XSLT" un chapitre que j'ai écrit pour répondre exactement aux questions que vous posez. J'aimerais savoir si cela vous permet de faire le point.

De manière moins formelle, je vais tenter de répondre à chacune de vos questions.

  1. Dans quel ordre les modèles s'exécutent-ils, et
  2. Lorsqu'ils s'exécutent, correspondent-ils (a) au XML source d'origine ou (b) à la sortie actuelle du XSLT pour le transformer ? la sortie actuelle du XSLT à ce stade ? ce point ?

À n'importe quel moment du traitement XSLT, il y a, en quelque sorte, deux contextes, que vous identifiez comme (a) et (b) : où vous êtes dans le arbre source et où vous vous trouvez dans le arbre des résultats . L'endroit où vous vous trouvez dans l'arbre des sources s'appelle le noeud actuel . Il peut changer et sauter tout autour de l'arbre source, car vous choisissez des ensembles arbitraires de nœuds à traiter à l'aide de XPath. Cependant, d'un point de vue conceptuel, on ne "saute" jamais autour de l'arbre des résultats de la même manière. Le processeur XSLT le construit de manière ordonnée ; il crée d'abord le nœud racine de l'arbre de résultat ; puis il ajoute des enfants, construisant le résultat dans l'ordre du document (profondeur d'abord). [Votre post me motive à reprendre mes expériences de visualisation logicielle pour XSLT...].

L'ordre des règles du modèle dans une feuille de style n'a jamais d'importance. Vous ne pouvez pas savoir, juste en regardant la feuille de style, dans quel ordre les règles du modèle seront instanciées, combien de fois une règle sera instanciée, ou même si elle le sera tout court. ( match="/" est une exception ; on peut toujours savoir qu'elle sera déclenchée).

Je suppose que le modèle n°1 sera s'exécutera en premier. Je ne sais pas pourquoi je suppose cela -- est-ce juste parce qu'il apparaît en premier dans le document ?

Non. Il sera appelé en premier même si vous le placez en dernier dans le document. L'ordre des règles de gabarit n'a jamais d'importance (sauf en cas d'erreur lorsque plusieurs règles de gabarit avec la même priorité correspondent au même noeud ; même dans ce cas, c'est facultatif pour l'implémenteur et vous ne devriez jamais compter sur un tel comportement). Elle est appelée en premier parce que la première chose que toujours se produit chaque fois que vous exécutez un processeur XSLT est un appel virtuel à <xsl:apply-templates select="/"/> . L'appel virtuel unique construit l'arbre de résultat entier. Rien ne se passe en dehors de celui-ci. Vous pouvez personnaliser, ou "configurer", le comportement de cette instruction en définissant des règles de modèle.

Le modèle n°2 sera-t-il exécuté ? Il correspond à un nœud dans le XML source, mais le temps que l'on arrive à ce modèle (en supposant qu'il s'exécute en second), le noeud "firstName" ne sera pas dans l'arbre de sortie.

Le modèle n° 2 (ni aucune autre règle de modèle) ne sera jamais déclenché à moins que vous n'ayez une règle de type <xsl:apply-templates/> quelque part dans le match="/" règle. Si vous n'en avez pas, alors aucune règle de modèle autre que match="/" sera déclenché. Pensez-y de cette façon : pour qu'une règle de modèle soit déclenchée, elle ne peut pas simplement correspondre à un nœud de l'entrée. Elle doit correspondre à un nœud que vous avez choisi de faire correspondre à la règle de modèle. processus (en utilisant <xsl:apply-templates/> ). Inversement, il continuera à faire correspondre le nœud autant de fois que vous choisirez de le traiter.

Est-ce que [le match="/" modèle] empêchent tous les autres modèles de s'exécuter puisqu'il n'y a rien à faire correspondre après que le premier modèle est terminé ?

Cette règle prévaut sur les autres en n'incluant nulle part <xsl:apply-templates/> en elle. Il y a encore beaucoup de noeuds qui pourrait être traitées dans l'arbre source. Ils sont toujours là, mûrs pour la cueillette ; traitez chacun d'eux autant de fois que vous le souhaitez. Mais la seule façon de les traiter en utilisant des règles de modèle est d'appeler <xsl:apply-templates/> .

Jusqu'à présent, j'ai été préoccupé avec les modèles ultérieurs ne s'exécutant pas parce que les noeuds sur lesquels ils ont opéré n'apparaissent pas dans la sortie, mais mais qu'en est-il de l'inverse ? Est-ce qu'un modèle modèle "antérieur" peut-il créer un nœud sur lequel un modèle "ultérieur" peut faire quelque chose avec ?

Ce n'est pas qu'un modèle "antérieur" crée un nouveau nœud à traiter ; c'est qu'un modèle "antérieur" traite à son tour d'autres nœuds de l'arbre source, en utilisant cette même instruction ( <xsl:apply-templates ). On peut considérer qu'il s'agit d'appeler la même "fonction" de manière récursive, avec des paramètres différents à chaque fois (les nœuds à traiter, déterminés par le contexte et l'attribut select ).

Au final, vous obtenez une pile arborescente d'appels récursifs à la même "fonction" ( <xsl:apply-templates> ). Et cette arborescence est isomorphique à votre résultat réel. Tout le monde ne s'en rend pas compte ou n'y a pas pensé de cette façon ; c'est parce que nous ne disposons pas d'outils de visualisation efficaces... pour l'instant.

Le modèle #1 crée un nouveau noeud appelé "nom complet". Le modèle n°2 correspond à ce même noeud. Le modèle n°2 sera-t-il exécuté parce que le nœud "fullName" existe existe dans la sortie au moment où nous que nous arrivions au modèle n°2 ?

Non. La seule façon de faire une chaîne de traitement est de la configurer explicitement de cette façon. Créez une variable, par exemple $tempTree qui contient la nouvelle <fullName> et ensuite traiter il comme ceci <xsl:apply-templates select="$tempTree"> . Pour faire cela dans XSLT 1.0, vous devez envelopper la référence de la variable avec une fonction d'extension (par exemple, exsl:node-set() ), mais dans XSLT 2.0, il fonctionnera tel quel.

Que vous traitiez les nœuds de l'arbre source d'origine ou d'un arbre temporaire que vous construisez, dans tous les cas, vous devez indiquer explicitement les nœuds que vous souhaitez traiter.

Ce que nous n'avons pas couvert, c'est la façon dont le XSLT obtient tous ses comportements implicites. Vous devez également comprendre le règles de modèles intégrés . J'écris constamment des feuilles de style qui n'incluent même pas une règle explicite pour le nœud racine ( match="/" ). Au lieu de cela, je m'appuie sur la règle intégrée pour les nœuds Root (appliquer les modèles aux enfants), qui est la même que la règle intégrée pour les nœuds d'éléments. Ainsi, je peux ignorer une grande partie de l'entrée, laisser le processeur XSLT la parcourir automatiquement, et ne faire quelque chose de spécial que lorsqu'il rencontre un noeud qui m'intéresse. Je peux aussi écrire une seule règle qui copie tout de manière récursive (appelée la transformation d'identité), en ne la remplaçant qu'en cas de besoin, pour apporter des modifications progressives à l'entrée. Après avoir lu "How XSLT Works", votre prochaine tâche consistera à rechercher la "identity transform".

Je réalise que je suis profondément ignorant sur le "zen" de XSLT. Jusqu'à présent, mes feuilles de style ont consisté en un modèle correspondant au nœud Root, puis sont complètement procédurales à partir de là. J'en ai assez de faire ça. Je préférerais plutôt comprendre correctement XSLT correctement, d'où ma question.

Je vous applaudis. Maintenant il est temps de prendre la "pilule rouge" : lire "Comment fonctionne XSLT"

6voto

mirod Points 10845

Modèles toujours dans le XML source. L'ordre n'a donc pas vraiment d'importance, sauf si deux modèles ou plus correspondent au(x) même(s) nœud(s). Dans ce cas, de manière quelque peu contre-intuitive, la règle avec la balise dernier le modèle correspondant est déclenché.

2voto

Chris R Points 523

Dans votre premier exemple, le modèle n°1 s'exécute parce que lorsque vous commencez à traiter le xml d'entrée, il commence à la racine et c'est le seul modèle de votre feuille de style qui correspond à l'élément racine. Même s'il était le deuxième dans la feuille de style, il serait toujours exécuté en premier.

Dans cet exemple, le modèle 2 ne sera pas exécuté car vous avez déjà traité l'élément Root à l'aide du modèle 1 et il n'y a plus d'éléments à traiter après le Root. Si vous souhaitez traiter d'autres éléments à l'aide de modèles supplémentaires, vous devez le modifier comme suit.

<xsl:template match="/">
  <xsl:apply-templates/>
</xsl:template>

Cela vous permet ensuite de définir un modèle pour chaque élément qui vous intéresse et de traiter le xml de manière plus logique, plutôt que de le faire de manière procédurale.

Notez également que cet exemple ne produira rien car dans le contexte actuel (la racine), il n'y a pas d'élément firstName, seulement un élément person, donc il devrait être :

<xsl:template match="/">
  <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>

Je trouve plus facile de penser que vous vous déplacez dans le XML, en commençant par la racine et en recherchant le modèle qui correspond à cet élément, puis en suivant ces instructions pour générer la sortie. Le XSLT transforme le document d'entrée en document de sortie, de sorte que le document de sortie est vide au début de la transformation. La sortie n'est pas utilisée comme une partie de la transformation, c'est juste la sortie de celle-ci.

Dans votre deuxième exemple, le modèle n°2 ne s'exécutera pas car le modèle est exécuté sur le xml d'entrée et non sur la sortie.

0voto

user2151421 Points 1

La réponse d'Evan est fondamentalement bonne.

Cependant, une chose qui semble faire défaut est la possibilité d'"appeler" des morceaux de code sans faire de correspondance. Cela permettrait - du moins de l'avis de certaines personnes - une bien meilleure structuration.

J'ai fait un petit exemple pour essayer de montrer ce que je veux dire.

<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
    <!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>

    <body>
<!-- Comments are better than nothing -->
    <!-- but that part should really have been somewhere else ... -->

<!-- Now do what we really want here ... this really is making the table! -->

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->

<!-- This approach works, but leads to horribly monolithic code -->
    <!-- Further - it leads to templates including code which is strictly -->
    <!-- not relevant to them. I've not found a way round this yet -->
</xsl:template>

Cependant, après avoir bricolé un peu, et d'abord en utilisant l'indication que s'il y a deux modèles correspondants, le dernier dans le code sera sélectionné, et ensuite en restructurant mon code (pas tout montré ici), j'ai obtenu ceci qui semble fonctionner, et avec un peu de chance, génère le code correct, tout en affichant les données voulues

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->

<xsl:template name="dohtml">
  <html>
      <xsl:call-template name="dohead" />
      <xsl:call-template name="dobody" />
  </html>
</xsl:template>

<xsl:template name="dohead">
<head>
    <title>Salary details</title>
</head>
</xsl:template>

<xsl:template name="dobody">
<body>
    <xsl:call-template name="dotable" />
</body>
</xsl:template>

<xsl:template match="/entries" name="dotable">

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

</xsl:template>

<xsl:template  match="/" name="main">
            <xsl:call-template name="dohtml" />
</xsl:template> 

[Faites défiler le code ci-dessus de haut en bas si vous ne pouvez pas tout voir].

Le principe de fonctionnement est le suivant : le modèle principal correspond toujours - correspond à /

Il contient les morceaux de code - les modèles - qui sont appelés.

Cela signifie maintenant qu'il n'est pas possible de faire correspondre un autre modèle sur / mais qu'il est possible de faire correspondre explicitement sur un noeud nommé, qui dans ce cas est le noeud de plus haut niveau dans le xml - appelé entrées.

Une petite modification du code a permis de produire l'exemple donné ci-dessus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X