2010-06-18 3 views
0

Voici mon exemple artificiel qui illustre ce que je tente d'accomplir. J'ai un fichier XML d'entrée que je souhaite aplatir pour un traitement ultérieur.Réutilisation du modèle XSL à appeler avec différents XPath relatifs

fichier d'entrée:

<BICYCLES> 
    <BICYCLE> 
     <COLOR>BLUE</COLOR> 
     <WHEELS> 
      <WHEEL> 
       <WHEEL_TYPE>FRONT</WHEEL_TYPE> 
       <FLAT>NO</FLAT> 
       <REFLECTORS> 
        <REFLECTOR> 
         <REFLECTOR_NUM>1</REFLECTOR_NUM> 
         <SHAPE>SQUARE</SHAPE> 
        </REFLECTOR> 
        <REFLECTOR> 
         <REFLECTOR_NUM>2</REFLECTOR_NUM> 
         <SHAPE>ROUND</SHAPE> 
        </REFLECTOR> 
       </REFLECTORS> 
      </WHEEL> 
      <WHEEL> 
       <WHEEL_TYPE>REAR</WHEEL_TYPE> 
       <FLAT>NO</FLAT> 
      </WHEEL> 
     </WHEELS> 
    </BICYCLE> 
</BICYCLES> 

L'entrée est une liste de noeuds <BICYCLE>. Chaque <BICYCLE> a une <COLOR> et a en option <WHEELS>.

<WHEELS> est une liste de noeuds <WHEEL>, chacun d'entre eux ayant quelques attributs et ayant éventuellement la valeur <REFLECTORS>.

<REFLECTORS> est une liste de nœuds <REFLECTOR>, chacun ayant quelques attributs.

L'objectif est d'aplatir ce XML. C'est le XSL J'utilise:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> 
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

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

<xsl:template match="BICYCLE"> 
<xsl:choose> 
    <xsl:when test="WHEELS"> 
     <xsl:apply-templates select="WHEELS"/> 
    </xsl:when> 
    <xsl:otherwise> 
     <BICYCLE> 
      <COLOR><xsl:value-of select="COLOR"/></COLOR> 
      <WHEEL_TYPE/> 
      <FLAT/> 
      <REFLECTOR_NUM/> 
      <SHAPE/> 
     </BICYCLE> 
    </xsl:otherwise> 
</xsl:choose> 
</xsl:template> 

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

<xsl:template match="WHEEL"> 
    <xsl:choose> 
     <xsl:when test="REFLECTORS"> 
      <xsl:apply-templates select="REFLECTORS"/> 
     </xsl:when> 
     <xsl:otherwise> 
      <BICYCLE> 
       <COLOR><xsl:value-of select="../../COLOR"/></COLOR> 
       <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE> 
       <FLAT><xsl:value-of select="FLAT"/></FLAT> 
       <REFLECTOR_NUM/> 
       <SHAPE/> 
      </BICYCLE> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

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

<xsl:template match="REFLECTOR"> 
    <BICYCLE> 
     <COLOR><xsl:value-of select="../../../../COLOR"/></COLOR> 
     <WHEEL_TYPE><xsl:value-of select="../../WHEEL_TYPE"/></WHEEL_TYPE> 
     <FLAT><xsl:value-of select="../../FLAT"/></FLAT> 
     <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM> 
     <SHAPE><xsl:value-of select="SHAPE"/></SHAPE> 
    </BICYCLE> 
</xsl:template> 

</xsl:stylesheet> 

La sortie est:

<BICYCLES xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema"> 
    <BICYCLE> 
     <COLOR>BLUE</COLOR> 
     <WHEEL_TYPE>FRONT</WHEEL_TYPE> 
     <FLAT>NO</FLAT> 
     <REFLECTOR_NUM>1</REFLECTOR_NUM> 
     <SHAPE>SQUARE</SHAPE> 
    </BICYCLE> 
    <BICYCLE> 
     <COLOR>BLUE</COLOR> 
     <WHEEL_TYPE>FRONT</WHEEL_TYPE> 
     <FLAT>NO</FLAT> 
     <REFLECTOR_NUM>2</REFLECTOR_NUM> 
     <SHAPE>ROUND</SHAPE> 
    </BICYCLE> 
    <BICYCLE> 
     <COLOR>BLUE</COLOR> 
     <WHEEL_TYPE>REAR</WHEEL_TYPE> 
     <FLAT>NO</FLAT> 
     <REFLECTOR_NUM/> 
     <SHAPE/> 
    </BICYCLE> 
