Periksa apakah string nol atau kosong di XSLT

325

Bagaimana saya bisa memeriksa apakah suatu nilai nol atau kosong dengan XSL ?

Misalnya, apakah categoryNamekosong? Saya menggunakan ketika memilih konstruksi.

Sebagai contoh:

<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
sumber
Bisakah Anda memperluas contoh kode?
Nick Allen
Tergantung pada kasus penggunaan Anda, Anda mungkin tidak ingin menggunakan xsl:whenuntuk tes-simpul. Pertimbangkan <xsl:template match="Category[categoryName[not(node())]]">...bersama dengan a <xsl:template match="Category">.... Prosesor kemudian akan membuat keputusan yang tepat untuk Anda dan Anda tidak perlu menuliskan logika bisnis dalam sarang xsl:chooselagi. Dalam banyak kasus, menggunakan templat yang cocok membuat lembar gaya penulisan lebih mudah.
Abel

Jawaban:

322
test="categoryName != ''"

Sunting : Ini mencakup interpretasi yang paling mungkin, menurut pendapat saya, tentang "[bukan] nol atau kosong" seperti yang disimpulkan dari pertanyaan, termasuk pseudo-code dan pengalaman awal saya sendiri dengan XSLT. Yaitu, "Apa yang setara dengan Java berikut?":

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

Untuk lebih jelasnya, misalnya, mengidentifikasi nol vs kosong, lihat jawaban johnvey di bawah ini dan / atau 'biola' XSLT yang telah saya adaptasi dari jawaban itu, yang mencakup opsi dalam komentar Michael Kay serta kemungkinan interpretasi keenam.

kapal uap25
sumber
14
Semantik terperinci dari tes ini adalah: return true jika setidaknya ada satu elemen categoryName yang nilainya string adalah string kosong.
jelovirt
14
@ jelovirt maksud Anda untuk mengatakan jika ada setidaknya satu kategoriNAMA yang BUKAN string kosong? (Saya seorang pemula xsl, jadi maafkan semua kebodohan potensial untuk pertanyaan saya.)
joedevon
10
Jawaban ini, walaupun diterima dan sangat dipilih, juga sangat menyesatkan. Itu benar-benar tergantung apa yang Anda maksud dengan "null atau kosong". Jika Anda ingin tes yang berhasil jika CategoryName tidak ada, atau hadir dengan nilai nol panjang, Anda harus menggunakan test="not(categoryName = '')". Jawaban yang diberikan akan kembali salah jika elemen categoryName tidak ada, yang menurut saya interpretasi pertanyaan membuatnya menjadi jawaban yang salah.
Michael Kay
2
@MichaelKay: Saya sudah memperbarui jawabannya untuk memberikan rincian lebih lanjut. Terima kasih atas komentar dan untuk prosesor Saxon XSLT!
steamer25
Bagaimana saya bisa menerjemahkan <xsl:for-each select="root/*[matches(name(.), 'grp')]">sehingga bisa digunakan di VS2010?
Si8
276

Tidak ada informasi lain, saya akan menganggap XML berikut:

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

Contoh kasus penggunaan akan terlihat seperti:

<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
sumber
Bagaimana Anda menguji contoh </CategoryName>? , tes string kosong tidak berfungsi untuk ini
raffian
3
Kami menghargai bahwa Anda menyertakan banyak contoh untuk menunjukkan bagaimana setiap ekspresi dihasilkan.
doubleJ
1
@raffian: di XSLT, atau teknologi terkait (XQuery, DOM, XDM, Schema dll), tag-akhir tidak dianggap sebagai entitas yang terpisah. Sebaliknya, Anda hanya berurusan dengan node, atau elemen dalam hal ini, yang merupakan keseluruhan antara tag-awal dan tag-akhir. Singkatnya, tidak ada cara untuk menguji </CategoryName>, juga tidak perlu.
Abel
4
Saya membintangi pertanyaan khusus untuk jawaban ini, dan meskipun pertanyaannya sudah cukup lama, jawaban ini tampaknya jauh lebih layak untuk menjadi jawaban yang dipilih
Patrick
68

Dari Elemen Kosong :

Untuk menguji apakah nilai node tertentu kosong

Itu tergantung pada apa yang Anda maksud dengan kosong.

  • Tidak mengandung node anak: not(node())
  • Tidak mengandung konten teks: not(string(.))
  • Tidak mengandung teks selain spasi putih: not(normalize-space(.))
  • Tidak mengandung apa pun kecuali komentar: not(node()[not(self::comment())])
