Controlla se una stringa è nulla o vuota in XSLT


325

Come posso verificare se un valore è nullo o vuoto con XSL ?

Ad esempio, se categoryNameè vuoto? Sto usando un quando scelgo il costrutto.

Per esempio:

<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>

Puoi espandere l'esempio di codice?
Nick Allen,

A seconda del caso d'uso, probabilmente non si desidera utilizzare xsl:whenper i test dei nodi. Considerare <xsl:template match="Category[categoryName[not(node())]]">...insieme a <xsl:template match="Category">.... Il processore prenderà quindi le decisioni giuste per te e non dovrai più scrivere la logica di business in nidificato xsl:choose. In molti casi, l'utilizzo di modelli di corrispondenza semplifica la scrittura di fogli di stile.
Abel,

Risposte:


322
test="categoryName != ''"

Modifica : copre l'interpretazione più probabile, secondo me, di "[non] nullo o vuoto" come dedotto dalla domanda, incluso il suo pseudo-codice e la mia prima esperienza con XSLT. Vale a dire, "Qual è l'equivalente del seguente Java?":

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

Per maggiori dettagli, ad esempio, identificando distintamente null vs empty, vedi la risposta di johnvey in basso e / o il "violino" XSLT che mi sono adattato da quella risposta, che include l'opzione nel commento di Michael Kay e la sesta interpretazione possibile.


14
La semantica dettagliata di questo test è: restituisce vero se esiste almeno un elemento categoryName il cui valore stringa è una stringa vuota.
jelovirt,

14
@jelovirt intendevi dire se esiste almeno una categoriaNome che NON è una stringa vuota? (Sono un principiante xsl, quindi perdona qualsiasi potenziale stupidità alla mia domanda.)
joedevon

10
Questa risposta, sebbene accettata e altamente votata, è anche molto fuorviante. Dipende davvero da cosa intendi per "null o vuoto". Se si desidera un test che ha esito positivo se categoryName è assente o presente con un valore di lunghezza zero, è necessario utilizzare test="not(categoryName = '')". La risposta fornita restituirà false se l'elemento categoryName è assente, il che nella mia interpretazione della domanda rende una risposta errata.
Michael Kay,

2
@MichaelKay: ho aggiornato la risposta per fornire maggiori dettagli. Grazie per il commento e per il processore Saxon XSLT!
steamer25,

Come posso tradurre in <xsl:for-each select="root/*[matches(name(.), 'grp')]">modo che possa essere utilizzato in VS2010?
Si8,

276

In assenza di altre informazioni, assumerò il seguente XML:

<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 esempio di utilizzo potrebbe apparire come:

<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>

Come test per le istanze di </CategoryName>? , i test delle stringhe vuote non funzionano per questo
raffian

3
È apprezzabile che tu abbia incluso più esempi per mostrare come ogni espressione risulta.
doubleJ

1
@raffian: in XSLT o nelle tecnologie correlate (XQuery, DOM, XDM, Schema ecc.), i tag di fine non sono considerati entità separate. Invece, in questo caso hai a che fare solo con nodi o elementi, che è il tutto tra start-tag e end-tag. In breve, non c'è modo di testare </CategoryName>, né ce n'è bisogno.
Abel,

4
Ho recitato la domanda appositamente per questa risposta, e mentre la domanda è piuttosto vecchia, questa sembra molto più meritevole di essere la risposta selezionata
Patrick

68

Dall'elemento vuoto :

Per verificare se il valore di un determinato nodo è vuoto

Dipende da cosa intendi per vuoto.

  • Non contiene nodi figlio: not(node())
  • Non contiene contenuto testuale: not(string(.))
  • Non contiene altro testo che spazi bianchi: not(normalize-space(.))
  • Non contiene nulla tranne i commenti: not(node()[not(self::comment())])

2
+1. Alcune note Il primo punto elenco verifica anche il contenuto del testo, che è anche un nodo. Il secondo punto elenco verifica qualsiasi nodo di testo a qualsiasi profondità, se si desidera sapere se il nodo corrente non contiene testo, ma può contenere altri nodi, è possibile utilizzare not(text()). Un'alternativa al tuo secondo proiettile è anche not(.//text()). Come mostra il tuo ultimo proiettile: ci sono molti modi per considerare il "nulla";).
Abel,

Molto pratico: per verificare se una stringa non è vuota, puoi semplicemente testare la stringa stessa! if ($mystring) then ... else ...
Felix Dombek,

22

Che dire?

test="not(normalize-space(categoryName)='')"

1
Funziona benissimo. Anche quando c'è un commento dentro <categoryName> <!-- some comment --> </categoryName>e altrimenti nessun testo significativo, questo continua a valutaretrue
rustyx

9

I primi due trattano con valore nullo e i secondi due trattano con stringa vuota.

<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>

1
Spaventoso. Cosa succede se ci sono più utenti o più nomi? Usa xsl:apply-templatese abbina i modelli per ottenere ciò che desideri, molto più facile.
Abel,

7

In alcuni casi, potresti voler sapere quando il valore è specificamente nullo, il che è particolarmente necessario quando si utilizza XML che è stato serializzato da oggetti .NET. Mentre la risposta accettata funziona per questo, restituisce anche lo stesso risultato quando la stringa è vuota o vuota, cioè '', quindi non è possibile differenziare.

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

Quindi puoi semplicemente testare l'attributo.

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

A volte è necessario conoscere lo stato esatto e non puoi semplicemente controllare se CategoryName è istanziato, perché a differenza di dire Javascript

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

