2010-06-07 6 views
4

J'essaie d'utiliser XSLT pour transformer un document en marquant un groupe de nœuds XML avec des ID entiers, en commençant à 0 et en augmentant d'un pour chaque nœud du groupe. Le XML transmis dans la feuille de style doit être renvoyé, mais augmenté pour inclure cette information supplémentaire.Comment utiliser XSLT pour marquer des nœuds spécifiques avec des ID entiers uniques, séquentiels et croissants?

Juste pour être clair sur ce que je parle, voici comment cette transformation serait exprimée à l'aide DOM:

states = document.getElementsByTagName("state"); 
for(i = 0; i < states.length; i++){ 
    states.stateNum = i; 
} 

Ceci est très simple avec DOM, mais je vais avoir beaucoup plus de mal faire cela avec XSLT. La stratégie actuelle que j'ai conçue a été de commencer par la transformation d'identité, puis de créer une variable globale qui sélectionne et stocke tous les nœuds que je souhaite numéroter. Je crée ensuite un modèle qui correspond à ce type de noeud. L'idée, alors, est que dans le modèle, je chercherais la position du nœud correspondant dans la variable globale nodelist, ce qui me donnerait un nombre unique que je pourrais ensuite définir comme un attribut.

Le problème avec cette approche est que la fonction de position ne peut être utilisé avec le noeud contextuel, donc quelque chose comme ce qui suit est illégale:

<template match="state"> 
    <variable name="stateId" select="@id"/> 
    <variable name="uniqueStateNum" select="$globalVariable[@id = $stateId]/position()"/> 
</template> 

en est de même pour les éléments suivants:

<template match="state"> 
    <variable name="stateId" select="@id" 
    <variable name="stateNum" select="position($globalVariable[@id = $stateId])/"/> 
</template> 

Pour utiliser position() pour rechercher la position d'un élément dans $ globalVariable, le noeud contextuel doit être modifié.

J'ai trouvé une solution, mais elle est très sous-optimale. Fondamentalement, dans le modèle, j'utilise chacun pour parcourir la variable globale. Car chacun change le nœud de contexte, ce qui me permet d'utiliser position() comme je l'ai décrit. Le problème est que cela transforme ce qui serait normalement une opération O (n) en une opération O (n^2), où n est la longueur de la nodelist, car cela nécessite de parcourir toute la liste à chaque fois que le modèle est trouvé. Je pense qu'il doit y avoir une solution plus élégante.

Au total, voici mon courant (un peu simplifiée) feuille de style XSLT:

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:s="http://www.w3.org/2005/07/scxml" 
    xmlns="http://www.w3.org/2005/07/scxml" 
    xmlns:c="http://msdl.cs.mcgill.ca/" 
    version="1.0"> 
    <xsl:output method="xml"/> 

    <!-- we copy them, so that we can use their positions as identifiers --> 
    <xsl:variable name="states" select="//s:state" /> 


    <!-- identity transform --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="s:state"> 

     <xsl:variable name="stateId"> 
      <xsl:value-of select="@id"/> 
     </xsl:variable> 

     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 

      <xsl:for-each select="$states"> 
       <xsl:if test="@id = $stateId"> 
        <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
         <xsl:value-of select="position()"/> 
        </xsl:attribute> 
       </xsl:if> 
      </xsl:for-each> 

      <xsl:apply-templates select="node()"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

J'apprécierais tout conseil qu'on peut offrir. Merci.

+0

Bonne question (+1). Voir ma réponse pour une solution très simple mais correcte.:) –

Répondre

3

Cette transformation:

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

<xsl:template match="node()|@*" name="identity"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="s:state"> 
    <xsl:variable name="vNum"> 
    <xsl:number level="any" count="s:state"/> 
    </xsl:variable> 

    <xsl:copy> 
    <xsl:copy-of select="@*"/> 

    <xsl:attribute name="stateId"> 
    <xsl:value-of select="@id"/> 
    </xsl:attribute> 

    <xsl:attribute name="id"> 
    <xsl:value-of select="$vNum -1"/> 
    </xsl:attribute> 

    <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 
</xsl:stylesheet> 

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

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state id="Compound1"> 
     <state id="Basic1"/> 
     <state id="Basic2"/> 
     <state id="Basic3"/> 
    </state> 
</scxml> 

produit la sortie désirée, correcte:

<scxml xmlns="http://www.w3.org/2005/07/scxml"> 
    <state stateId="Compound1" id="0"> 
     <state stateId="Basic1" id="1"/> 
     <state stateId="Basic2" id="2"/> 
     <state stateId="Basic3" id="3"/> 
    </state> 
</scxml> 
+0

Fonctionne parfaitement, merci! – jbeard4

0

approche la plus simple:

<xsl:template match="s:state"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:attribute name="stateNum" namespace="http://msdl.cs.mcgill.ca/"> 
     <xsl:value-of select="count(preceding::s:state)" /> 
    </xsl:attribute> 
    <xsl:apply-templates select="node()"/> 
    </xsl:copy> 
</xsl:template> 

Je ne sais pas comment votre processeur XSLT gère l'axe preceding, c'est donc quelque chose à référence dans tous les cas.

+0

Tomalak, basé sur ma compréhension de l'axe précédent, cela semblait être une approche raisonnable. Malheureusement, cela ne semble pas fonctionner. S'il vous plaît voir le document XML suivant: \t \t \t \t \t \t \t \t Cela va créer les numéros d'état suivants: stateid: le composé 1, stateNum: 0 ; stateId: Basic1, stateNum: 0; stateId: Basic2, étatNum: 1; stateId: Basic3, stateNum: 2 Vérifié avec Xalan, 4xslt et xsltproc. Des idées? – jbeard4

+0

Hm. Je vois ce que tu veux dire. Essayez d'ajouter 'count (ancestor :: s: state)' et 'count (previous :: s: state)', mais cela pourrait aussi être faux. Pas de chane à tester en ce moment, je suis sur mon téléphone portable actuellement. ;-) – Tomalak

Questions connexes