Перевірте, чи рядок є нульовим чи порожнім у XSLT


325

Як я можу перевірити, чи значення XSL є нульовим чи порожнім ?

Наприклад, якщо categoryNameпорожній? Я використовую при виборі конструкції.

Наприклад:

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

Чи можете ви розширити приклад коду?
Нік Аллен

Залежно від вашого випадку використання, ви, ймовірно, не хочете використовувати xsl:whenдля тестування вузлів. Розглянемо <xsl:template match="Category[categoryName[not(node())]]">...разом із a <xsl:template match="Category">.... Потім процесор прийме правильні рішення для вас, і вам більше не потрібно виписувати бізнес-логіку вкладеними xsl:choose. У багатьох випадках використання відповідних шаблонів полегшує написання таблиць стилів.
Авель

Відповіді:


322
test="categoryName != ''"

Редагувати : Це стосується найбільш вірогідної інтерпретації "[not] null or empty", як випливає з запитання, включаючи псевдокод та власний ранній досвід роботи з XSLT. Тобто, "Який еквівалент наступної Java?":

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

Детальніше, наприклад, чітко визначивши нуль проти порожнього, дивіться відповідь Джонві нижче та / або "скрипку" XSLT яку я адаптував із цього відповіді, що включає можливість коментаря Майкла Кей, а також шосту можливу інтерпретацію.


14
Детальна семантика цього тесту: повертайте true, якщо є принаймні один елемент категоріїName, значення рядка якого є порожнім рядком.
jelovirt

14
@jelovirt Ви хотіли сказати, чи є принаймні одна категоріяName, що НЕ порожня рядок? (Я новачок xsl, тому пробачте будь-яку потенційну дурість на моє запитання.)
joedevon

10
Ця відповідь, хоч і прийнята і високо голосується, також є дуже оманливою. Це дійсно залежить від того, що ви маєте на увазі під "нулем або порожнім". Якщо ви хочете, щоб тест був успішним, якщо категоріяName відсутня або має значення нульової довжини, вам слід скористатися test="not(categoryName = '')". Надана відповідь поверне помилковим, якщо елемент категоріїName відсутній, що в моєму трактуванні питання робить його неправильною відповіддю.
Майкл Кей

2
@MichaelKay: я оновив відповідь, щоб надати більше деталей. Дякуємо за коментар та процесору Saxon XSLT!
пароплав25

Як я можу перекласти, <xsl:for-each select="root/*[matches(name(.), 'grp')]">щоб його можна було використовувати у VS2010?
Si8

276

Відсутня будь-яка інша інформація, я припускаю наступний 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>

Приклад використання зразка виглядатиме так:

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

Як ви протестуєте на випадки </CategoryName>? , тести порожніх рядків для цього не працюють
raffian

3
Цінується, що ви включили кілька прикладів, щоб показати, як кожен вираз отримує результат.
подвійнийJ

1
@raffian: у XSLT або споріднених технологіях (XQuery, DOM, XDM, схема тощо) кінцеві теги не розглядаються як окремі об'єкти. Натомість ви маєте справу лише з вузлами або елементами в цьому випадку, що є цілим між стартовим і кінцевим тегом. Коротше кажучи, немає ніякого способу тестування </CategoryName>, а також не потрібно.
Авель

4
Я поставив головну роль на запитання спеціально для цієї відповіді, і хоча це питання досить давнє, цей здається набагато заслуговуючим на те, що це обрана відповідь
Патрік

68

Від Порожнього елемента :

Щоб перевірити, чи значення певного вузла порожнє

Це залежить від того, що ви маєте на увазі під порожнім.

  • Не містить дочірніх вузлів: not(node())
  • Не містить текстового вмісту: not(string(.))
  • Не містить тексту, окрім пробілу: not(normalize-space(.))
  • Не містить нічого, крім коментарів: not(node()[not(self::comment())])

