Sprawdź, czy ciąg znaków jest pusty lub pusty w XSLT

325

Jak mogę sprawdzić, czy wartość jest pusta lub pusta w XSL ?

Na przykład, jeśli categoryNamejest pusty? Używam przy wyborze konstruktu.

Na przykład:

<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
źródło
Czy możesz rozwinąć przykład kodu?
Nick Allen
W zależności od przypadku użycia prawdopodobnie nie chcesz używać xsl:whendo testów węzłów. Zastanów się <xsl:template match="Category[categoryName[not(node())]]">...razem z <xsl:template match="Category">.... Procesor podejmie wtedy właściwe dla Ciebie decyzje i nie musisz już zapisywać logiki biznesowej w zagnieżdżonym xsl:choose. W wielu przypadkach użycie pasujących szablonów ułatwia pisanie arkuszy stylów.
Abel

Odpowiedzi:

322
test="categoryName != ''"

Edycja : Obejmuje to, moim zdaniem, najbardziej prawdopodobną interpretację „[nie] zerowy lub pusty”, jak wynika z pytania, w tym pseudokod i moje własne wczesne doświadczenia z XSLT. Tj. „Co jest równoważne z następującą Javą?”:

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

Aby uzyskać więcej informacji, np. Wyraźne identyfikowanie wartości null vs. pusta, zobacz odpowiedź Johnavey poniżej i / lub „skrzypce” XSLT , które dostosowałem z tej odpowiedzi, która obejmuje opcję w komentarzu Michaela Kaya, a także szóstą możliwą interpretację.

parowiec 25
źródło
14
Szczegółowa semantyka tego testu to: zwróć wartość true, jeśli istnieje co najmniej jeden element categoryName, którego wartość ciągu jest pusta.
jelovirt
14
@jelovirt chciałeś powiedzieć, czy istnieje co najmniej jedna nazwa kategorii, która NIE jest pustym ciągiem? (Jestem początkującym xsl, więc wybacz mi wszelką potencjalną głupotę do mojego pytania).
joedevon
10
Ta odpowiedź, choć zaakceptowana i wysoko głosowana, jest również bardzo myląca. To naprawdę zależy od tego, co rozumiesz przez „pusty lub pusty”. Jeśli chcesz, aby test się powiódł, jeśli kategoriaNazwa jest nieobecna lub ma wartość zerową, powinieneś użyć test="not(categoryName = '')". Podana odpowiedź zwróci false, jeśli element categoryName jest nieobecny, co w mojej interpretacji pytania sprawia, że ​​jest to zła odpowiedź.
Michael Kay,
2
@MichaelKay: Zaktualizowałem odpowiedź, aby podać więcej szczegółów. Dzięki za komentarz i za saksoński procesor XSLT!
parowiec
Jak mogę przetłumaczyć, <xsl:for-each select="root/*[matches(name(.), 'grp')]">aby można go było używać w VS2010?
Si8
276

Wobec braku innych informacji przyjmuję następujący kod 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>

Przykładowy przypadek użycia wyglądałby następująco:

<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
źródło
Jak testujesz wystąpienia </CategoryName>? , testy pustych ciągów nie działają w tym przypadku
raffian
3
Rozumiemy, że podałeś wiele przykładów, aby pokazać, jak powstaje każde wyrażenie.
doubleJ
1
@raffian: w XSLT lub pokrewnych technologiach (XQuery, DOM, XDM, schemat itp.) znaczniki końcowe nie są uważane za osobne jednostki. Zamiast tego zajmujesz się tylko węzłami lub elementami w tym przypadku, które stanowią całość między znacznikiem początkowym a znacznikiem końcowym. Krótko mówiąc, nie ma sposobu na sprawdzenie </CategoryName>ani nie ma takiej potrzeby.
Abel
4
Oznacziłem to pytanie specjalnie dla tej odpowiedzi i chociaż pytanie jest dość stare, wydaje się, że bardziej zasługuje na to, aby być wybraną odpowiedzią
Patrick
68

Z pustego elementu :

Aby sprawdzić, czy wartość określonego węzła jest pusta

To zależy od tego, co rozumiesz przez „pusty”.

  • Nie zawiera węzłów potomnych: not(node())
  • Nie zawiera treści tekstowych: not(string(.))
  • Nie zawiera tekstu oprócz białych znaków: not(normalize-space(.))
  • Nie zawiera nic oprócz komentarzy: not(node()[not(self::comment())])