Chris Doggett
sumber
2
+1. Beberapa catatan. Bulletpoint pertama juga menguji konten teks, yang juga merupakan simpul. Bulletpoint kedua menguji setiap simpul teks pada kedalaman apa pun, jika Anda ingin tahu apakah simpul saat ini tidak mengandung teks, tetapi dapat mengandung simpul lain, Anda dapat menggunakan not(text()). Alternatif untuk peluru ke-2 Anda juga not(.//text()). Seperti yang ditunjukkan oleh peluru terakhir Anda: ada banyak cara untuk mempertimbangkan "ketiadaan";).
Abel
Sangat praktis: Untuk menguji apakah string tidak kosong, Anda bisa menguji string itu sendiri! if ($mystring) then ... else ...
Felix Dombek
22

Bagaimana dengan?

test="not(normalize-space(categoryName)='')"
helcim
sumber
1
Ini sangat bagus. Bahkan ketika ada komentar di dalam <categoryName> <!-- some comment --> </categoryName>dan sebaliknya tidak ada teks yang bermakna, ini masih dievaluasi untuktrue
rustyx
9

Dua kesepakatan pertama dengan nilai nol dan dua kesepakatan kedua dengan string kosong.

<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
sumber
1
Mengerikan. Bagaimana jika ada banyak pengguna atau banyak nama depan? Gunakan xsl:apply-templatesdan sesuaikan template untuk mendapatkan yang Anda inginkan, jauh lebih mudah.
Abel
7

Dalam beberapa kasus, Anda mungkin ingin tahu kapan nilainya secara khusus nol, yang sangat diperlukan saat menggunakan XML yang telah diserialisasi dari objek .NET. Sementara jawaban yang diterima berfungsi untuk ini, itu juga mengembalikan hasil yang sama ketika string kosong atau kosong, yaitu '', sehingga Anda tidak dapat membedakan.

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

Jadi, Anda cukup menguji atributnya.

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

Kadang-kadang perlu untuk mengetahui keadaan sebenarnya dan Anda tidak bisa begitu saja memeriksa apakah CategoryName adalah instantiated, karena tidak seperti mengatakan Javascript

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

Akan mengembalikan true untuk elemen nol.

DustJones
sumber
6

Saya tahu pertanyaan ini sudah tua, tetapi di antara semua jawaban, saya kehilangan satu yang merupakan pendekatan umum untuk kasus penggunaan ini dalam pengembangan XSLT.

Saya membayangkan bahwa kode yang hilang dari OP terlihat seperti ini:

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

Dan inputnya terlihat seperti ini:

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

Yaitu, saya berasumsi mungkin ada nol, kosong, categoryNameelemen tunggal atau ganda . Untuk menangani semua kasus ini menggunakan xsl:choosekonstruksi gaya, atau dengan kata lain, secara imperatif, dengan cepat menjadi berantakan (terlebih lagi jika elemen dapat berada pada level yang berbeda!). Sebuah idiom pemrograman tipikal dalam XSLT adalah menggunakan templat (maka T dalam XSLT), yang merupakan pemrograman deklaratif, bukan keharusan (Anda tidak memberi tahu prosesor apa yang harus dilakukan, Anda hanya memberi tahu apa yang Anda inginkan keluaran jika kondisi tertentu terpenuhi). Untuk kasus penggunaan ini, itu bisa terlihat seperti berikut:

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

Ini berfungsi (dengan versi XSLT apa pun), karena yang pertama di atas memiliki prioritas lebih tinggi (memiliki predikat). Templat pencocokan "fall-through", yang kedua, menangkap apa pun yang tidak valid. Yang ketiga kemudian menangani menghasilkan categoryNamenilai dengan cara yang tepat.

Perhatikan bahwa dalam skenario ini tidak perlu mencocokkan secara spesifik categoriesatau category, karena prosesor akan secara otomatis memproses semua anak, kecuali kami memberi tahu sebaliknya (dalam contoh ini, templat kedua dan ketiga tidak memproses lebih lanjut anak-anak, karena tidak ada xsl:apply-templatesdalam mereka).

Pendekatan ini lebih mudah diperluas daripada pendekatan imperatif, karena pendekatan ini secara otomatis menangani beberapa kategori dan dapat diperluas untuk elemen atau pengecualian lainnya dengan hanya menambahkan templat pencocokan lain. Pemrograman tanpa if-branch .

Catatan: tidak ada yang namanya nullXML. Ada xsi: nihil , tapi itu jarang digunakan, terutama jarang dalam skenario yang tidak diketik tanpa skema.

Abel
sumber
1
Selamat atas penyebutan " Pemrograman tanpa if-branch ". Ada beberapa orang yang gagal memahami pentingnya hal ini. Bagi mereka semua di sini ada tautan ke kursus Pluralsight yang sangat bagus tentang topik ini: " Pola Desain Taktis di .NET: Alur Kontrol " oleh Zoran Horvat: app.pluralsight.com/library/courses/... A must-read!
Dimitre Novatchev
5

Bagaimana saya bisa memeriksa apakah suatu nilai nol atau kosong dengan XSL?

Misalnya, apakah categoryNamekosong?

Ini mungkin ekspresi XPath yang paling sederhana (yang ada di jawaban yang diterima memberikan tes untuk kebalikannya, dan akan lebih lama, jika dinegasikan):

not(string(categoryName))

Penjelasan :

Argumen untuk not()fungsi di atas adalah false()tepat ketika tidak ada categoryNameanak ("null") dari item konteks, atau anak (tunggal tersebut) categoryNamememiliki nilai string - string kosong.

Saya menggunakan ketika memilih konstruksi.

Sebagai contoh:

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

Dalam penggunaan XSLT 2.0 :

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

Ini adalah contoh lengkapnya :

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

Ketika transformasi ini diterapkan pada dokumen XML berikut:

<categoryName>X</categoryName>

hasil yang diinginkan dan benar dihasilkan :

X

Ketika diterapkan pada dokumen XML ini :

<categoryName></categoryName>

atau ini:

<categoryName/>

atau ini

<somethingElse>Y</somethingElse>

hasil yang benar dihasilkan :

Other

Demikian pula, gunakan transformasi XSLT 1.0 ini :

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

Perhatikan : Tidak ada persyaratan yang digunakan sama sekali. Pelajari lebih lanjut tentang pentingnya menghindari konstruksi kondisional dalam kursus Pluralsight yang bagus ini:

" Pola Desain Taktis di .NET: Alur Kontrol "

Dimitre Novatchev
sumber
Hai Dimitre, saya perlu solusi Anda untuk 1.0, jadi apakah saya perlu kode di setiap tag yang saya miliki atau apakah ada cara yang lebih sederhana untuk menerapkannya untuk seluruh XML?
zyberjock
@ Erberjock, Tidak jelas apa yang Anda minta. Tolong, kirim pertanyaan dan kirimkan saya komentar dengan tautan. Ikuti panduan bagaimana mengajukan pertanyaan yang bagus.
Dimitre Novatchev
Hai @ Dimitre, saya mengirim pertanyaan di sini stackoverflow.com/questions/38150093/...
zyberjock
4

Jika ada kemungkinan elemen tidak ada dalam XML, saya akan menguji apakah elemen tersebut ada dan panjang senarnya lebih besar dari nol:

<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
sumber
3
Nilai string dari set simpul kosong (yang diberikan oleh ekspresi XPath categoryNameketika tidak ada categoryNameelemen turunan dalam konteks saat ini) didefinisikan sebagai string kosong, jadi ini berlebihan - string-length(categoryName)adalah nol jika tidak ada categoryNameelemen.
Ian Roberts
3

Jika sebuah simpul tidak memiliki nilai yang tersedia di input xml seperti di bawah xpath,

<node>
    <ErrorCode/>
</node>

fungsi string () dikonversi menjadi nilai kosong. Jadi ini berfungsi dengan baik:

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

Sesuatu seperti ini bekerja untuk saya:

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

Atau sebaliknya:

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

Catatan: Jika Anda tidak memeriksa nilai nol atau menangani nilai nol, IE7 mengembalikan -2147483648 alih-alih NaN.

HSol
sumber
1

Saya benar-benar menemukan itu lebih baik hanya menguji panjang string karena berkali-kali bidang ini tidak nol, hanya kosong

<xsl: when test = "string-length (bidang-Anda-ingin-menguji) <1">

Pedro Pereira
sumber
0

Menurut pengalaman saya, cara terbaik adalah:

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

Gunakan categoryName / teks sederhana () Tes tersebut berfungsi dengan baik <categoryName/>dan juga <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
sumber