</BICYCLES> 

Ce que je n'aime pas à ce sujet est que je suis sortie l'attribut de couleur sous plusieurs formes:

Il semble qu'il devrait y avoir un moyen de créer un modèle nommé et de l'invoquer à partir des divers endroits où il est nécessaire, et de passer un paramètre qui représente le chemin vers le nœud <BICYCLE> ch il se réfère.

Y a-t-il un moyen de nettoyer cela, disons avec un gabarit nommé pour les champs de bicyclette, pour les champs de roues et pour les champs de réflecteurs? Dans l'exemple réel sur lequel il est basé, il y a beaucoup plus d'attributs à un "vélo" que juste la couleur, et je veux que ce XSL soit facile à changer pour inclure ou exclure des champs sans avoir à changer le XSL dans plusieurs endroits.

Répondre

2

Vous pouvez nommer des modèles en utilisant l'attribut name. Vous invoquez un modèle par son nom en utilisant <xsl:call-template>, et il est valide (IIRC) partout où <xsl:apply-templates> est valide. MISE À JOUR (à partir des commentaires): On dirait que vous voulez un autre axis, probablement ancestor. Quelque chose comme ancestor::bicycle/color?

+0

Mais le chemin était-il dans le modèle? Si je mets , il ne trouvera pas la couleur si le template est invoqué depuis le template REFECTOR. – user189169

+1

