Проверьте, является ли строка нулевой или пустой в 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>
raklos
источник
Можете ли вы расширить пример кода?
Ник Аллен
В зависимости от вашего варианта использования, вы, вероятно, не захотите использовать его xsl:whenдля тестов узлов. Посмотрим <xsl:template match="Category[categoryName[not(node())]]">...вместе с собой <xsl:template match="Category">.... Затем процессор примет правильные решения за вас, и вам больше не нужно будет выписывать бизнес-логику во вложенных xsl:chooseфайлах. Во многих случаях использование соответствующих шаблонов облегчает написание таблиц стилей.
Авель

Ответы:

322
test="categoryName != ''"

Редактировать : Это охватывает наиболее вероятную интерпретацию, по моему мнению, «[не] нулевого или пустого», как следует из вопроса, включая его псевдокод и мой собственный ранний опыт работы с XSLT. Т.е. «что эквивалентно следующей Java?»:

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

Для получения более подробной информации, например, для четкого определения « ноль» или «пусто», см . Ответ Джонни ниже и / или «скрипку» XSLT, которую я адаптировал из этого ответа, которая включает опцию в комментарии Майкла Кея, а также шестую возможную интерпретацию.

steamer25
источник
14
Подробная семантика этого теста: вернуть true, если есть хотя бы один элемент categoryName, строковое значение которого является пустой строкой.
Jelovirt
14
@jelovirt Вы хотели сказать, есть ли хотя бы одно categoryName, которое НЕ является пустой строкой? (Я новичок в xsl, так что прости меня за любую потенциальную глупость на мой вопрос.)
joedevon
10
Этот ответ, хотя и принят и высоко оценен, также вводит в заблуждение. Это действительно зависит от того, что вы подразумеваете под «нулевым или пустым». Если вам нужен тест, который завершится успешно, если categoryName либо отсутствует, либо присутствует со значением нулевой длины, вы должны использовать test="not(categoryName = '')". Предоставленный ответ вернет false, если элемент categoryName отсутствует, что в моей интерпретации вопроса делает его неправильным ответом.
Майкл Кей
2
@MichaelKay: я обновил ответ, чтобы предоставить больше деталей. Спасибо за комментарий и за процессор Saxon XSLT!
steamer25
Как я могу перевести, <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>
johnvey
источник
Как вы проверяете на случаи </CategoryName>? тесты на пустые строки не работают для этого
raffian
3
Приветствуется то, что вы включили несколько примеров, чтобы показать, как получается каждое выражение.
doubleJ
1
@raffian: в XSLT или связанных с ним технологиях (XQuery, DOM, XDM, Schema и т. д.) конечные теги не рассматриваются как отдельные объекты. Вместо этого вы имеете дело только с узлами или элементами в этом случае, которые являются целым между начальным тегом и конечным тегом. Короче говоря, нет никакого способа проверить </CategoryName>, и в этом нет никакой необходимости.
Авель
4
Я пометил вопрос специально для этого ответа, и, хотя вопрос довольно старый, этот кажется гораздо более заслуживающим того, чтобы быть выбранным ответом
Патрик
68

Из пустого элемента :

Чтобы проверить, является ли значение определенного узла пустым

Это зависит от того, что вы подразумеваете под пустым.

  • Не содержит дочерних узлов: not(node())
  • Не содержит текстового содержимого: not(string(.))
  • Не содержит текста, кроме пробелов: not(normalize-space(.))
  • Не содержит ничего, кроме комментариев: not(node()[not(self::comment())])
Крис Доггетт
источник
2
+1. Некоторые заметки. Первая точка обзора также проверяет текстовое содержимое, которое также является узлом. Вторая точка обзора проверяет любой текстовый узел на любой глубине, если вы хотите узнать, содержит ли текущий узел текст, но может ли он содержать другие узлы, вы можете использовать not(text()). Альтернатива вашей второй пули тоже not(.//text()). Как показывает ваша последняя пуля: есть много способов рассмотреть «ничто»;).
Авель
Очень практично: Для того, чтобы проверить , если строка не пустая, вы можете просто проверить саму строку! if ($mystring) then ... else ...
Феликс Домбек
22

Что о?

test="not(normalize-space(categoryName)='')"
helcim
источник
1
Это прекрасно работает. Даже если внутри есть комментарий, <categoryName> <!-- some comment --> </categoryName>а в остальном нет значимого текста, он все равно оценивается какtrue
rustyx
9

Первые два имеют дело со значением NULL, а вторые два имеют дело с пустой строкой.

<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 для нулевого элемента.

DustJones
источник
6

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

Я представляю, что отсутствующий код из OP выглядит примерно так:

<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: поток управления » Зорана Хорват: app.pluralsight.com/library/courses/… Обязательно читать!
Димитр Новатчев
5

Как я могу проверить, является ли значение пустым или пустым в XSL?

Например, если categoryNameпусто?

Вероятно, это самое простое выражение XPath (в принятом ответе содержится проверка на обратное, и оно будет длиннее, если его отменить):

not(string(categoryName))

Пояснение :

Аргументом not()функции выше является false()именно тот случай, когда отсутствует categoryNameдочерний элемент («ноль») элемента контекста или (единственный такой) 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: поток управления »

Димитр Новатчев
источник
Привет Димитр, мне нужно твое решение для 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.

HSol
источник
1

Я на самом деле нашел, что лучше просто проверить длину строки, так как много раз поле не пусто, просто пусто

<xsl: when test = "длина строки (поле, которое вы хотите проверить) <1">

Педро Перейра
источник
0

По моему опыту лучший способ это:

<xsl:when test="not(string(categoryName))">
    <xsl:value-of select="other" />
</xsl:when>
<otherwise>
    <xsl:value-of select="categoryName" />
</otherwise>
dr_leevsey
источник
0

Используйте простой categoryName / 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>
Ярослав Кубачек
источник