Vérifiez si une chaîne est nulle ou vide dans XSLT

325

Comment puis-je vérifier si une valeur est nulle ou vide avec XSL ?

Par exemple, si categoryNameest vide? J'utilise un lors du choix de la construction.

Par exemple:

<xsl:choose>
    <xsl:when test="categoryName !=null">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>
raklos
la source
Pouvez-vous développer l'exemple de code?
Nick Allen,
Selon votre cas d'utilisation, vous ne voudrez probablement pas utiliser xsl:whenpour les tests de nœuds. Considérez <xsl:template match="Category[categoryName[not(node())]]">...avec un <xsl:template match="Category">.... Le processeur prendra alors les bonnes décisions pour vous et vous n'aurez plus besoin d'écrire la logique métier imbriquée xsl:choose. Dans de nombreux cas, l'utilisation de modèles correspondants facilite la rédaction de feuilles de style.
Abel

Réponses:

322
test="categoryName != ''"

Edit : Cela couvre l'interprétation la plus probable, à mon avis, de "[pas] nul ou vide" comme déduit de la question, y compris son pseudo-code et ma propre première expérience avec XSLT. C'est-à-dire, "Quel est l'équivalent du Java suivant?":

!(categoryName == null || categoryName.equals(""))

Pour plus de détails, par exemple, identifier distinctement null vs vide, voir la réponse de johnvey ci - dessous et / ou le `` violon '' XSLT que j'ai adapté de cette réponse, qui inclut l'option dans le commentaire de Michael Kay ainsi que la sixième interprétation possible.

steamer25
la source
14
La sémantique détaillée de ce test est la suivante: renvoyer true s'il existe au moins un élément categoryName dont la valeur de chaîne est une chaîne vide.
jelovirt
14
@jelovirt vouliez-vous dire s'il y a au moins un nom de catégorie qui n'est PAS une chaîne vide? (Je suis un débutant xsl, alors pardonnez toute stupidité potentielle à ma question.)
joedevon
10
Cette réponse, bien qu'acceptée et fortement votée, est également très trompeuse. Cela dépend vraiment de ce que vous entendez par «nul ou vide». Si vous souhaitez qu'un test réussisse si categoryName est absent ou présent avec une valeur de longueur nulle, vous devez utiliser test="not(categoryName = '')". La réponse fournie retournera false si l'élément categoryName est absent, ce qui, dans mon interprétation de la question, en fait une mauvaise réponse.
Michael Kay
2
@MichaelKay: J'ai mis à jour la réponse pour fournir plus de détails. Merci pour le commentaire et pour le processeur Saxon XSLT!
steamer25
Comment puis-je traduire <xsl:for-each select="root/*[matches(name(.), 'grp')]">pour qu'il puisse être utilisé dans VS2010?
Si8
276

En l'absence de toute autre information, je suppose que le XML suivant:

<group>
    <item>
        <id>item 1</id>
        <CategoryName>blue</CategoryName>
    </item>
    <item>
        <id>item 2</id>
        <CategoryName></CategoryName>
    </item>
    <item>
        <id>item 3</id>
    </item>
    ...
</group>

Un exemple d'utilisation ressemblerait à ceci:

<xsl:for-each select="/group/item">
    <xsl:if test="CategoryName">
        <!-- will be instantiated for item #1 and item #2 -->
    </xsl:if>
    <xsl:if test="not(CategoryName)">
        <!-- will be instantiated for item #3 -->
    </xsl:if>
    <xsl:if test="CategoryName != ''">
        <!-- will be instantiated for item #1 -->
    </xsl:if>
    <xsl:if test="CategoryName = ''">
        <!-- will be instantiated for item #2 -->
    </xsl:if>
