Scala XML:测试节点的存在和价值

时间:2012-09-06 20:46:05

标签: xml scala

我正在从外部数据存储中解析一系列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。截止日期,愚弄我们所有人。

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

由于

3 个答案:

答案 0 :(得分:3)

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

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

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

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

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

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

但这一切都没有解决你的"illegal start of simple expression"问题 - 它只是修复逻辑并将16行代码减少到3行。这里的问题是,如果您刚刚完成的测试失败,您需要决定name应该是什么。最明显的方法是使用Option

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

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

答案 1 :(得分:1)

这里的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)

只需移除发电机前面的val(Pattern1'&lt; - 'Expr [Guard]),即可恢复正常服务。如果没有我找到的for循环中的val,它也会更好地流动。

答案 2 :(得分:0)

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

老实说,我猜不出你想要达到什么目标,所以我不能建议一个语法正确的替代方案。但是如果你有更多依赖于cond的逻辑,你可以把它放在一个块中:

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