【问题标题】:Scala XML: test for node existence and valueScala XML:测试节点存在和值
【发布时间】:2012-09-06 20:46:05
【问题描述】:

我正在解析来自外部数据存储的一系列 XML 响应。在此期间,我必须测试子节点是否存在,如果存在,则测试它的值。为了实现这一点,我有以下代码:

...
  val properties = for {
    val row <- root \\ "ResultDescription"
    val cond:Boolean = checkDetectionNode(row) match {
      case Some(nodeseq) => {
          val txt = nodeseq.text.toLowerCase
          if (txt contains "non-detect")
            false
          else
            true
      }
      case None => true
    }
    if (cond)
    val name = (row \ "CharacteristicName").text
    if (charNameList.exists(s => s == name) == false)
  } yield {
    getObservedProperty(name) match {
      case Some(property) => {
          charNameList = name :: charNameList
          property
      }
    }
  }
...

checkDetectionNode 是这样定义的:

private def checkDetectionNode(row: scala.xml.NodeSeq) : Option[scala.xml.NodeSeq] = {
  if ((row \ "ResultDetectionConditionText") != null)
    Some[scala.xml.NodeSeq]((row \ "ResultDetectionConditionText"))
  else
    None
}

以上代码导致val name... 行出现“非法开始简单表达式”的未指定错误。老实说,我不是 Scala 程序员,甚至不是函数式程序员(总是更偏向于 OO/命令式)。我只使用 Scala 几天,并且基于我从 Java 和 lambda 运算符中了解的大部分内容。不幸的是,我真的没有时间像我希望的那样坐下来真正学习 Scala。截止日期,愚弄我们所有人。

我希望有人可以看看并让我知道我是否做错了什么(我确信有)。我试图将显示的代码限制为我希望与问题相关的代码。但是,如果需要任何其他代码,请告诉我。

谢谢

【问题讨论】:

  • 我想我应该明确一点,以上内容是对陈述的理解的一部分。 if(cond) 被认为是用于确定 yield 块的执行。我已经编辑了我的答案以详细说明代码。

标签: xml scala


【解决方案1】:

首先,请注意 (row \ "ResultDetectionConditionText") 不会是 null 如果不存在具有该名称的子项,它将只是一个空的 NodeSeq (惯用的 Scala 代码倾向于避免返回 null,因为您'可能已经注意到)。因此,您当前的代码将始终返回 Some,这可能不是您想要的。将您的 != null 更改为 .nonEmpty 将解决该问题。

接下来,这是编写条件逻辑的更简洁的方式:

val cond = (row \ "ResultDetectionConditionText").exists(
  _.text.toLowerCase contains "non-detect"
)

这表示:获取一个包含所有名为 "Result..." 的子节点(如果存在)的 NodeSeq,然后检查 NodeSeq 是否有包含文本 "non-detect" 的节点。逻辑与您的不完全相同,因为我们单独检查节点的文本,但它实际上更符合我的猜测是您的意图 - 大概您不希望这样的东西通过测试:

val row = <row>
  <ResultDetectionConditionText>non-d</ResultDetectionConditionText>
  <ResultDetectionConditionText>etect</ResultDetectionConditionText>
</row>

但它会在您当前的版本中。

但是这些都不能解决您的"illegal start of simple expression" 问题——它只是修复了逻辑并将十六行代码减少到三行。这里的问题是,如果您刚刚完成的测试失败,您需要决定您的name 应该是什么。最明显的方法是使用Option:

val name = if (cond) Some((row \ "CharacteristicName").text) else None

但根据您使用name 的方式,其他一些方法可能更合适。

【讨论】:

  • 感谢您的回复并指出这些问题,我会将这些更改应用到代码中。我已经编辑了我的问题,指出这一切都是全面的——我认为(可能是错误的)改变了 if 语句的三元性质。
【解决方案2】:

这里的 xml 让人分心。问题是最后的 if (cond) 没有起到保护作用,它看起来应该是,编译器认为是新的 if 'then' 部分的开始。

在以下示例中:

val l = List(1,2,3,4,5)

val r = for {
    i <- l 
      if (i > 2)
    x <- Some(i * 2)
  } yield x

你会得到你所期望的 List(6,8,10)。

使用

val r = for {
    val i <- l if (i > 2)
    val x <- Some(i * 2)
  } yield x

应该给你一个弃用警告,并且

val r = for {
    val i <- l 
    if (i > 2)
    val x <- Some(i * 2)
  } yield x

得到

error: illegal start of simple expression
  val x <- Some(i * 2)

只需删除 Generator 前面的 val(Pattern1 ‘

【讨论】:

  • 这是与问题直接相关的最佳答案。不幸的是,这让我重新思考了这个问题并进行了重大的重写。
【解决方案3】:

if (cond) 后面应该跟一个表达式。在 Scala 中,if 更像是 Java 中的三元运算符:它是一个表达式,而不是一个语句。变量声明不是表达式(如在 Java 中),因此您不能在 if 的 then 部分中包含 val。

老实说,我猜不出你想在那里实现什么,所以我不能提出一个语法正确且有意义的替代方案。但是如果你有更多的逻辑依赖于cond,你可以把它放在一个块中:

if (cond) {
  val name = ...
  // do more here
}

【讨论】:

  • 感谢您的简洁回复。我忘记了 if 语句可以充当条件运算符。但是,在这种情况下,我将 if 语句用作综合 for 操作的一部分,我相信(我很可能会弄错)将它从三元运算符更改为 yield 语句的条件要求。我已经修改了上面的问题以包含更多代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
相关资源
最近更新 更多