</xsl:for-each>
johnvey
la source
Comment testez-vous les instances de </CategoryName>? , les tests de chaînes vides ne fonctionnent pas pour cela
raffian
3
Il est apprécié que vous ayez inclus plusieurs exemples pour montrer comment chaque expression résulte.
doubleJ
1
@raffian: dans XSLT, ou dans les technologies associées (XQuery, DOM, XDM, Schema etc.), les balises de fin ne sont pas considérées comme des entités distinctes. Au lieu de cela, vous ne traitez qu'avec des nœuds ou des éléments dans ce cas, qui est le tout entre la balise de début et la balise de fin. En bref, il n'y a aucun moyen de tester </CategoryName>, ni aucun besoin.
Abel
4
J'ai mis en vedette la question spécifiquement pour cette réponse, et bien que la question soit assez ancienne, celle-ci semble beaucoup plus mériter d'être la réponse sélectionnée
Patrick
68

De l' élément vide :

Pour tester si la valeur d'un certain nœud est vide

Cela dépend de ce que vous entendez par vide.

  • Ne contient aucun nœud enfant: not(node())
  • Ne contient aucun contenu textuel: not(string(.))
  • Ne contient aucun texte autre que des espaces: not(normalize-space(.))
  • Ne contient que des commentaires: not(node()[not(self::comment())])
Chris Doggett
la source
2
+1. Quelques notes. La première puce teste également le contenu textuel, qui est également un nœud. Le deuxième puce teste n'importe quel nœud de texte à n'importe quelle profondeur, si vous voulez savoir si le nœud actuel ne contient pas de texte, mais peut contenir d'autres nœuds, vous pouvez utiliser not(text()). Une alternative à votre 2ème puce est également not(.//text()). Comme le montre votre dernière puce: il existe de nombreuses façons de considérer le "néant";).
Abel
Très pratique: pour tester si une chaîne n'est pas vide, vous pouvez simplement tester la chaîne elle-même! if ($mystring) then ... else ...
Felix Dombek
22

Qu'en est-il de?

test="not(normalize-space(categoryName)='')"
helcim
la source
1
Cela fonctionne très bien. Même quand il y a un commentaire à l'intérieur <categoryName> <!-- some comment --> </categoryName>et sinon aucun texte significatif, cela true
vaut
9

Les deux premiers traitent avec une valeur nulle et les deux autres traitent avec une chaîne vide.

<xsl:if test="USER/FIRSTNAME">
    USERNAME is not null
</xsl:if>
<xsl:if test="not(USER/FIRSTNAME)">
    USERNAME is null
 </xsl:if>
 <xsl:if test="USER/FIRSTNAME=''">
     USERNAME is empty string
 </xsl:if>
 <xsl:if test="USER/FIRSTNAME!=''">
     USERNAME is not empty string
 </xsl:if>
Aleksandar Borkovac
la source
1
Effrayant. Et s'il y a plusieurs utilisateurs ou plusieurs prénoms? Utilisez xsl:apply-templateset associez des modèles pour obtenir ce que vous voulez, beaucoup plus facilement.
Abel
7

Dans certains cas, vous souhaiterez peut-être savoir quand la valeur est spécifiquement nulle, ce qui est particulièrement nécessaire lors de l'utilisation de XML qui a été sérialisé à partir d'objets .NET. Bien que la réponse acceptée fonctionne pour cela, elle renvoie également le même résultat lorsque la chaîne est vide ou vide, c'est-à-dire '', vous ne pouvez donc pas faire de différence.

<group>
    <item>
        <id>item 1</id>
        <CategoryName xsi:nil="true" />
    </item>
</group>

Vous pouvez donc simplement tester l'attribut.

<xsl:if test="CategoryName/@xsi:nil='true'">
   Hello World.
</xsl:if>

Parfois, il est nécessaire de connaître l'état exact et vous ne pouvez pas simplement vérifier si CategoryName est instancié, car contrairement à dire Javascript

<xsl:if test="CategoryName">
   Hello World.
</xsl:if>

Renvoie true pour un élément null.

DustJones
la source
6

Je sais que cette question est ancienne, mais entre toutes les réponses, je manque celle qui est une approche courante pour ce cas d'utilisation dans le développement XSLT.

J'imagine que le code manquant de l'OP ressemble à ceci:

<xsl:template match="category">
    <xsl:choose>
        <xsl:when test="categoryName !=null">
            <xsl:value-of select="categoryName " />
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="other" />
        </xsl:otherwise>
    </xsl:choose>
</category>

