Überprüfen Sie, ob eine Zeichenfolge in XSLT null oder leer ist

325

Wie kann ich mit XSL überprüfen, ob ein Wert null oder leer ist ?

Zum Beispiel, wenn categoryNameleer ist? Ich verwende ein bei der Auswahl des Konstrukts.

Zum Beispiel:

<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
quelle
Können Sie das Codebeispiel erweitern?
Nick Allen
Abhängig von Ihrem Anwendungsfall möchten Sie wahrscheinlich nicht xsl:whenfür Knotentests verwenden. Betrachten Sie <xsl:template match="Category[categoryName[not(node())]]">...zusammen mit einem <xsl:template match="Category">.... Der Prozessor trifft dann die richtigen Entscheidungen für Sie und Sie müssen die verschachtelte Geschäftslogik xsl:choosenicht mehr ausschreiben. In vielen Fällen erleichtert die Verwendung passender Vorlagen das Schreiben von Stylesheets.
Abel

Antworten:

322
test="categoryName != ''"

Bearbeiten : Dies deckt meiner Meinung nach die wahrscheinlichste Interpretation von "[nicht] null oder leer" ab, wie aus der Frage abgeleitet, einschließlich des Pseudocodes und meiner eigenen frühen Erfahrung mit XSLT. Dh "Was entspricht dem folgenden Java?":

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

Weitere Einzelheiten, z. B. die eindeutige Identifizierung von Null und Leer, finden Sie in Johnveys Antwort unten und / oder in der XSLT-Geige, die ich aus dieser Antwort übernommen habe. Diese enthält die Option in Michael Kays Kommentar sowie die sechstmögliche Interpretation.

Dampfer25
quelle
14
Die detaillierte Semantik dieses Tests lautet: return true, wenn mindestens ein categoryName-Element vorhanden ist, dessen Zeichenfolgenwert eine leere Zeichenfolge ist.
Jelovirt
14
@jelovirt wolltest du sagen, ob es mindestens einen Kategorienamen gibt, der KEINE leere Zeichenfolge ist? (Ich bin ein xsl-Neuling, also verzeihen Sie jede mögliche Dummheit meiner Frage.)
Joedevon
10
Diese Antwort ist zwar akzeptiert und hoch bewertet, aber auch sehr irreführend. Es kommt wirklich darauf an, was du mit "null oder leer" meinst. Wenn Sie einen Test wünschen, der erfolgreich ist, wenn categoryName entweder fehlt oder mit einem Wert von null vorhanden ist, sollten Sie verwenden test="not(categoryName = '')". Die angegebene Antwort gibt false zurück, wenn das Element categoryName fehlt, was bei meiner Interpretation der Frage zu einer falschen Antwort führt.
Michael Kay
2
@ MichaelKay: Ich habe die Antwort aktualisiert, um weitere Details bereitzustellen. Danke für den Kommentar und für den Saxon XSLT Prozessor!
Dampfer25
Wie kann ich übersetzen, <xsl:for-each select="root/*[matches(name(.), 'grp')]">damit es in VS2010 verwendet werden kann?
Si8
276

Ohne weitere Informationen gehe ich von folgendem XML aus:

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

Ein Anwendungsbeispiel würde folgendermaßen aussehen:

<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
quelle
Wie testest du auf Instanzen von </CategoryName>? , leere
Stringtests
3
Es wird geschätzt, dass Sie mehrere Beispiele hinzugefügt haben, um zu zeigen, wie jeder Ausdruck resultiert.
doubleJ
1
@raffian: In XSLT oder verwandten Technologien (XQuery, DOM, XDM, Schema usw.) werden End-Tags nicht als separate Entitäten betrachtet. Stattdessen beschäftigen Sie sich in diesem Fall nur mit Knoten oder Elementen, dh dem Ganzen zwischen Start-Tag und End-Tag. Kurz gesagt, es gibt keine Möglichkeit zum Testen </CategoryName>und es besteht auch keine Notwendigkeit dafür.
Abel
4
Ich habe die Frage speziell für diese Antwort markiert, und obwohl die Frage ziemlich alt ist, scheint diese viel mehr die ausgewählte Antwort zu verdienen
Patrick
68

Vom leeren Element :

Um zu testen, ob der Wert eines bestimmten Knotens leer ist

Es kommt darauf an, was du mit leer meinst.

  • Enthält keine untergeordneten Knoten: not(node())
  • Enthält keinen Textinhalt: not(string(.))
  • Enthält keinen anderen Text als Leerzeichen: not(normalize-space(.))
  • Enthält nichts außer Kommentare: not(node()[not(self::comment())])