Chris Doggett
źródło
2
+1. Kilka notatek. Pierwszy punkt również sprawdza zawartość tekstu, który jest również węzłem. Drugi punkt wypunktowania sprawdza dowolny węzeł tekstowy na dowolnej głębokości, jeśli chcesz wiedzieć, czy bieżący węzeł nie zawiera tekstu, ale może zawierać inne węzły, możesz użyć not(text()). Alternatywą dla twojego drugiego pocisku jest również not(.//text()). Jak pokazuje twój ostatni pocisk: istnieje wiele sposobów na rozważenie „nicości”;).
Abel
Bardzo praktyczne: Aby sprawdzić, czy łańcuch nie jest pusty, możesz po prostu przetestować sam łańcuch! if ($mystring) then ... else ...
Felix Dombek
22

Co powiesz na?

test="not(normalize-space(categoryName)='')"
helcim
źródło
1
To działa świetnie. Nawet jeśli w środku jest komentarz, <categoryName> <!-- some comment --> </categoryName>a poza tym brak sensownego tekstu, to nadal oceniatrue
rustyx
9

Pierwsze dwa dotyczą wartości pustej, a drugie dwa pustego ciągu.

<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
źródło
1
Straszny. Co jeśli jest wielu użytkowników lub wiele imion? Użyj xsl:apply-templatesi dopasuj szablony, aby uzyskać to, czego chcesz, o wiele łatwiej.
Abel
7

W niektórych przypadkach możesz chcieć wiedzieć, kiedy wartość jest konkretnie pusta, co jest szczególnie konieczne, gdy używasz XML, który został zserializowany z obiektów .NET. Chociaż zaakceptowana odpowiedź działa w tym przypadku, zwraca ten sam wynik, gdy ciąg znaków jest pusty lub pusty, tj. „”, Więc nie można odróżnić.

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

Możesz więc po prostu przetestować atrybut.

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

Czasami konieczna jest znajomość dokładnego stanu i nie można po prostu sprawdzić, czy wystąpiła instancja CategoryName, ponieważ w przeciwieństwie do Javascript

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

Zwróci wartość true dla elementu zerowego.

DustJones
źródło
6

Wiem, że to pytanie jest stare, ale między wszystkimi odpowiedziami brakuje mi jednego, które jest powszechnym podejściem do tego przypadku użycia w rozwoju XSLT.

Wyobrażam sobie, że brakujący kod z OP wygląda mniej więcej tak:

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

I że dane wejściowe wyglądają mniej więcej tak:

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

Tzn. Zakładam, że może być zero, pusty, pojedynczy lub wiele categoryNameelementów. Aby poradzić sobie z tymi wszystkimi przypadkami za pomocą xsl:choosekonstruktów-style, lub innymi słowy, koniecznie szybko robi się bałagan (tym bardziej, jeśli elementy mogą znajdować się na różnych poziomach!). Typowym idiomem programistycznym w XSLT jest używanie szablonów (stąd T w XSLT), co jest programowaniem deklaratywnym, a nie koniecznym (nie mówisz procesorowi, co ma robić, po prostu mówisz, co chcesz uzyskać, jeśli spełnione są określone warunki). W tym przypadku użycia może to wyglądać mniej więcej tak:

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

Działa to (z każdą wersją XSLT), ponieważ pierwsza powyżej ma wyższy priorytet (ma predykat). Drugi szablon dopasowania „przechodzący” łapie wszystko, co nie jest prawidłowe. Trzeci zadba następnie categoryNameo prawidłowe wyprowadzenie wartości.

Należy zauważyć, że w tym scenariuszu nie ma potrzeby, aby specifially dopasować categorieslub category, ponieważ procesor będzie automatycznie przetwarzać wszystkie dzieci, chyba że mówimy to inaczej (w tym przykładzie, drugi i trzeci szablon nie dalej przetwarzać dzieci, bo nie ma xsl:apply-templatesw im).

To podejście jest łatwiejsze do rozszerzenia niż podejście imperatywne, ponieważ automatycznie zajmuje się wieloma kategoriami i można je rozszerzyć o inne elementy lub wyjątki, po prostu dodając inny pasujący szablon. Programowanie bez rozgałęzień if .

Uwaga: nie ma czegoś takiego jak nullw XML. Istnieje xsi: zero , ale jest to rzadko używane, szczególnie rzadko w nietypowych scenariuszach bez jakiegoś schematu.

Abel
źródło
1
Gratulujemy wspominania „ Programowanie bez rozgałęzień ”. Są ludzie, którzy nie rozumieją tego znaczenia. Dla nich wszystkich znajduje się link do bardzo ładnego kursu Pluralsight na ten temat: „ Taktyczne wzorce projektowe w .NET: Kontrola przepływu ” autorstwa Zorana Horvata: app.pluralsight.com/library/courses/… Trzeba przeczytać!
Dimitre Novatchev
5

Jak mogę sprawdzić, czy wartość jest pusta lub pusta w XSL?

Na przykład, jeśli categoryNamejest pusty?

Jest to prawdopodobnie najprostsze wyrażenie XPath (to w zaakceptowanej odpowiedzi zapewnia test odwrotny i byłoby dłuższe, gdyby było zanegowane):

not(string(categoryName))

Objaśnienie :

Argument not()funkcji powyżej jest false()dokładnie wtedy, gdy nie ma categoryNameelementu podrzędnego („null”) elementu kontekstu lub (jedno takie categoryNamedziecko ) ma wartość ciągu - pusty ciąg.

Używam przy wyborze konstruktu.

Na przykład:

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

W XSLT 2.0 użyj :

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

Oto kompletny przykład :

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

Po zastosowaniu tej transformacji w następującym dokumencie XML:

<categoryName>X</categoryName>

pożądany, poprawny wynik jest generowany :

X

Po zastosowaniu w tym dokumencie XML :

<categoryName></categoryName>

lub na ten temat:

<categoryName/>

lub w tej sprawie

<somethingElse>Y</somethingElse>

powstaje poprawny wynik :

Other

Podobnie użyj tej transformacji 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>

Uwaga : W ogóle nie są używane warunki warunkowe. Dowiedz się więcej o znaczeniu unikania konstrukcji warunkowych w tym ładnym kursie Pluralsight:

Wzorce projektowania taktycznego w .NET: kontrola przepływu

Dimitre Novatchev
źródło
Cześć Dimitre, potrzebuję twojego rozwiązania dla 1.0, więc czy muszę go kodować we wszystkich tagach, które posiadam, czy jest prostszy sposób na ulepszenie go dla całego XML?
zyberjock
@zyberjock, Nie jest jasne, o co pytasz. Proszę, zadaj pytanie i wyślij mi komentarz z linkiem. Postępuj zgodnie z wytycznymi, jak zadać dobre pytanie.
Dimitre Novatchev
Cześć @Dimitre, wysłałem pytanie tutaj stackoverflow.com/questions/38150093/...
zyberjock
4

Jeśli istnieje możliwość, że element nie istnieje w pliku XML, sprawdziłbym zarówno, czy element jest obecny, jak i długość łańcucha jest większa niż 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
źródło
3
Wartość ciągu pustego zestawu węzłów (co categoryNamedaje ci wyrażenie XPath , gdy categoryNamew bieżącym kontekście nie ma elementów potomnych) jest zdefiniowana jako pusty ciąg, więc jest nadmiarowy - string-length(categoryName)wynosi zero, jeśli nie ma żadnych categoryNameelementów.
Ian Roberts
3

Jeśli węzeł nie ma dostępnej wartości w wejściowym pliku XML, takim jak poniżej xpath,

<node>
    <ErrorCode/>
</node>

Funkcja string () konwertuje na pustą wartość. To działa dobrze:

string(/Node/ErrorCode) =''
Sanjeev Singh
źródło
2

Coś takiego działa dla mnie:

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

Albo na odwrót:

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

Uwaga: Jeśli nie sprawdzisz wartości zerowych lub nie obsłużysz wartości zerowych, IE7 zwróci -2147483648 zamiast NaN.

HSol
źródło
1

Właściwie to uważam, że lepiej jest po prostu sprawdzać długość łańcucha, ponieważ wiele razy pole nie jest puste, tylko puste

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

Pedro Pereira
źródło
0

Z mojego doświadczenia wynika, że ​​najlepszym sposobem jest:

<xsl:when test="not(string(categoryName))">
    <xsl:value-of select="other" />
</xsl:when>
<otherwise>
    <xsl:value-of select="categoryName" />
</otherwise>
dr_leevsey
źródło
0

Użyj prostej categoryName / text () Taki test działa dobrze na <categoryName/>i również <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
źródło