Et que l'entrée ressemble à ceci:

<categories>
    <category>
       <categoryName>Books</categoryName>
    </category>
    <category>
       <categoryName>Magazines</categoryName>
       <categoryName>Periodicals</categoryName>
       <categoryName>Journals</categoryName>
    </category>
    <category>
        <categoryName><!-- please fill in category --></categoryName>
    </category>
    <category>
        <categoryName />
    </category>
    <category />
</categories>

C'est-à-dire que je suppose qu'il peut y avoir zéro, vide, un seul categoryNameélément ou plusieurs éléments. Traiter tous ces cas en utilisant xsl:choosedes constructions -style, ou en d'autres termes, impérativement, devient rapidement compliqué (encore plus si les éléments peuvent être à différents niveaux!). Un idiome de programmation typique dans XSLT utilise des modèles (d'où le T dans XSLT), qui est une programmation déclarative, pas impérative (vous ne dites pas au processeur quoi faire, vous dites simplement ce que vous voulez sortir si certaines conditions sont remplies). Pour ce cas d'utilisation, cela peut ressembler à ceci:

<!-- positive test, any category with a valid categoryName -->
<xsl:template match="category[categoryName[text()]]">
    <xsl:apply-templates />
</xsl:template>

<!-- any other category (without categoryName, "null", with comments etc) -->
<xsl:template match="category">
    <xsl:text>Category: Other</xsl:text>
</xsl:template>

<!-- matching the categoryName itself for easy handling of multiple names -->
<xsl:template match="categoryName">
    <xsl:text>Category: </xsl:text>
    <xsl:value-of select="." />
</xsl:template>

Cela fonctionne (avec n'importe quelle version XSLT), car la première ci-dessus a une priorité plus élevée (elle a un prédicat). Le modèle de correspondance "fall-through", le second, capture tout ce qui n'est pas valide. Le troisième se charge ensuite de sortir lecategoryName valeur de manière appropriée.

Notez que dans ce scénario, il n'est pas nécessaire de faire correspondre spécifiquement categoriesou category, car le processeur traitera automatiquement tous les enfants, sauf indication contraire (dans cet exemple, les deuxième et troisième modèles ne traitent pas davantage les enfants, car il n'y a pas xsl:apply-templatesdans leur).

Cette approche est plus facilement extensible que l'approche impérative, car elle traite automatiquement plusieurs catégories et peut être développée pour d'autres éléments ou exceptions en ajoutant simplement un autre modèle correspondant. Programmation sans if-branches .

Remarque: il n'y a rien de tel qu'en nullXML. Il existe xsi: nil , mais il est rarement utilisé, surtout rarement dans des scénarios non typés sans schéma d'aucune sorte.

Abel
la source
1
Félicitations pour avoir mentionné " Programmation sans if-branches ". Il y a des gens qui ne comprennent pas l'importance de cela. Pour chacun d'entre eux, voici un lien vers un très joli cours Pluralsight sur ce sujet: " Modèles de conception tactique dans .NET: Control Flow " par Zoran Horvat: app.pluralsight.com/library/courses/… A lire absolument!
Dimitre Novatchev
5

Comment puis-je vérifier si une valeur est nulle ou vide avec XSL?

Par exemple, si categoryNameest vide?

C'est probablement l'expression XPath la plus simple (celle de la réponse acceptée fournit un test pour le contraire, et serait plus longue si elle était annulée):

not(string(categoryName))

Explication :

L'argument de la not()fonction ci-dessus est false()exactement lorsqu'il n'y a pas d' categoryNameenfant ("null") de l'élément de contexte, ou lorsque l' enfant (unique) categoryNamea une valeur de chaîne - la chaîne vide.

J'utilise un lors du choix de la construction.

Par exemple:

<xsl:choose>
    <xsl:when test="categoryName !=null">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>

Dans XSLT 2.0, utilisez :

<xsl:copy-of select="concat(categoryName,  $vOther[not(string(current()/categoryName))])"/>

Voici un exemple complet :

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vOther" select="'Other'"/>

 <xsl:template match="/">
  <xsl:copy-of select="concat(categoryName,$vOther[not(string(current()/categoryName))])"/>
 </xsl:template>
