Verifique se uma sequência é nula ou vazia no XSLT

325

Como posso verificar se um valor é nulo ou vazio com XSL ?

Por exemplo, se categoryNameestá vazio? Eu estou usando um ao escolher construir.

Por exemplo:

<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
fonte
Você pode expandir o exemplo de código?
Nick Allen
Dependendo do seu caso de uso, você provavelmente não deseja usar xsl:whenpara testes de nó. Considere <xsl:template match="Category[categoryName[not(node())]]">...junto com a <xsl:template match="Category">.... O processador tomará as decisões corretas para você e você não precisará mais escrever a lógica de negócios no aninhado xsl:choose. Em muitos casos, o uso de modelos correspondentes facilita a escrita de folhas de estilo.
Abel

Respostas:

322
test="categoryName != ''"

Edit : Isso cobre a interpretação mais provável, na minha opinião, de "[não] nulo ou vazio", como inferido da pergunta, incluindo seu pseudo-código e minha própria experiência inicial com o XSLT. Ou seja, "Qual é o equivalente do seguinte Java?":

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

Para obter mais detalhes, por exemplo, identificando claramente nulo x vazio, consulte a resposta de johnvey abaixo e / ou o 'violino' do XSLT que adaptei dessa resposta, que inclui a opção no comentário de Michael Kay e a sexta interpretação possível.

steamer25
fonte
14
A semântica detalhada desse teste é: return true se houver pelo menos um elemento categoryName cujo valor da string seja uma string vazia.
jelovirt
14
@jelovirt, você quis dizer se existe pelo menos uma categoriaName que NÃO é uma string vazia? (Eu sou um novato xsl, então perdoar qualquer estupidez potencial para a minha pergunta.)
joedevon
10
Essa resposta, apesar de aceita e altamente votada, também é muito enganadora. Realmente depende do que você quer dizer com "nulo ou vazio". Se você deseja um teste que seja bem-sucedido se categoryName estiver ausente ou presente com um valor de comprimento zero, você deve usar test="not(categoryName = '')". A resposta fornecida retornará false se o elemento categoryName estiver ausente, o que, na minha interpretação da pergunta, faz com que seja uma resposta errada.
Michael Kay
2
@ MichaelKay: Atualizei a resposta para fornecer mais detalhes. Obrigado pelo comentário e pelo processador Saxon XSLT!
precisa
Como posso traduzir <xsl:for-each select="root/*[matches(name(.), 'grp')]">para que possa ser usado no VS2010?
Si8
276

Na ausência de outras informações, assumirei o seguinte 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>

Um exemplo de caso de uso seria semelhante a:

<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
fonte
Como você testa instâncias de </CategoryName>? , Testes de cordas vazias não funcionam para isso
raffian
3
Apreciamos que você tenha incluído vários exemplos para mostrar como cada expressão resulta.
precisa
1
@raffian: no XSLT ou em tecnologias relacionadas (XQuery, DOM, XDM, Esquema etc.), as tags finais não são consideradas entidades separadas. Em vez disso, você lida apenas com nós ou elementos nesse caso, que é o todo entre a marca inicial e a final. Em suma, não há como testar </CategoryName>, nem há necessidade.
Abel
4
Eu estrelou a questão especificamente para esta resposta, e enquanto a questão é bastante antiga, este parece muito mais merecedor de ser a resposta escolhida
Patrick
68

Do elemento vazio :

Para testar se o valor de um determinado nó está vazio

Depende do que você quer dizer com vazio.

  • Não contém nós filhos: not(node())
  • Não contém conteúdo de texto: not(string(.))
  • Não contém texto além de espaço em branco: not(normalize-space(.))
  • Não contém nada, exceto comentários: not(node()[not(self::comment())])
