2012-07-02 3 views
2

Mon XML d'entrée se compose des éléments suivants,Afficher le numéro de série en utilisant XSLT

<root> 
    <entry> 
     <type>U</type> 
     <value>111</value> 
    </entry> 
    <entry> 
     <type>X</type> 
     <value>222</value> 
    </entry> 
    <entry> 
     <type>E</type> 
     <value>333</value> 
    </entry> 
    <entry> 
     <type>Q</type> 
     <value>444</value> 
    </entry> 
    <entry> 
     <value>555</value> 
    </entry> 
    <entry> 
     <value>666</value> 
    </entry> 
</root> 

sortie i requis,

<ROOT> 
    <ENTRY> 
     <SLNO>1</SLNO> 
     <VALUE>111</VALUE> 
    </ENTRY> 
    <ENTRY> 
     <SLNO>1</SLNO> 
     <VALUE>222</VALUE> 
    </ENTRY> 
    <ENTRY> 
     <SLNO>1</SLNO> 
     <VALUE>333</VALUE> 
    </ENTRY> 
    <ENTRY> 
     <SLNO>2</SLNO> 
     <VALUE>444</VALUE> 
    </ENTRY> 
    <ENTRY> 
     <SLNO>3</SLNO> 
     <VALUE>555<VALUE> 
    </ENTRY> 
    <ENTRY> 
     <SLNO>4</SLNO> 
     <VALUE>666</VALUE> 
    </ENTRY> 
</ROOT> 

L'exigence est de générer de nouveaux SLNO pour les types autres que X, E, Y et K et aussi si la balise de type elle-même est manquante. Pour tous les autres types, nous devons afficher un nouveau numéro de série.

J'ai écrit un pour-chacun pour le même puisque je dois faire un peu plus de traitement avec d'autres valeurs, donc for-each est un must.

Comment puis-je y parvenir?

Mon exemple de code XSL est asl suit,

<xsl:for-each select="/ROOT/ENTRY"> 
    <xsl:if test="(TYPE != 'X') and (TYPE != 'E')">    
     <xsl:text><![CDATA[<SLNO>]]></xsl:text> 


     <xsl:if test="((type != 'X') and (type != 'E') and (type != 'Y') and (type != 'K'))"> 
      <xsl:value-of select="count(preceding::type[((. != 'X' and . != 'E' and . != 'Y' and . !='K') or . ='')]) + 1" /> 
     </xsl:if>                

     <xsl:if test="(type = 'X') or (type = 'E') or (type = 'Y') or (type = 'K')"> 
      <xsl:value-of select="count(preceding::type[(. != 'X' and . != 'E' and . != 'Y' and . !='K')])" />               
     </xsl:if> 



     <xsl:text><![CDATA[</SLNO>]]></xsl:text> 
    </xsl:if> 
    <!-- Printing remaining values --> 
</xsl:for-each> 

problème avec ce code est, je ne reçois pas SLNO pour les entrées qui ne sont pas de type balise.

Aidez-nous s'il vous plaît.

+2

Votre question est tout à fait correcte. Vous mentionnez "X, Y, K et L" dans la question, mais il y a une référence à "E" dans le XSLT, mais pas "L" –

+0

@Tim, vous avez raison, je l'ai mis à jour. Pardon. – Wilz

+1

@Wilz: Dans votre code, vous générez * text * qui ressemble à du balisage - cela ne produit pas du tout le document XML que vous voulez obtenir comme résultat. Apprenez * jamais * à produire du texte semblable à un balisage. –

Répondre

3

Avertissement :

Le code fourni dans ce question n'est pas syntaxiquement légal XSLT - il n'y a pas d'instruction XSLT <xsl:type-of> dans la langue.

En outre, ce code génère le texte qui ressemble à des balises d'élément d'ouverture et de fermeture.

Ne jamais faire une telle chose, quand vous voulez générer un élément - test n'est pas un balisage - c'est juste ... texte .; et la sortie générée n'est pas ce que vous vouliez qu'elle soit.


Solution:

Cette transformation:

<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="entry"> 
    <entry> 
    <slno> 
    <xsl:value-of select= 
    "count((. 
      | 
       preceding-sibling::entry) 
       [not(type) 
       or 
       not(contains('+X+E+Y+K+', concat('+', type, '+'))) 
       ] 
      )"/> 
    </slno> 
    <xsl:apply-templates select="node()[not(self::type)]"/> 
    </entry> 
</xsl:template> 
</xsl:stylesheet> 

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

<root> 
    <entry> 
     <type>U</type> 
     <value>111</value> 
    </entry> 
    <entry> 
     <type>X</type> 
     <value>222</value> 
    </entry> 
    <entry> 
     <type>E</type> 
     <value>333</value> 
    </entry> 
    <entry> 
     <type>Q</type> 
     <value>444</value> 
    </entry> 
    <entry> 
     <value>555</value> 
    </entry> 
    <entry> 
     <value>666</value> 
    </entry> 