@meomaxy Semble que vous voulez un [axe] différent (http://www.w3schools.com/xpath/xpath_axes.asp), probablement 'ancestor'. Quelque chose comme 'ancestor :: bicycle/color'? –

+0

OUI !!! ancestor-or-self :: BICYCLETTE/COULEUR MERCI! – user189169

0

Tu veux dire comme:

<xsl:template name="wheelblock"> 
    <xsl:param name="color"></xsl:param> 
    <!-- Do something here --> 
</xsl:template> 

<xsl:template match="WHEEL"> 
    <xsl:call-template name="wheelblock"> 
    <xsl:with-param name="color">whatever element/etc</xsl:with-param> 
    </xsl:call-template> 
</xsl:template> 
+0

Non, je ne veux pas faire de la couleur le paramètre, je veux que le chemin vers le nœud BICYCLE soit le paramètre pour que je puisse écrire un modèle qui puisse trouver la couleur du vélo indépendamment du fait qu'il soit invoqué par le BICYCLE, ROUE ou le modèle REFLECTEUR. Si le modèle peut remonter lui-même pour trouver le bon nœud, tant mieux, mais au moins l'appelant connaît le bon chemin. – user189169

2

Ce que je n'aime pas à ce sujet est que je sortie l'attribut de couleur dans plusieurs formes:

Il semble comme il devrait y avoir un moyen de faire un modèle nommé et l'appeler des différents endroits où il est nécessaire et passer un paramètre qui représente le chemin de retour au nœud auquel il se réfère.

est-il un moyen de nettoyer tout ça, dire avec un modèle nommé pour les champs de vélo , pour les champs de roue et champs de réflecteur?

Vous pouvez obtenir encore plus de réutilisation que cela.

Le principe est d'éviter la logique conditionnelle et de laisser le processeur XSLT choisir le modèle à sélectionner pour le traitement. Toute valeur nécessaire doit être transmise en tant que paramètre aux modèles appliqués.

La transformation suivante démontre ces principes:

<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="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="BICYCLE"> 
    <xsl:apply-templates> 
    <xsl:with-param name="pColor" select="COLOR"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="BICYCLE[not(WHEELS)]"> 
    <BICYCLE> 
     <COLOR><xsl:value-of select="COLOR"/></COLOR> 
     <WHEEL_TYPE/> 
     <FLAT/> 
     <REFLECTOR_NUM/> 
     <COLOR/> 
     <SHAPE/> 
    </BICYCLE> 
</xsl:template> 

<xsl:template match="WHEELS"> 
    <xsl:param name="pColor"/> 

    <xsl:apply-templates> 
    <xsl:with-param name="pColor" select="$pColor"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="WHEEL[REFLECTORS]"> 
    <xsl:param name="pColor"/> 

    <xsl:apply-templates select="REFLECTORS"> 
    <xsl:with-param name="pColor" select="$pColor"/> 
    <xsl:with-param name="pWheel_Type" select="WHEEL_TYPE"/> 
    <xsl:with-param name="pFlat" select="FLAT"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="WHEEL"> 
    <xsl:param name="pColor"/> 

    <BICYCLE> 
     <COLOR><xsl:value-of select="$pColor"/></COLOR> 
     <WHEEL_TYPE><xsl:value-of select="WHEEL_TYPE"/></WHEEL_TYPE> 
     <FLAT><xsl:value-of select="FLAT"/></FLAT> 
     <REFLECTOR_NUM/> 
     <COLOR/> 
     <SHAPE/> 
    </BICYCLE> 
</xsl:template> 

<xsl:template match="REFLECTORS"> 
    <xsl:param name="pColor"/> 
    <xsl:param name="pWheel_Type"/> 
    <xsl:param name="pFlat"/> 

    <xsl:apply-templates select="REFLECTOR"> 
    <xsl:with-param name="pColor" select="$pColor"/> 
    <xsl:with-param name="pWheel_Type" select="$pWheel_Type"/> 
    <xsl:with-param name="pFlat" select="$pFlat"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="REFLECTOR"> 
    <xsl:param name="pColor"/> 
    <xsl:param name="pWheel_Type"/> 
    <xsl:param name="pFlat"/> 

    <BICYCLE> 
     <COLOR><xsl:value-of select="$pColor"/></COLOR> 
     <WHEEL_TYPE><xsl:value-of select="$pWheel_Type"/></WHEEL_TYPE> 
     <FLAT><xsl:value-of select="$pFlat"/></FLAT> 
     <REFLECTOR_NUM><xsl:value-of select="REFLECTOR_NUM"/></REFLECTOR_NUM> 
     <COLOR><xsl:value-of select="COLOR"/></COLOR> 
     <SHAPE><xsl:value-of select="SHAPE"/></SHAPE> 
    </BICYCLE> 
</xsl:template> 

<xsl:template match="BICYCLE/COLOR"/> 
</xsl:stylesheet> 

Lorsqu'il est appliqué sur le document XML fourni, le résultat recherché est produit:

<BICYCLES> 
<BICYCLE> 
    <COLOR>BLUE</COLOR> 
    <WHEEL_TYPE>FRONT</WHEEL_TYPE> 
    <FLAT>NO</FLAT> 
    <REFLECTOR_NUM>1</REFLECTOR_NUM> 
    <COLOR>RED</COLOR> 
    <SHAPE>SQUARE</SHAPE> 
</BICYCLE> 
<BICYCLE> 
    <COLOR>BLUE</COLOR> 
    <WHEEL_TYPE>FRONT</WHEEL_TYPE> 
    <FLAT>NO</FLAT> 
    <REFLECTOR_NUM>2</REFLECTOR_NUM> 
    <COLOR>WHITE</COLOR> 
    <SHAPE>ROUND</SHAPE> 
</BICYCLE> 
<BICYCLE> 
    <COLOR>BLUE</COLOR> 
    <WHEEL_TYPE>REAR</WHEEL_TYPE> 
    <FLAT>NO</FLAT> 
    <REFLECTOR_NUM/> 
    <COLOR/> 
    <SHAPE/> 
</BICYCLE> 
</BICYCLES> 
0

Merci Hank Gay! C'est ce que je cherchais. Le XSL suivant a la même sortie que mon original:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> 
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" xml:space="preserve"/> 

<xsl:template name="allfields"> 
    <BICYCLE> 
     <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR> 
     <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE> 
     <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT> 
     <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM> 
     <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE> 
    </BICYCLE> 
</xsl:template> 

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

<xsl:template match="BICYCLE"> 
<xsl:choose> 
    <xsl:when test="WHEELS"> 
     <xsl:apply-templates/> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:call-template name="allfields"/> 
    </xsl:otherwise> 
</xsl:choose> 
</xsl:template> 

<xsl:template match="WHEEL"> 
      <xsl:apply-templates/> 
    <xsl:choose> 
     <xsl:when test="REFLECTORS"> 
      <xsl:apply-templates/> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:call-template name="allfields"/> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<xsl:template match="REFLECTOR"> 
    <xsl:call-template name="allfields"/> 
</xsl:template> 

</xsl:stylesheet> 
+0

Ceci est plus inefficace que de passer les paramètres nécessaires aux modèles. L'axe 'ancestor:' devra être scanné plusieurs fois. Ma réponse n'a pas de telles inefficacités. Une autre observation est que vous pouvez simplifier le code en supprimant le traitement conditionnel ('' et '') et laisser ce travail au processeur XSLT. Pour résumer, ce code a beaucoup d'espace pour l'amélioration. –

0

Deux façons.

Préserver votre utilisation de ancestor-or-self axe:

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

<xsl:template match="/"> 
    <BICYCLES> 
     <xsl:for-each select="BICYCLE[not(WHELLS)]|//REFLECTOR|//WHEEL[not(REFLECTORS)]"> 
      <BICYCLE> 
       <COLOR><xsl:value-of select="ancestor-or-self::BICYCLE/COLOR"/></COLOR> 
       <WHEEL_TYPE><xsl:value-of select="ancestor-or-self::WHEEL/WHEEL_TYPE"/></WHEEL_TYPE> 
       <FLAT><xsl:value-of select="ancestor-or-self::WHEEL/FLAT"/></FLAT> 
       <REFLECTOR_NUM><xsl:value-of select="ancestor-or-self::REFLECTOR/REFLECTOR_NUM"/></REFLECTOR_NUM> 
       <COLOR><xsl:value-of select="ancestor-or-self::REFLECTOR/COLOR"/></COLOR> 
       <SHAPE><xsl:value-of select="ancestor-or-self::REFLECTOR/SHAPE"/></SHAPE> 
      </BICYCLE> 
     </xsl:for-each> 
    </BICYCLES> 
</xsl:template> 

</xsl:stylesheet> 

Ceci est juste un exemple d'utilisation abusive du modèle de traitement des règles. Par contre, avec Dimitre post, vous pouvez éviter la logique conditionnelle et appliquer le gabarit à la branche souhaitée.

Je pense que les axes descendant et ancêtre ne sont pas efficaces pour une entrée XML longue, dans ce cas. Donc, si pour votre plus traitement des noeuds vides et non noeuds existants sont les mêmes (comme avec le résultat de la base de données), ce schéma général devrait fonctionner:

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

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

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

    <!-- 
     When you don't want a leaf in your result, just: 
     <xsl:template match="here the leaf name" /> 
    --> 

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

    <xsl:template match="*[*/*]"> 
     <xsl:param name="prev" /> 
     <xsl:apply-templates select="*[*]"> 
      <xsl:with-param name="prev"> 
       <xsl:copy-of select="$prev"/> 
       <xsl:apply-templates select="*[not(*)]"/> 
      </xsl:with-param> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="*[* and not(*/*)]"> 
     <xsl:param name="prev" /> 
     <BICYCLE> 
      <xsl:copy-of select="$prev"/> 
      <xsl:apply-templates select="*"/> 
     </BICYCLE> 
    </xsl:template> 

</xsl:stylesheet> 

Remarque: vous ne devez pas savoir Wich sont la feuille.

Modifier: Un pire exemple d'utilisation descendant et axes ancêtres, et quelques modifications à ma proposition

Edit2: Ajout d'une nouvelle feuille posible (I Missed que la première fois!) Pour le pire mise en œuvre. Ajout de la possibilité d'exclure certaines feuilles pour une deuxième implémentation.

Éditer3: Juste une réécriture pour clarifier mon point.

+0

Merci! En ce qui concerne la 1ère implémentation, est-ce que match = "REFLECTOR | WHEEL [pas (REFLECTORS)]"> en supposant qu'il y ait toujours un WHEELS? Je ne veux pas qu'il saute des bicyclettes qui n'ont pas de roues. En ce qui concerne le 2ème, dans mon cas je ne veux pas sortir TOUTES les feuilles, je veux juste sortir les feuilles que je spécifie, mais tout cela est très utile. Merci. – user189169

+0

@meomaxy: La première implémentation est la pire! Si vous devez le réutiliser, vous devez déterminer quelles sont les feuilles (dans ce cas, chaque RÉFLECTEUR définit un type de bicyclette, et aussi n'importe quelle ROUE même si elle n'a pas de RÉFLECTEURS). En ce qui concerne la deuxième implémentation, je vais l'éditer afin d'attraper facilement votre requeriment. –

Questions connexes