Chris Doggett
fonte
2
+1. Algumas notas. O primeiro marcador também testa conteúdo de texto, que também é um nó. O segundo marcador de teste testa qualquer nó de texto em qualquer profundidade. Se você deseja saber se o nó atual não contém texto, mas pode conter outros nós, você pode usá-lo not(text()). Uma alternativa para o seu segundo marcador também é not(.//text()). Como mostra seu último marcador: existem várias maneiras de considerar o "nada";).
Abel
Muito prático: para testar se uma string não está vazia, você pode apenas testar a própria string! if ($mystring) then ... else ...
Felix Dombek 14/02
22

A respeito?

test="not(normalize-space(categoryName)='')"
helcim
fonte
1
Isso funciona muito bem. Mesmo quando há um comentário interno <categoryName> <!-- some comment --> </categoryName>e, de outro modo, nenhum texto significativo, ele ainda é avaliado comotrue
rustyx 15/09/2015
9

Os dois primeiros tratam com valor nulo e os dois segundos tratam com a string vazia.

<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
fonte
1
Assustador. E se houver vários usuários ou vários primeiros nomes? Use xsl:apply-templatese combine modelos para obter o que deseja, muito mais fácil.
Abel
7

Em alguns casos, convém saber quando o valor é especificamente nulo, o que é particularmente necessário ao usar XML que foi serializado a partir de objetos .NET. Embora a resposta aceita funcione para isso, ela também retorna o mesmo resultado quando a string está em branco ou vazia, ou seja, '', para que você não possa diferenciar.

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

Então você pode simplesmente testar o atributo.

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

Às vezes, é necessário saber o estado exato e você não pode simplesmente verificar se CategoryName é instanciado, porque, diferentemente do Javascript

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

Retornará true para um elemento nulo.

DustJones
fonte
6

Sei que essa pergunta é antiga, mas entre todas as respostas, sinto falta de uma que seja uma abordagem comum para esse caso de uso no desenvolvimento de XSLT.

Estou imaginando que o código ausente do OP se parece com isso:

<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 que a entrada se parece com isso:

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

Ou seja, suponho que pode haver zero, vazio, único ou vários categoryNameelementos. Lidar com todos esses casos usando xsl:chooseconstruções de estilo-ou, em outras palavras, imperativamente, está ficando rapidamente confuso (ainda mais se os elementos puderem estar em níveis diferentes!). Um idioma típico de programação no XSLT está usando modelos (daí o T no XSLT), que é uma programação declarativa, não imperativa (você não diz ao processador o que fazer, apenas diz o que deseja obter, se determinadas condições forem atendidas). Para este caso de uso, isso pode ser algo como o seguinte:

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

Isso funciona (com qualquer versão XSLT), porque o primeiro acima tem uma precedência mais alta (tem um predicado). O modelo correspondente "fall-through", o segundo, captura qualquer coisa que não seja válida. O terceiro, então, cuida da saída docategoryName valor de maneira adequada.

Observe que, nesse cenário, não há necessidade de corresponder categoriesoucategory camente , porque o processador processará automaticamente todos os filhos, a menos que seja indicado o contrário (neste exemplo, o segundo e o terceiro modelo não processam mais os filhos, porque não háxsl:apply-templates em eles).

Essa abordagem é mais facilmente extensível que a abordagem imperativa, porque lida automaticamente com várias categorias e pode ser expandida para outros elementos ou exceções apenas adicionando outro modelo correspondente. Programação sem if-branches .

Nota: não existe nullXML. Existe xsi: nil , mas isso raramente é usado, especialmente raramente em cenários sem tipo, sem um esquema de algum tipo.

Abel
fonte
1
Parabéns por mencionar " Programação sem if-branches ". Algumas pessoas não conseguem entender a importância disso. Para todos eles, aqui está um link para um curso muito agradável do Pluralsight sobre este tópico: " Padrões de Design Tático no .NET: Fluxo de Controle ", de Zoran Horvat: app.pluralsight.com/library/courses/… Uma leitura obrigatória!
Dimitre Novatchev
5

Como posso verificar se um valor é nulo ou vazio com XSL?

Por exemplo, se categoryNameestá vazio?

Essa é provavelmente a expressão XPath mais simples (a resposta aceita fornece um teste para o oposto e seria mais longo, se negado):

not(string(categoryName))

Explicação :

O argumento para a not()função acima é false()exatamente quando não há categoryNamefilho ("nulo") do item de contexto ou o categoryNamefilho (único) possui valor de sequência - a sequência vazia.

Eu estou usando um ao escolher construir.

Por exemplo:

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

No XSLT 2.0, use :

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

Aqui está um exemplo 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 essa transformação é aplicada no seguinte documento XML:

<categoryName>X</categoryName>

o resultado desejado e correto é produzido :

X

Quando aplicado neste documento XML :

<categoryName></categoryName>

ou sobre isso:

<categoryName/>

ou sobre isso

<somethingElse>Y</somethingElse>

o resultado correto é produzido :

Other

Da mesma forma, use esta transformação 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>

Observe : nenhum condicional é usado. Aprenda mais sobre a importância de evitar construções condicionais neste belo curso do Pluralsight:

" Padrões de design tático no .NET: fluxo de controle "

Dimitre Novatchev
fonte
Oi Dimitre, preciso da sua solução para a 1.0, então preciso codificá-la em todas as tags que possuo ou existe uma maneira mais simples de incrementá-la para todo o XML?
Zyberjock
@zyberjock, não está claro o que você está perguntando. Por favor, poste uma pergunta e me envie um comentário com um link. Siga as orientações de como fazer uma boa pergunta.
Dimitre Novatchev 01/07/16
Oi @Dimitre, eu postei uma pergunta aqui stackoverflow.com/questions/38150093/...
zyberjock
4

Se houver a possibilidade de o elemento não existir no XML, eu testaria se o elemento está presente e se o comprimento da string é maior que 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>
Marie Taylor
fonte
3
O valor da sequência de um conjunto de nós vazio (que é o que a expressão XPath categoryNamefornece quando não há categoryNameelementos filhos no contexto atual) é definido como a sequência vazia, portanto, isso é redundante - string-length(categoryName)é zero se não houver categoryNameelementos.
Ian Roberts
3

Se um nó não tiver um valor disponível no xml de entrada, como abaixo do xpath,

<node>
    <ErrorCode/>
</node>

A função string () converte em valor vazio. Então, isso funciona bem:

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

Algo assim funciona para mim:

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

Ou o contrário:

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

Nota: Se você não verificar valores nulos ou manipular valores nulos, o IE7 retornará -2147483648 em vez de NaN.

HSol
fonte
1

Na verdade, achei melhor apenas testar o comprimento da string, pois muitas vezes o campo não é nulo, apenas vazio

<xsl: when test = "comprimento da string (campo que você deseja testar) <1">

Pedro Pereira
fonte
0

Pela minha experiência, a melhor maneira é:

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

Use categoryName simples / texto () Esse teste funciona bem <categoryName/>e também <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
fonte