</xsl:stylesheet>

Lorsque cette transformation est appliquée sur le document XML suivant:

<categoryName>X</categoryName>

le résultat voulu et correct est produit :

X

Lorsqu'il est appliqué sur ce document XML :

<categoryName></categoryName>

ou sur ceci:

<categoryName/>

ou sur ce

<somethingElse>Y</somethingElse>

le résultat correct est produit :

Other

De même, utilisez cette transformation XSLT 1.0 :

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

 <xsl:variable name="vOther" select="'Other'"/>

  <xsl:template match="/">
    <xsl:copy-of select=
    "concat(categoryName,  substring($vOther, 1 div not(string(categoryName))))"/>
  </xsl:template>
</xsl:stylesheet>

Notez : aucun conditionnel n'est utilisé. En savoir plus sur l'importance d'éviter les constructions conditionnelles dans ce joli cours Pluralsight:

" Modèles de conception tactique dans .NET: flux de contrôle "

Dimitre Novatchev
la source
Salut Dimitre, j'ai besoin de votre solution pour 1.0, dois-je donc la coder dans toutes les balises que j'ai ou existe-t-il un moyen plus simple de l'implémenter pour l'ensemble du XML?
zyberjock
@zyberjock, ce que vous demandez n'est pas clair. Postez une question et envoyez-moi un commentaire avec un lien. Suivez les directives pour poser une bonne question.
Dimitre Novatchev
Salut @Dimitre, j'ai posté une question ici stackoverflow.com/questions/38150093/…
zyberjock
4

S'il y a une possibilité que l'élément n'existe pas dans le XML, je testerais à la fois que l'élément est présent et que la longueur de chaîne est supérieure à zéro:

<xsl:choose>
    <xsl:when test="categoryName and string-length(categoryName) &gt; 0">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>
Marie Taylor
la source
3
La valeur de chaîne d'un ensemble de nœuds vide (qui est ce que l'expression XPath categoryNamevous donne lorsqu'il n'y a aucun categoryNameélément enfant dans le contexte actuel) est définie comme étant la chaîne vide, donc c'est redondant - string-length(categoryName)est zéro s'il n'y a pas d' categoryNameéléments.
Ian Roberts
3

Si un nœud n'a aucune valeur disponible dans le xml d'entrée comme ci-dessous xpath,

<node>
    <ErrorCode/>
</node>

La fonction string () est convertie en valeur vide. Donc ça marche bien:

string(/Node/ErrorCode) =''
Sanjeev Singh
la source
2

Quelque chose comme ça fonctionne pour moi:

<xsl:choose>
  <xsl:when test="string(number(categoryName)) = 'NaN'"> - </xsl:when> 
  <xsl:otherwise> 
    <xsl:number value="categoryName" />
  </xsl:otherwise>
</xsl:choose>

Ou l'inverse:

<xsl:choose>
  <xsl:when test="string(number(categoryName)) != 'NaN'">
    <xsl:number value="categoryName" />
  </xsl:when> 
  <xsl:otherwise> - </xsl:otherwise>
</xsl:choose>

Remarque: Si vous ne vérifiez pas les valeurs nulles ou ne gérez pas les valeurs nulles, IE7 renvoie -2147483648 au lieu de NaN.

HSol
la source
1

J'ai trouvé qu'il valait mieux juste tester la longueur de la chaîne car plusieurs fois le champ n'est pas nul, juste vide

<xsl: when test = "string-length (field-you-want-to-test) <1">

Pedro Pereira
la source
0

D'après mon expérience, le meilleur moyen est:

<xsl:when test="not(string(categoryName))">
    <xsl:value-of select="other" />
</xsl:when>
<otherwise>
    <xsl:value-of select="categoryName" />
</otherwise>
dr_leevsey
la source
0

Utilisez simple categoryName / text () Un tel test fonctionne très bien <categoryName/>aussi <categoryName></categoryName>.

<xsl:choose>
    <xsl:when test="categoryName/text()">
        <xsl:value-of select="categoryName" />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>
Jaroslav Kubacek
la source