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

时间:2022-01-19 01:53:28

I am parsing a series of XML responses from a external data store. During which I must test for the existence of a child node and - if it exists - test its value. To achieve that I have the following code:

我正在从外部数据存储解析一系列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 is defined as such:

checkDetectionNode定义如下:

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

The above code results in an unspecified error of "illegal start of simple expression" on the val name... line. To be honest I am not a Scala programmer or even a functional programmer (always was more partial to OO/imperative). I've only been using Scala for a few days and been basing most of what I know from Java and lambda operators. Unfortunately, I don't really have the time to sit down and really learn Scala like I wish I could. Deadlines, make fools of us all.

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

I am hoping that someone could take a look and let me know if there is something I am doing wrong (as I am sure there is). I tried to limit the code shown to, what I hope, is relevant to the question. However, please let me know if any additional code is needed.

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

Thanks

谢谢

3 个解决方案

#1


1  

The xml is a distraction here. The problem is the if (cond) at the end is not acting as a guard, it just looks like it should, the compiler thinks is the start of a new if 'then' part.

xml在这里分散注意力。问题是最后的if(cond)不是作为一个保护,它看起来应该是这样,编译器认为是一个新的if'then'部分的开始。

In the following example:

在以下示例中:

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

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

you'll get List(6,8,10) as you might expect.

如你所料,你会获得List(6,8,10)。

Using

运用

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

should get you a deprecation warning, and

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

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

gets

得到

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

Simply remove the val in front of your Generator (Pattern1 ‘<-’ Expr [Guard]), and you can resume normal service. It also flows a bit more nicely without the vals in the for loop I find.

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

#2


3  

First of all, note that (row \ "ResultDetectionConditionText") won't be null if no children with that name exist—it will just be an empty NodeSeq (idiomatic Scala code tends to avoid returning null, as you've probably noticed). So your current code will always return a Some, which probably isn't what you want. Changing your != null to .nonEmpty will fix that problem.

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

Next, here's a more concise way of writing your conditional logic:

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

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

This says: get a NodeSeq that contains all the children named "Result...", if they exist, and then check that NodeSeq for a node that contains the text "non-detect". The logic's not exactly the same as yours, since we check the text of the nodes individually, but it actually fits what I'd guess is your intent even better—presumably you don't want something like this to pass the test:

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

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

But it will in your current version.

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

But none of this solves your "illegal start of simple expression" issue—it just fixes the logic and cuts sixteen lines of code down to three. The problem here is that you need to decide what your name should be if the test you've just done fails. The most obvious approach is to use an Option:

但这一切都没有解决你的“非法启动简单表达”问题 - 它只是修复逻辑并将16行代码减少到3行。这里的问题是,如果你刚刚完成的测试失败,你需要决定你的名字应该是什么。最明显的方法是使用选项:

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

But depending on how you're using name, some other approach may be more appropriate.

但取决于你如何使用名称,其他一些方法可能更合适。

#3


0  

if (cond) should be followed by an expression. In Scala, if is more like the ternary operator in Java: it is an expression, not a statement. A variable declaration is not an expression (as in Java), so you can't have a val in the then part of an if.

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

Honestly, I can't guess what you want to achieve there, so I can't suggest a syntactically correct alternative that makes sense. But if you have more logic that depends on cond, you could put it in a block:

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

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

#1


1  

The xml is a distraction here. The problem is the if (cond) at the end is not acting as a guard, it just looks like it should, the compiler thinks is the start of a new if 'then' part.

xml在这里分散注意力。问题是最后的if(cond)不是作为一个保护,它看起来应该是这样,编译器认为是一个新的if'then'部分的开始。

In the following example:

在以下示例中:

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

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

you'll get List(6,8,10) as you might expect.

如你所料,你会获得List(6,8,10)。

Using

运用

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

should get you a deprecation warning, and

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

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

gets

得到

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

Simply remove the val in front of your Generator (Pattern1 ‘<-’ Expr [Guard]), and you can resume normal service. It also flows a bit more nicely without the vals in the for loop I find.

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

#2


3  

First of all, note that (row \ "ResultDetectionConditionText") won't be null if no children with that name exist—it will just be an empty NodeSeq (idiomatic Scala code tends to avoid returning null, as you've probably noticed). So your current code will always return a Some, which probably isn't what you want. Changing your != null to .nonEmpty will fix that problem.

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

Next, here's a more concise way of writing your conditional logic:

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

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

This says: get a NodeSeq that contains all the children named "Result...", if they exist, and then check that NodeSeq for a node that contains the text "non-detect". The logic's not exactly the same as yours, since we check the text of the nodes individually, but it actually fits what I'd guess is your intent even better—presumably you don't want something like this to pass the test:

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

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

But it will in your current version.

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

But none of this solves your "illegal start of simple expression" issue—it just fixes the logic and cuts sixteen lines of code down to three. The problem here is that you need to decide what your name should be if the test you've just done fails. The most obvious approach is to use an Option:

但这一切都没有解决你的“非法启动简单表达”问题 - 它只是修复逻辑并将16行代码减少到3行。这里的问题是,如果你刚刚完成的测试失败,你需要决定你的名字应该是什么。最明显的方法是使用选项:

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

But depending on how you're using name, some other approach may be more appropriate.

但取决于你如何使用名称,其他一些方法可能更合适。

#3


0  

if (cond) should be followed by an expression. In Scala, if is more like the ternary operator in Java: it is an expression, not a statement. A variable declaration is not an expression (as in Java), so you can't have a val in the then part of an if.

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

Honestly, I can't guess what you want to achieve there, so I can't suggest a syntactically correct alternative that makes sense. But if you have more logic that depends on cond, you could put it in a block:

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

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