2
+1. Деякі примітки. Перша куля також перевіряє текст-контент, який також є вузлом. Другий тест пункту для будь-якого текстового вузла на будь-якій глибині, якщо ви хочете знати, чи поточний вузол не містить тексту, але може містити інші вузли, ви можете використовувати not(text()). Альтернативою вашій 2 кулі також є not(.//text()). Як показує ваша остання куля: існує багато способів вважати "небуттям";).
Авель

Дуже практично: Щоб перевірити, чи рядок не порожній, ви можете просто протестувати саму рядок! if ($mystring) then ... else ...
Фелікс Домбек

22

А як на рахунок?

test="not(normalize-space(categoryName)='')"

1
Це чудово працює. Навіть коли є коментар всередині, <categoryName> <!-- some comment --> </categoryName>а в іншому випадку немає змістовного тексту, це все одно оцінюєтьсяtrue
rustyx

9

Перші два мають значення з нульовим значенням і другі два мають порожній рядок.

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

1
Страшно. Що робити, якщо є кілька користувачів або кілька імен? Використовуйте xsl:apply-templatesі сумісні шаблони, щоб отримати те, що ви хочете, набагато простіше.
Авель

7

У деяких випадках, можливо, ви хочете дізнатися, коли значення конкретно є нульовим, що особливо необхідно при використанні XML, серіалізованого з .NET-об'єктів. Хоча прийнята відповідь для цього працює, вона також повертає той самий результат, коли рядок порожній або порожній, тобто "", тому ви не можете розмежувати.

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

Таким чином, ви можете просто протестувати атрибут.

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

Іноді потрібно знати точний стан, і ви не можете просто перевірити, чи CategoryName є екземпляром, тому що на відміну від Javascript

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

Поверне значення true для нульового елемента.


6

Я знаю, що це питання давнє, але між усіма відповідями я пропускаю один, який є загальним підходом до цього випадку використання в розробці XSLT.

Я уявляю, що пропущений код з ОП виглядає приблизно так:

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

І що вхід виглядає приблизно так:

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

Тобто, я припускаю, що може бути нульовий, порожній, одинарний або кілька categoryNameелементів. Впоратися з усіма цими випадками за допомогою xsl:chooseконструкцій -style, або іншими словами, обов'язково, швидко стає безладним (тим більше, якщо елементи можуть бути на різних рівнях!). Типова ідіома програмування в XSLT - це використання шаблонів (отже, T у XSLT), що є декларативним програмуванням, а не обов'язковим (ви не говорите процесору, що робити, ви просто повідомте, що ви хочете вивести, якщо будуть виконані певні умови). У цьому випадку використання може виглядати приблизно так:

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

Це працює (з будь-якою версією XSLT), оскільки перша вище має перевагу (має присудок). Шаблон відповідності "пропускний", другий, вловлює все, що недійсне. Потім третій піклується про вихідcategoryName значення належним чином.

Зауважте, що в цьому сценарії немає необхідності в точній відповідності categoriesабо category, оскільки процесор автоматично обробляє всіх дітей, якщо ми не скажемо це інакше (у цьому прикладі другий і третій шаблони не обробляють дітей далі, оскільки немаєxsl:apply-templates в їх).

Цей підхід легше розширити, ніж імперативний підхід, оскільки він автоматично обробляє декілька категорій і його можна розширити для інших елементів або винятків, просто додавши ще один узгоджений шаблон. Програмування без if-філій .

Примітка. Немає такого поняття, як nullу XML. Є xsi: nil , але це рідко використовується, особливо рідко в нетипових сценаріях без якоїсь схеми.


1
Подяки з згадуванням " Програмування без if-філій ". Є люди, які не розуміють важливості цього. Для них тут є посилання на дуже хороший Pluralsight курсу по цій темі: « Візерунки тактичного дизайну в .NET: Flow Control » Зоран Хорват: app.pluralsight.com/library/courses / ... Обов'язкового читати!
Димитрій Новатчев

5

Як я можу перевірити, чи значення XSL є нульовим чи порожнім?

Наприклад, якщо categoryNameпорожній?

Це, мабуть, найпростіший вираз XPath (те, що у прийнятій відповіді дає тест на протилежне, і було б довше, якщо було б заперечено):

not(string(categoryName))

Пояснення :

Аргумент not()вищевказаної функції полягає false()саме в тому випадку, коли categoryNameв контексті елемента немає дочірнього ("null") або categoryNameдочірнє (одиночне таке) має значення рядка - порожній рядок.

Я використовую при виборі конструкції.

Наприклад:

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

У XSLT 2.0 використовуйте :

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

Ось повний приклад :

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

Коли це перетворення застосовується до наступного XML-документа:

<categoryName>X</categoryName>

шукається правильний результат :

X

Якщо застосовується до цього XML-документа :

<categoryName></categoryName>

або на цьому:

<categoryName/>

або на цьому

<somethingElse>Y</somethingElse>

правильний результат виробляється :

Other

Аналогічно використовуйте це перетворення 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>

Зверніть увагу : умовні умови взагалі не використовуються. Дізнайтеся більше про важливість уникнення умовних конструкцій у цьому приємному курсі Pluralsight:

" Шаблони тактичного дизайну в .NET: Control Flow "


Привіт Димитре, мені потрібно рішення для 1.0, тому мені потрібно кодувати його у всіх тегах, які у мене є, або є більш простий спосіб перешкодити цьому для всього XML?
zyberjock

@zyberjock, Незрозуміло, про що ви питаєте. Будь ласка, опублікуйте питання та надішліть мені коментар за посиланням. Дотримуйтесь вказівок, як поставити гарне запитання.
Димитрій Новатчев

Привіт @Dimitre, я розмістив питання тут stackoverflow.com/questions/38150093 / ...
zyberjock

4

Якщо є можливість, що елемента не існує в XML, я би перевірив і наявність елемента, і довжина рядка більше нуля:

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

3
Значення рядка порожнього набору вузлів (саме це categoryNameдає вираз XPath, коли categoryNameв поточному контексті немає дочірніх елементів) визначається як порожня рядок, тому це зайве - string-length(categoryName)дорівнює нулю, якщо categoryNameелементів немає .
Ян Робертс

3

Якщо вузол не має значення у вхідному xml, як нижче xpath,

<node>
    <ErrorCode/>
</node>

Функція string () перетворюється в порожнє значення. Отже, це добре працює:

string(/Node/ErrorCode) =''

2

Щось подібне працює для мене:

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

Або навпаки:

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

Примітка: Якщо ви не перевіряєте нульові значення або не обробляєте нульові значення, IE7 повертає -2147483648 замість NaN.


1

Я фактично вважаю, що краще просто перевірити довжину рядка, оскільки багато разів поле не є нульовим, просто порожнім

<xsl: when test = "string-length (поле, яке ви хочете перевірити) <1">


0

На мій досвід найкращий спосіб:

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

0

Використовуйте просту категоріюName / text () Такий тест добре працює <categoryName/>і також <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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.