Проверьте, является ли строка нулевой или пустой в XSLT - PullRequest
307 голосов
/ 05 мая 2009

Как я могу проверить, является ли значение нулевым или пустым с помощью 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>

Ответы [ 14 ]

309 голосов
/ 05 мая 2009
test="categoryName != ''"

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

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

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

260 голосов
/ 05 мая 2009

В отсутствие какой-либо другой информации, я приму следующий 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>
63 голосов
/ 05 мая 2009

С Пустой элемент :

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

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

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

А как же?

test="not(normalize-space(categoryName)='')"
9 голосов
/ 10 августа 2012

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

<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>
5 голосов
/ 12 августа 2011

В некоторых случаях вам может потребоваться узнать, когда значение является конкретно нулевым, что особенно необходимо при использовании 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 для нулевого элемента.

4 голосов
/ 21 июля 2014

Я знаю, что этот вопрос старый, но между всеми ответами мне не хватает одного, который является общим подходом для этого варианта использования при разработке 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, или, другими словами, обязательно, становится быстро грязно (тем более, если элементы могут быть на разных уровнях!). Типичная идиома программирования в 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-ветвей .

Примечание: в XML нет такой вещи как null. Существует xsi: nil , но он используется редко, особенно редко в нетипизированных сценариях без какой-либо схемы.

4 голосов
/ 14 июня 2013

Если существует вероятность того, что элемент не существует в 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 голосов
/ 01 января 2016

Как я могу проверить, является ли значение нулевым или пустым в 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>

Do note : никакие условия не используются вообще. Узнайте больше о важности избегания условных конструкций в этом хорошем курсе Pluralsight:

" Тактические шаблоны проектирования в .NET: поток управления "

3 голосов
/ 23 апреля 2015

Если узел не имеет значения, доступного во входном xml, как показано ниже xpath,

<node>
    <ErrorCode/>
</node>

Функция string () конвертируется в пустое значение. Так что это прекрасно работает:

string(/Node/ErrorCode) =''
...