Compruebe si una cadena es nula o vacía en XSLT

325

¿Cómo puedo verificar si un valor es nulo o está vacío con XSL ?

Por ejemplo, si categoryNameestá vacío? Estoy usando un al elegir la construcción.

Por ejemplo:

<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
fuente
¿Puedes expandir el código de ejemplo?
Nick Allen
Dependiendo de su caso de uso, probablemente no quiera usarlo xsl:whenpara pruebas de nodo. Considere <xsl:template match="Category[categoryName[not(node())]]">...junto con a <xsl:template match="Category">.... El procesador tomará las decisiones correctas por usted y ya no necesitará escribir la lógica de negocios en anidado xsl:choose. En muchos casos, el uso de plantillas coincidentes facilita la escritura de hojas de estilo.
Abel

Respuestas:

322
test="categoryName != ''"

Editar : Esto cubre la interpretación más probable, en mi opinión, de "[no] nulo o vacío" como se infiere de la pregunta, incluido su pseudocódigo y mi propia experiencia inicial con XSLT. Es decir, "¿Cuál es el equivalente de la siguiente Java?":

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

Para obtener más detalles, por ejemplo, identificar claramente nulo frente a vacío, consulte la respuesta de johnvey a continuación y / o el 'violín' XSLT que he adaptado de esa respuesta, que incluye la opción en el comentario de Michael Kay, así como la sexta interpretación posible.

vapor25
fuente
14
La semántica detallada de esta prueba es: devuelve verdadero si hay al menos un elemento categoryName cuyo valor de cadena es una cadena vacía.
jelovirt
14
@jelovirt, ¿quiso decir si hay al menos un categoryName que NO es una cadena vacía? (Soy un novato xsl, así que perdona cualquier posible estupidez a mi pregunta.)
Joeevon
10
Esta respuesta, aunque aceptada y muy votada, también es muy engañosa. Realmente depende de lo que quieras decir con "nulo o vacío". Si desea una prueba que tenga éxito si categoryName está ausente o presente con un valor de longitud cero, debe usar test="not(categoryName = '')". La respuesta proporcionada devolverá falso si el elemento categoryName está ausente, lo que en mi interpretación de la pregunta hace que sea una respuesta incorrecta.
Michael Kay
2
@MichaelKay: He actualizado la respuesta para proporcionar más detalles. ¡Gracias por el comentario y por el procesador Saxon XSLT!
steamer25
¿Cómo puedo traducir <xsl:for-each select="root/*[matches(name(.), 'grp')]">para que pueda usarse en VS2010?
Si8
276

En ausencia de cualquier otra información, asumiré el siguiente 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 ejemplo de caso de uso se vería así:

<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
fuente
¿Cómo se prueban las instancias de </CategoryName>? , Pruebas de cadena vacía no funcionan para este
raffian
3
Se agradece que haya incluido varios ejemplos para mostrar cómo resulta cada expresión.
doubleJ
1
@raffian: en XSLT o tecnologías relacionadas (XQuery, DOM, XDM, Schema, etc.), las etiquetas finales no se consideran entidades separadas. En cambio, solo trata con nodos o elementos en este caso, que es el conjunto entre la etiqueta de inicio y la etiqueta de finalización. En resumen, no hay forma de hacer una prueba </CategoryName>, ni tampoco es necesario.
Abel
44
Destaqué la pregunta específicamente para esta respuesta, y aunque la pregunta es bastante antigua, esta parece ser mucho más merecedora de ser la respuesta seleccionada
Patrick
68

Del elemento vacío :

Para probar si el valor de un determinado nodo está vacío

Depende de lo que quieras decir con vacío.

  • No contiene nodos secundarios: not(node())
  • No contiene contenido de texto: not(string(.))
  • No contiene ningún texto que no sea espacio en blanco: not(normalize-space(.))
  • No contiene nada excepto comentarios: not(node()[not(self::comment())])
