【问题标题】:Is there an "if -then - else " statement in XPath?XPath 中是否有“if -then - else”语句?
【发布时间】:2009-06-09 16:13:17
【问题描述】:

在 xpath 中,您似乎可以使用所有丰富的函数来执行 "if" 。但是,我的引擎一直坚持“没有这样的功能”,我在网上几乎找不到任何文档(我发现了一些可疑的来源,但它们的语法不起作用)

我需要从字符串末尾删除 ':'(如果存在),所以我想这样做:

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

有什么建议吗?

【问题讨论】:

  • 这个问题有更多的背景吗? XPath 通常用于查询信息,而不是作为操作工具。操作通常留给 XSL 模板或更高级别的语言之类的东西
  • 我发现 w3schools 教程易于使用。你可以从那里开始。 w3schools.com/Xpath
  • @James:这实际上是一个查询。不要让 if-then-else 语法欺骗您——从技术上讲,它是一个“条件表达式”而不是“if 语句”——更类似于许多编程语言中的三元条件运算符。

标签: xpath if-statement


【解决方案1】:

是的,在 XPath 1.0 中有一种方法:

连接( substring($s1, 1, number($condition) * 字符串长度($s1)), substring($s2, 1, number(not($condition)) * 字符串长度($s2)) )

这依赖于两个互斥字符串的连接,如果条件为假 (0 * string-length(...)),第一个为空,如果条件为真,第二个为空。这被称为“贝克尔方法”,归属于Oliver Becker(原链接现已失效,the web archive has a copy

在你的情况下:

连接( 子串( substring-before(//div[@id='head']/text(), ':'), 1、 数字( 以(//div[@id='head']/text(), ':') 结尾 ) * string-length(substring-before(//div [@id='head']/text(), ':')) ), 子串( //div[@id='head']/text(), 1、 数(不是( 以(//div[@id='head']/text(), ':') 结尾 )) * string-length(//div[@id='head']/text()) ) )

虽然我之前会尝试摆脱所有"//"

另外,//div[@id='head'] 有可能返回多个节点。
请注意这一点 - 使用 //div[@id='head'][1] 更具防御性。

【讨论】:

  • 我可能不会使用这个(我只是升级 xpath 引擎),但这是一个很棒的技巧和一个很棒的技巧 :)
  • 使用数字表达式(您不需要选择任何字符串)它更简单,因为您可以执行 X * number(condition) + Y * number(condition) 等,其中 number(condition)给你 0 代表 false 和 1 代表 true (“布尔 True 被转换为 1;布尔 False 被转换为 0。”msdn.microsoft.com/en-us/library/aa926043.aspx
  • 可能在这些表达式中需要 +1,我必须在以下位置添加:
  • 贝克尔法。我正在寻找 c# 的 ? 运算符。
  • @Tomalak 链接已损坏,这是另一个讨论该方法的页面的链接:blog.alessio.marchetti.name/post/2011/02/12/…
【解决方案2】:

W3.org 上XPath 2.0 的官方语言规范详细说明了该语言确实支持 if 语句。请特别参阅Section 3.8 Conditional Expressions。连同语法格式和解释,它给出了以下示例:

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 的一部分。

【讨论】:

  • 我不认为它建议你不应该使用括号,只是你不需要需要使用它们。如果它抱怨“没有这样的功能”,那么我怀疑他没有使用 XPath 2。规范要求在条件周围加上括号。
  • @Rob:这很有可能,但我确实声明它可能不一定是解决办法。但是,如果您试图让某些东西正常工作,那么总是值得遵循这个例子。 :)
  • 但是,我不知何故错过了条件周围的括号。现已修复。
【解决方案3】:

根据 pkarat 的法律,您可以在 1.0 版本中实现条件 XPath。

对于您的情况,请遵循以下概念:

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

这肯定会奏效。看看它怎么运作。给两个输入

praba:
karan

对于第一个输入:它包含:,因此条件为真,: 之前的字符串将是输出,比如praba 是您的输出。第二个条件是假的,所以没有问题。

对于第二个输入:它不包含:,因此条件失败,进入第二个条件,字符串不包含:,因此条件为真......因此将抛出输出karan

最后你的输出是praba,karan

【讨论】:

    【解决方案4】:

    改用 fn:replace(string,pattern,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>
    

    【讨论】:

      【解决方案5】:

      就我个人而言,我会使用 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> 
      

      【讨论】:

        【解决方案6】:

        不幸的是,以前的答案对我来说是没有选择的,所以我研究了一段时间并找到了这个解决方案:

        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)
        

        【讨论】:

          【解决方案7】:

          稍微简单一点的 XPath 1.0 解决方案,改编自 Tomalek(在此处发布)和 Dimitre 的 (here):

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

          注意:我发现将 bool 转换为 int 需要显式 number() ,否则某些 XPath 评估器会引发类型不匹配错误。根据您的 XPath 处理器类型匹配的严格程度,您可能不需要它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-08-02
            相关资源
            最近更新 更多