Есть ли в XPath утверждение «если-потом - еще»? - PullRequest
38 голосов
/ 09 июня 2009

Кажется, что со всем богатым количеством функций в xpath, вы можете выполнить "если". Тем не менее, мой движок настаивает на том, что «такой функции нет», и я почти не нахожу документацию в Интернете (я нашел несколько сомнительных источников, но синтаксис у них не работал)

Мне нужно удалить ':' из конца строки (если существует), поэтому я хотел сделать это:

if (fn:ends-with(//div [@id='head']/text(),': '))
            then (fn:substring-before(//div [@id='head']/text(),': ') )
            else (//div [@id='head']/text())

Любой совет?

Ответы [ 7 ]

59 голосов
/ 09 июня 2009

Да, есть способ сделать это в XPath 1.0:

concat(
  substring($s1, 1, number($condition)      * string-length($s1)),
  substring($s2, 1, number(not($condition)) * string-length($s2))
)

Это зависит от объединения двух взаимоисключающих строк, первая из которых пуста, если условие ложно (0 * string-length(...)), вторая - пусто, если условие истинно. Это называется «Метод Беккера» , приписываемый Оливеру Беккеру .

В вашем случае:

concat(
  substring(
    substring-before(//div[@id='head']/text(), ': '),
    1, 
    number(
      ends-with(//div[@id='head']/text(), ': ')
    )
    * string-length(substring-before(//div [@id='head']/text(), ': '))
  ),
  substring(
    //div[@id='head']/text(), 
    1, 
    number(not(
      ends-with(//div[@id='head']/text(), ': ')
    ))
    * string-length(//div[@id='head']/text())
  )
)

Хотя я бы попытался избавиться от всех "//" раньше.

Также существует вероятность, что //div[@id='head'] вернет более одного узла.
Просто знайте об этом & mdash; использование //div[@id='head'][1] более оборонительно.

21 голосов
/ 09 июня 2009

В официальной спецификации языка для XPath 2.0 на W3.org указано, что язык действительно поддерживает оператор if . См. Раздел 3.8 Условные выражения , в частности. Наряду с форматом синтаксиса и пояснениями он приводит следующий пример:

if ($widget1/unit-cost < $widget2/unit-cost) 
  then $widget1
  else $widget2

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

if (fn:ends-with(//div [@id='head']/text(),': '))
  then fn:substring-before(//div [@id='head']/text(),': ')
  else //div [@id='head']/text()

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

Наконец, чтобы подчеркнуть очевидное, убедитесь, что ваш движок XPath действительно поддерживает XPath 2.0 (в отличие от более ранней версии)! Я не верю, что условные выражения являются частью предыдущих версий XPath.

3 голосов
/ 18 февраля 2011

Лично я бы использовал XSLT для преобразования XML и удаления конечных двоеточий. Например, предположим, у меня есть этот вход:

<?xml version="1.0" encoding="UTF-8"?>
<Document>
    <Paragraph>This paragraph ends in a period.</Paragraph>
    <Paragraph>This one ends in a colon:</Paragraph>
    <Paragraph>This one has a : in the middle.</Paragraph>
</Document>

Если бы я хотел удалить конечные двоеточия в своих абзацах, я бы использовал этот XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:fn="http://www.w3.org/2005/xpath-functions"
    version="2.0">
    <!-- identity -->
    <xsl:template match="/|@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <!-- strip out colons at the end of paragraphs -->
    <xsl:template match="Paragraph">
        <xsl:choose>
            <!-- if it ends with a : -->
            <xsl:when test="fn:ends-with(.,':')">
                <xsl:copy>
                    <!-- copy everything but the last character -->
                    <xsl:value-of select="substring(., 1, string-length(.)-1)"></xsl:value-of>
                </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:apply-templates/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet> 
3 голосов
/ 16 ноября 2010

Согласно закону Пкарата, вы можете достичь условного XPath в версии 1.0.

Для вашего случая следуйте концепции:

concat(substring-before(your-xpath[contains(.,':')],':'),your-xpath[not(contains(.,':'))])

Это точно сработает. Посмотри, как это работает. Дайте два ввода

praba:
karan

Для 1-го ввода: оно содержит :, поэтому условие true, строка перед : будет выводом, скажем, praba будет вашим выводом. 2-е условие будет ложным, поэтому никаких проблем.

Для 2-го ввода: оно не содержит :, поэтому условие не выполнено, при переходе ко 2-му условию строка не содержит :, поэтому условие истинно ... поэтому вывод karan будет выдан.

Наконец, ваш вывод будет praba, karan.

1 голос
/ 15 марта 2013

К сожалению, предыдущие ответы были для меня невозможны, поэтому я некоторое время искал и нашел это решение:

http://blog.alessio.marchetti.name/post/2011/02/12/the-Oliver-Becker-s-XPath-method

Я использую его для вывода текста, если существует определенный узел. 4 - длина текста foo. Поэтому я думаю, что более элегантным решением было бы использование переменной.

substring('foo',number(not(normalize-space(/elements/the/element/)))*4)
1 голос
/ 09 июня 2009

Как насчет использования fn: replace (строка, шаблон, replace) вместо этого?

XPATH очень часто используется в XSLT, и если вы находитесь в такой ситуации и не имеете XPATH 2.0, вы можете использовать:

  <xsl:choose>
    <xsl:when test="condition1">
      condition1-statements
    </xsl:when>
    <xsl:when test="condition2">
      condition2-statements
    </xsl:when>
    <xsl:otherwise>
      otherwise-statements
    </xsl:otherwise>
  </xsl:choose>
0 голосов
/ 06 октября 2017

Несколько более простое решение XPath 1.0, адаптированное из работ Томалека (размещено здесь) и Димитра ( здесь ):

concat(substring($s1, 1 div number($cond)), substring($s2, 1 div number(not($cond))))

Примечание. Я обнаружил, что для преобразования bool в int требуется явное число (), в противном случае некоторые оценщики XPath выдают ошибку несоответствия типов. В зависимости от того, насколько строго ваш XPath-процессор соответствует типу, он может вам и не понадобиться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...