Chris Doggett
fuente
2
+1. Algunas notas. El primer punto de viñeta también prueba el contenido de texto, que también es un nodo. La segunda viñeta prueba cualquier nodo de texto a cualquier profundidad, si desea saber si el nodo actual no contiene texto, pero puede contener otros nodos, puede usar not(text()). Una alternativa a su segunda viñeta es también not(.//text()). Como muestra su última viñeta: hay muchas formas de considerar la "nada";).
Abel
Muy práctico: para probar si una cadena no está vacía, ¡solo puede probar la cadena en sí! if ($mystring) then ... else ...
Felix Dombek
22

¿Qué pasa?

test="not(normalize-space(categoryName)='')"
helcim
fuente
1
Esto funciona muy bien. Incluso cuando hay un comentario dentro <categoryName> <!-- some comment --> </categoryName>y, de lo contrario, no hay texto significativo, esto todavía se evalúa comotrue
rustyx
9

Los primeros dos tratan con un valor nulo y los segundos dos tratan con una cadena vacía.

<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
fuente
1
De miedo. ¿Qué pasa si hay múltiples usuarios o múltiples nombres? Use xsl:apply-templatesy combine plantillas para obtener lo que desea, mucho más fácil.
Abel
7

En algunos casos, es posible que desee saber cuándo el valor es específicamente nulo, lo cual es particularmente necesario cuando se utiliza XML que se ha serializado a partir de objetos .NET. Si bien la respuesta aceptada funciona para esto, también devuelve el mismo resultado cuando la cadena está en blanco o vacía, es decir, '', por lo que no puede diferenciar.

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

Entonces puedes simplemente probar el atributo.

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

A veces es necesario saber el estado exacto y no puede simplemente verificar si se crea una instancia de CategoryName, porque a diferencia de decir Javascript

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

Devuelve verdadero para un elemento nulo.

DustJones
fuente
6

Sé que esta pregunta es antigua, pero entre todas las respuestas, echo de menos una que es un enfoque común para este caso de uso en el desarrollo de XSLT.

Me imagino que el código faltante del OP se ve así:

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

Y que la entrada se ve así:

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

Es decir, supongo que puede haber cero, elementos vacíos, únicos o múltiples categoryName. Tratar todos estos casos usando xsl:chooseconstrucciones de estilo, o en otras palabras, imperativamente, se está volviendo rápidamente desordenado (¡aún más si los elementos pueden estar en diferentes niveles!). Un lenguaje de programación típico en XSLT es usar plantillas (de ahí la T en XSLT), que es programación declarativa, no imperativa (no le dices al procesador qué hacer, solo dices qué quieres que salga si se cumplen ciertas condiciones). Para este caso de uso, puede tener un aspecto similar al siguiente:

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

Esto funciona (con cualquier versión XSLT), porque la primera de arriba tiene una mayor prioridad (tiene un predicado). La plantilla de coincidencia "fall-through", la segunda, captura cualquier cosa que no sea válida. El tercero se encarga de generar el categoryNamevalor de manera adecuada.

Tenga en cuenta que en este escenario no hay necesidad para que coincida con specifially categorieso category, debido a que el procesador procesará automáticamente todos los niños, a menos que decimos que de otra forma (en este ejemplo, la segunda y la tercera plantilla No más trámite a los niños, porque no hay xsl:apply-templatesen ellos).

Este enfoque es más fácilmente extensible que el enfoque imperativo, ya que trata automáticamente con múltiples categorías y puede expandirse para otros elementos o excepciones simplemente agregando otra plantilla coincidente. Programación sin if-sucursales .

Nota: no existe tal cosa como nullen XML. Hay xsi: nil , pero rara vez se usa, especialmente en escenarios sin tipo sin un esquema de algún tipo.

Abel
fuente
1
Felicidades por mencionar " Programación sin if-sucursales ". Hay algunas personas que no entienden la importancia de esto. Para todos ellos aquí hay un enlace a un curso Pluralsight muy agradable sobre este tema: " Patrones de diseño táctico en .NET: Control Flow " por Zoran Horvat: app.pluralsight.com/library/courses/… ¡ Una lectura obligada!
Dimitre Novatchev
5

¿Cómo puedo verificar si un valor es nulo o está vacío con XSL?

Por ejemplo, si categoryNameestá vacío?

Esta es probablemente la expresión XPath más simple (la respuesta aceptada proporciona una prueba para lo contrario, y sería más larga, si se niega):

not(string(categoryName))

Explicacion :

El argumento de la not()función anterior es false()exactamente cuando no hay un elemento categoryNamesecundario ("nulo") del elemento de contexto, o el elemento categoryNamesecundario (único) tiene un valor de cadena: la cadena vacía.

Estoy usando un al elegir la construcción.

Por ejemplo:

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

En XSLT 2.0 use :

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

Aquí hay un ejemplo 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>

Cuando esta transformación se aplica en el siguiente documento XML:

<categoryName>X</categoryName>

se produce el resultado deseado y correcto :

X

Cuando se aplica en este documento XML :

<categoryName></categoryName>

o sobre esto:

<categoryName/>

o en esto

<somethingElse>Y</somethingElse>

Se produce el resultado correcto :

Other

Del mismo modo, use esta transformación 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 : no se utilizan condicionales en absoluto. Aprenda más sobre la importancia de evitar construcciones condicionales en este agradable curso de Pluralsight:

" Patrones de diseño táctico en .NET: flujo de control "

Dimitre Novatchev
fuente
Hola Dimitre, necesito tu solución para 1.0, entonces, ¿necesito codificarla en cada etiqueta que tengo o hay una forma más sencilla de impedirlo para todo el XML?
zyberjock 01 de
@zyberjock, no está claro lo que está preguntando. Por favor, publique una pregunta y envíeme un comentario con un enlace. Siga las pautas sobre cómo hacer una buena pregunta.
Dimitre Novatchev
Hola @Dimitre, publiqué una pregunta aquí stackoverflow.com/questions/38150093/…
zyberjock
4

Si existe la posibilidad de que el elemento no exista en el XML, probaría que el elemento está presente y que la longitud de la cadena es mayor que cero:

<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
fuente
3
El valor de cadena de un conjunto de nodos vacío (que es lo que categoryNamele da la expresión XPath cuando no hay categoryNameelementos secundarios en el contexto actual) se define como la cadena vacía, por lo que esto es redundante; string-length(categoryName)es cero si no hay categoryNameelementos.
Ian Roberts
3

Si un nodo no tiene ningún valor disponible en la entrada xml como debajo de xpath,

<node>
    <ErrorCode/>
</node>

La función string () se convierte en valor vacío. Entonces esto funciona bien:

string(/Node/ErrorCode) =''
Sanjeev Singh
fuente
2

Algo como esto funciona para mí:

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

O al revés:

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

Nota: Si no verifica valores nulos o maneja valores nulos, IE7 devuelve -2147483648 en lugar de NaN.

HSol
fuente
1

De hecho, lo encontré mejor solo probando la longitud de la cadena ya que muchas veces el campo no es nulo, solo está vacío

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

Pedro Pereira
fuente
0

Según mi experiencia, la mejor manera es:

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

Use simple categoryName / text () Tal prueba funciona bien en <categoryName/>y también <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
fuente