Restituirà vero per un elemento null.


6

So che questa domanda è vecchia, ma tra tutte le risposte, mi manca una che è un approccio comune per questo caso d'uso nello sviluppo di XSLT.

Immagino che il codice mancante dall'OP assomigli a questo:

<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>

E che l'input è simile al seguente:

<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>

Cioè, suppongo che ci possano essere zero, vuoti, singoli o multipli categoryNameelementi. Affrontare tutti questi casi usando xsl:choosecostrutti in stile, o in altre parole, in modo imperativo, sta rapidamente diventando confuso (ancora di più se gli elementi possono essere a livelli diversi!). Un tipico linguaggio di programmazione in XSLT sta usando i template (da qui la T in XSLT), che è programmazione dichiarativa, non imperativa (non dite al processore cosa fare, dite semplicemente cosa volete output se sono soddisfatte determinate condizioni). Per questo caso d'uso, potrebbe essere simile al seguente:

<!-- 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>

Funziona (con qualsiasi versione XSLT), perché la prima sopra ha una precedenza più alta (ha un predicato). Il modello di corrispondenza "fall-through", il secondo, rileva tutto ciò che non è valido. Il terzo si occupa quindi di fornire il categoryNamevalore in modo corretto.

Si noti che in questo scenario non v'è alcuna necessità di specifially abbinare categorieso category, perché il processore elabora automaticamente tutti i bambini, a meno che non diciamo altrimenti (in questo esempio, il secondo e il terzo modello NON ulteriore processo i bambini, perché non c'è xsl:apply-templatesin loro).

Questo approccio è più facilmente estendibile di quello imperativo, perché si occupa automaticamente di più categorie e può essere espanso per altri elementi o eccezioni semplicemente aggiungendo un altro modello di corrispondenza. Programmazione senza if-branch .

Nota: non esiste qualcosa come nullin XML. C'è xsi: nil , ma è usato raramente, specialmente raramente in scenari non tipizzati senza uno schema di qualche tipo.


1
Congratulazioni per aver menzionato " Programmazione senza if-branch ". Ci sono alcune persone che non riescono a capire l'importanza di questo. Per tutti loro ecco un link a un bellissimo corso Pluralsight su questo argomento: " Modelli di progettazione tattica in .NET: flusso di controllo " di Zoran Horvat: app.pluralsight.com/library/courses/… Una lettura obbligata!
Dimitre Novatchev,

5

Come posso verificare se un valore è nullo o vuoto con XSL?

Ad esempio, se categoryNameè vuoto?

Questa è probabilmente l'espressione XPath più semplice (quella nella risposta accettata fornisce un test per il contrario e sarebbe più lunga, se negata):

not(string(categoryName))

Spiegazione :

L'argomento della not()funzione sopra è false()esattamente quando non c'è un categoryNamefiglio ("null") dell'elemento di contesto, o il (singolo tale) categoryNamefiglio ha un valore di stringa - la stringa vuota.

Sto usando un quando scelgo il costrutto.

Per esempio:

<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>

In XSLT 2.0 utilizzare :

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

Ecco un esempio completo :

<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>

Quando questa trasformazione viene applicata sul seguente documento XML:

<categoryName>X</categoryName>

si ottiene il risultato desiderato, corretto :

X

Se applicato su questo documento XML :

<categoryName></categoryName>

o su questo:

<categoryName/>

o su questo

<somethingElse>Y</somethingElse>

viene prodotto il risultato corretto :

Other

Allo stesso modo, utilizzare questa trasformazione 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>

Nota : non vengono utilizzati condizionali. Scopri di più sull'importanza di evitare costrutti condizionali in questo bel corso Pluralsight:

" Modelli di progettazione tattica in .NET: flusso di controllo "


Ciao Dimitre, ho bisogno della tua soluzione per 1.0, quindi devo codificarla in ogni tag che ho o c'è un modo più semplice per implementarla per l'intero XML?
zyberjock,

@zyberjock, Non è chiaro cosa stai chiedendo. Per favore, pubblica una domanda e inviami un commento con un link. Segui le linee guida su come porre una buona domanda.
Dimitre Novatchev,

Ciao @Dimitre, ho postato una domanda qui stackoverflow.com/questions/38150093/...
zyberjock

4

Se esiste la possibilità che l'elemento non esista nell'XML, verificherei sia che l'elemento sia presente sia che la lunghezza della stringa sia maggiore di zero:

<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>

3
Il valore di stringa di un set di nodi vuoto (che è quello che categoryNameti dà l' espressione XPath quando non ci sono categoryNameelementi figlio nel contesto corrente) è definito come stringa vuota, quindi è ridondante - string-length(categoryName)è zero se non ci sono categoryNameelementi.
Ian Roberts,

3

Se un nodo non ha alcun valore disponibile nell'xml di input come sotto xpath,

<node>
    <ErrorCode/>
</node>

La funzione string () viene convertita in valore vuoto. Quindi funziona bene:

string(/Node/ErrorCode) =''

2

Qualcosa del genere funziona per me:

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

O viceversa:

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

Nota: se non si controllano valori null o non si gestiscono valori null, IE7 restituisce -2147483648 invece di NaN.


1

In realtà l'ho trovato meglio solo testando la lunghezza della stringa poiché molte volte il campo non è nullo, solo vuoto

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


0

Per la mia esperienza il modo migliore è:

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

0

Usa semplice categoryName / testo () Tale test funziona bene <categoryName/>anche <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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.