Chris Doggett
quelle
2
+1. Einige Notizen. Der erste Aufzählungspunkt testet auch auf Textinhalt, der auch ein Knoten ist. Der zweite Aufzählungspunkt testet für jeden Textknoten in jeder Tiefe. Wenn Sie wissen möchten, ob der aktuelle Knoten keinen Text enthält, aber andere Knoten enthalten kann, können Sie ihn verwenden not(text()). Eine Alternative zu Ihrer 2. Kugel ist auch not(.//text()). Wie Ihre letzte Kugel zeigt: Es gibt viele Möglichkeiten, "Nichts" zu betrachten;).
Abel
Sehr praktisch: Um zu testen, ob eine Zeichenfolge nicht leer ist, können Sie einfach die Zeichenfolge selbst testen! if ($mystring) then ... else ...
Felix Dombek
22

Wie wäre es mit?

test="not(normalize-space(categoryName)='')"
Helcim
quelle
1
Das funktioniert super. Selbst wenn es einen Kommentar gibt <categoryName> <!-- some comment --> </categoryName>und ansonsten keinen aussagekräftigen Text, wird dies immer noch alstrue
rustyx
9

Die ersten beiden befassen sich mit dem Nullwert und die zweiten beiden mit der leeren Zeichenfolge.

<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
quelle
1
Unheimlich. Was ist, wenn mehrere Benutzer oder mehrere Vornamen vorhanden sind? Verwenden xsl:apply-templatesund passende Vorlagen, um das zu erhalten, was Sie möchten, viel einfacher.
Abel
7

In einigen Fällen möchten Sie möglicherweise wissen, wann der Wert speziell null ist. Dies ist insbesondere erforderlich, wenn Sie XML verwenden, das aus .NET-Objekten serialisiert wurde. Während die akzeptierte Antwort dafür funktioniert, gibt sie auch das gleiche Ergebnis zurück, wenn die Zeichenfolge leer oder leer ist, dh '', sodass Sie nicht unterscheiden können.

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

Sie können das Attribut also einfach testen.

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

Manchmal ist es notwendig, den genauen Status zu kennen, und Sie können nicht einfach überprüfen, ob CategoryName instanziiert ist, da im Gegensatz zu Javascript

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

Gibt true für ein Nullelement zurück.

DustJones
quelle
6

Ich weiß, dass diese Frage alt ist, aber zwischen all den Antworten vermisse ich eine, die ein gängiger Ansatz für diesen Anwendungsfall in der XSLT-Entwicklung ist.

Ich stelle mir vor, dass der fehlende Code im OP ungefähr so ​​aussieht:

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

Und dass die Eingabe ungefähr so ​​aussieht:

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

Das heißt, ich gehe davon aus, dass es null, leere, einzelne oder mehrere categoryNameElemente geben kann. Mit all diesen Fällen mit xsl:chooseKonstrukten im Stil umzugehen , oder mit anderen Worten, zwingend, wird schnell chaotisch (umso mehr, wenn sich Elemente auf verschiedenen Ebenen befinden können!). Eine typische Programmiersprache in XSLT ist die Verwendung von Vorlagen (daher das T in XSLT). Dies ist eine deklarative Programmierung, nicht zwingend erforderlich (Sie teilen dem Prozessor nicht mit, was zu tun ist, sondern nur, was ausgegeben werden soll, wenn bestimmte Bedingungen erfüllt sind). Für diesen Anwendungsfall kann das ungefähr so ​​aussehen:

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

Dies funktioniert (mit jeder XSLT-Version), da die erste oben eine höhere Priorität hat (sie hat ein Prädikat). Die "Fall-Through" -Matching-Vorlage, die zweite, fängt alles auf, was nicht gültig ist. Der dritte sorgt dann dafür, dass der categoryNameWert ordnungsgemäß ausgegeben wird.

Beachten Sie, dass in diesem Szenario keine spezifische Übereinstimmung erforderlich ist categoriesoder category, da der Prozessor automatisch alle untergeordneten Elemente verarbeitet, sofern nicht anders angegeben (in diesem Beispiel werden die untergeordneten Elemente in der zweiten und dritten Vorlage nicht weiter verarbeitet, da kein xsl:apply-templatesIn vorhanden ist Sie).

Dieser Ansatz ist leichter erweiterbar als der zwingende Ansatz, da er automatisch mehrere Kategorien behandelt und durch Hinzufügen einer weiteren passenden Vorlage für andere Elemente oder Ausnahmen erweitert werden kann. Programmierung ohne if-Verzweigungen .

Hinweis: nullIn XML gibt es keine . Es gibt xsi: nil , aber das wird selten verwendet, besonders selten in untypisierten Szenarien ohne irgendein Schema.

Abel
quelle
1
Herzlichen Glückwunsch zur Erwähnung von " Programmieren ohne If-Zweige ". Es gibt einige Leute, die die Wichtigkeit davon nicht verstehen. Für alle hier ist ein Link zu einem sehr schönen Pluralsight-Kurs zu diesem Thema: " Taktische Entwurfsmuster in .NET: Kontrollfluss " von Zoran Horvat: app.pluralsight.com/library/courses/… Ein Muss!
Dimitre Novatchev
5

Wie kann ich mit XSL überprüfen, ob ein Wert null oder leer ist?

Zum Beispiel, wenn categoryNameleer ist?

Dies ist wahrscheinlich der einfachste XPath-Ausdruck (der in der akzeptierten Antwort liefert einen Test für das Gegenteil und wäre länger, wenn er negiert würde):

not(string(categoryName))

Erklärung :

Das Argument für die not()obige Funktion istfalse() genau dann, wenn es kein categoryNameuntergeordnetes Element ("null") des Kontextelements gibt oder das (einzelne solche) categoryNameuntergeordnete Element einen Zeichenfolgenwert hat - die leere Zeichenfolge.

Ich benutze eine bei der Auswahl des Konstrukts.

Zum Beispiel:

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

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

Hier ist ein vollständiges Beispiel :

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

Wenn diese Umwandlung auf das folgende XML-Dokument angewendet wird:

<categoryName>X</categoryName>

Das gewünschte, korrekte Ergebnis wird erzeugt :

X

Bei Anwendung auf dieses XML-Dokument :

<categoryName></categoryName>

oder dazu:

<categoryName/>

oder darauf

<somethingElse>Y</somethingElse>

Das richtige Ergebnis wird erzielt :

Other

Verwenden Sie in ähnlicher Weise diese XSLT 1.0- Transformation:

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

Beachten Sie : Es werden überhaupt keine Bedingungen verwendet. Erfahren Sie in diesem schönen Pluralsight-Kurs mehr darüber, wie wichtig es ist, bedingte Konstrukte zu vermeiden:

" Taktische Entwurfsmuster in .NET: Kontrollfluss "

Dimitre Novatchev
quelle
Hallo Dimitre, ich brauche deine Lösung für 1.0. Muss ich sie also in allen Tags codieren, die ich habe, oder gibt es eine einfachere Möglichkeit, sie für das gesamte XML zu implementieren?
Zyberjock
@zyberjock, Es ist unklar, was Sie fragen. Bitte posten Sie eine Frage und senden Sie mir einen Kommentar mit einem Link. Befolgen Sie die Richtlinien, um eine gute Frage zu stellen.
Dimitre Novatchev
Hallo @Dimitre, ich habe hier eine Frage gepostet stackoverflow.com/questions/38150093/…
zyberjock
4

Wenn die Möglichkeit besteht, dass das Element im XML nicht vorhanden ist, würde ich testen, ob das Element vorhanden ist und ob die Zeichenfolgenlänge größer als Null ist:

<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
quelle
3
Der Zeichenfolgenwert eines leeren Knotensatzes (den der XPath-Ausdruck categoryNameangibt, wenn categoryNameim aktuellen Kontext keine untergeordneten Elemente vorhanden sind ) wird als leere Zeichenfolge definiert. Dies ist also redundant - string-length(categoryName)ist Null, wenn keine categoryNameElemente vorhanden sind .
Ian Roberts
3

Wenn für einen Knoten in der Eingabe-XML kein Wert verfügbar ist, wie unter xpath,

<node>
    <ErrorCode/>
</node>

Die Funktion string () wird in einen leeren Wert konvertiert. Das funktioniert also gut:

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

So etwas funktioniert bei mir:

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

Oder umgekehrt:

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

Hinweis: Wenn Sie keine Nullwerte prüfen oder keine Nullwerte verarbeiten, gibt IE7 -2147483648 anstelle von NaN zurück.

HSol
quelle
1

Ich fand es eigentlich besser, nur auf die Länge der Zeichenfolge zu testen, da das Feld oft nicht null ist, sondern nur leer

<xsl: when test = "Zeichenfolgenlänge (Feld, das Sie testen möchten) <1">

Pedro Pereira
quelle
0

Nach meiner Erfahrung ist der beste Weg:

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

Verwenden Sie einfach categoryName / text (). Ein solcher Test funktioniert auch <categoryName/>und <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
quelle