</root> 

produit le résultat souhaité, correct:

<root> 
    <entry> 
     <slno>1</slno> 
     <value>111</value> 
    </entry> 
    <entry> 
     <slno>1</slno> 
     <value>222</value> 
    </entry> 
    <entry> 
     <slno>1</slno> 
     <value>333</value> 
    </entry> 
    <entry> 
     <slno>2</slno> 
     <value>444</value> 
    </entry> 
    <entry> 
     <slno>3</slno> 
     <value>555</value> 
    </entry> 
    <entry> 
     <slno>4</slno> 
     <value>666</value> 
    </entry> 
</root> 
+0

Merci beaucoup pour la solution Dimitre, et son fonctionnement optimal. Pouvez-vous me fournir quelques références pour apprendre XSL, puisque je suis nouveau à XSLT. – Wilz

+0

@Wilz: Vous êtes les bienvenus. Cette réponse contient des pointeurs vers de bonnes ressources XSLT: http://stackoverflow.com/a/341589/36305 –

0

dans votre chèque

(TYPE != 'X') and (TYPE != 'E') 

vous devez inclure une déclaration 'OU' pour les nœuds d'entrée qui n'ont pas le nœud TYPE »

quelque chose comme

((TYPE != 'X') and (TYPE != 'E')) or count(TYPE) = 0 
+0

Essayé ce qui suit, mais ne fonctionne pas, Wilz

1
  • Ne pas jamais utilisation aux éléments XML de sortie. Déjà.C'est juste mauvais et faux (et laid à cela, mais laide n'est même pas un argument ici). Ne pas utiliser <xsl:for-each>. Utiliser <xsl:template>/<xsl:apply-templates>
  • Vous pouvez utiliser translate() à votre avantage, voir ci-dessous. AVERTISSEMENT Ce raccourci ne fonctionnera que si vous avez des types à lettre unique. Pensez si vous vraiment besoin de mettre en majuscules ces noms d'éléments. Cela ne me semble pas un changement nécessaire. Garder le boîtier d'origine est beaucoup plus facile, car vous pouvez utiliser le identity template. Voir également Dimitre's answer.

Cette feuille de style:

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

    <xsl:template match="root"> 
    <ROOT> 
     <xsl:apply-templates select="entry" /> 
    </ROOT> 
    </xsl:template> 

    <xsl:template match="entry"> 
    <ENTRY> 
     <SLNO> 
     <xsl:value-of select=" 
      count(
      (. | preceding-sibling::entry)[not(type and translate(type, 'EKXY', '') = '')] 
     ) 
     " /> 
     </SLNO> 
     <xsl:apply-templates select="value" /> 
    </ENTRY> 
    </xsl:template> 

    <xsl:template match="value"> 
    <VALUE> 
     <xsl:value-of select="." /> 
    </VALUE> 
    </xsl:template> 
</xsl:stylesheet> 

produit:

<ROOT> 
    <ENTRY> 
    <SLNO>1</SLNO> 
    <VALUE>111</VALUE> 
    </ENTRY> 
    <ENTRY> 
    <SLNO>1</SLNO> 
    <VALUE>222</VALUE> 
    </ENTRY> 
    <ENTRY> 
    <SLNO>1</SLNO> 
    <VALUE>333</VALUE> 
    </ENTRY> 
    <ENTRY> 
    <SLNO>2</SLNO> 
    <VALUE>444</VALUE> 
    </ENTRY> 
    <ENTRY> 
    <SLNO>3</SLNO> 
    <VALUE>555</VALUE> 
    </ENTRY> 
    <ENTRY> 
    <SLNO>4</SLNO> 
    <VALUE>666</VALUE> 
    </ENTRY> 
</ROOT> 
+0

Tomalak: Bonne réponse, mais elle ne produit pas le résultat requis. En outre, utiliser 'translate()' de cette façon compterait un ' XXXY', ce qui n'est pas voulu. –

+0

@Dimitre La question indique les types à une seule lettre, donc j'ai pris cela comme référence. L'OP * a * laissé * place à la conjecture, donc c'est une solution possible. Mais vous avez raison, les types multi-lettres * feraient échouer mon code. J'ai ajouté une note avec cet avertissement. L'erreur de comptage dans ma logique est fixe. – Tomalak

+0

Tomalak: Maintenant que vous avez corrigé la solution initiale et ajouté un avertissement +1 de ma part. Il est toujours vrai que la solution plus générique s'applique non seulement aux types de documents XML avec lesquels la solution limitée fonctionne. –

Questions connexes