1

我正在解析 XML,并不断发现自己在编写如下代码:

val xml = <outertag>
<dog>val1</dog>
<cat>val2</cat>
</outertag>

var cat = ""
var dog = ""

for (inner <- xml \ "_") {
  inner match {
    case <dog>{ dg @ _* }</dog> => dog = dg(0).toString()
    case <cat>{ ct @ _* }</cat> => cat = ct(0).toString()
  }
}

/* do something with dog and cat */

这让我很恼火,因为我应该能够将 cat 和 dog 声明为 val(不可变),因为我只需要设置它们一次,但我必须让它们可变。除此之外,似乎在 scala 中必须有更好的方法来做到这一点。有任何想法吗?

4

3 回答 3

3

这里有两个(现在变成三个)可能的解决方案。第一个非常快速和肮脏。您可以在 Scala 解释器中运行全部内容。

val xmlData = <outertag>
<dog>val1</dog>
<cat>val2</cat>
</outertag>

// A very simple way to do this mapping.
def simpleGetNodeValue(x:scala.xml.NodeSeq, tag:String) = (x \\ tag).text

val cat = simpleGetNodeValue(xmlData, "cat")
val dog = simpleGetNodeValue(xmlData, "dog")

cat将是“val2”,dog将是“val1”。

请注意,如果没有找到任何一个节点,将返回一个空字符串。你可以解决这个问题,或者你可以用一种更惯用的方式来编写它:

// A more idiomatic Scala way, even though Scala wouldn't give us nulls.
// This returns an Option[String].
def getNodeValue(x:scala.xml.NodeSeq, tag:String) = {
  (x \\ tag).text match {
    case "" => None
    case x:String => Some(x)
  }
}

val cat1 = getNodeValue(xmlData, "cat") getOrElse "No cat found."
val dog1 = getNodeValue(xmlData, "dog") getOrElse "No dog found."
val goat = getNodeValue(xmlData, "goat") getOrElse "No goat found."  

cat1将是“val2”,dog1将是“val1”,并且goat将是“No goat found”。

更新:这是获取标签名称列表并将其匹配项作为 Map[String, String] 返回的另一种便捷方法。

// Searches for all tags in the List and returns a Map[String, String].
def getNodeValues(x:scala.xml.NodeSeq, tags:List[String]) = {
  tags.foldLeft(Map[String, String]()) { (a, b) => a(b) = simpleGetNodeValue(x, b)}
}

val tagsToMatch = List("dog", "cat")
val matchedValues = getNodeValues(xmlData, tagsToMatch)

如果你运行它,matchedValues将是Map(dog -> val1, cat -> val2).

希望有帮助!

更新 2:根据 Daniel 的建议,我正在使用双反斜杠运算符,它将下降到子元素中,随着 XML 数据集的发展,这可能会更好。

于 2009-11-24T03:04:16.853 回答
2
scala> val xml = <outertag><dog>val1</dog><cat>val2</cat></outertag>
xml: scala.xml.Elem = <outertag><dog>val1</dog><cat>val2</cat></outertag>

scala> val cat = xml \\ "cat" text
cat: String = val2

scala> val dog = xml \\ "dog" text
dog: String = val1
于 2009-11-24T14:17:13.253 回答
1

考虑将 XML 检查和模式匹配封装在一个函数中,该函数将您需要的多个值作为元组 ( Tuple2[String, String]) 返回。但是停下来考虑一下:看起来有可能不匹配任何dogandcat元素,这将使您为一个或两个元组组件返回 null 。Option[String]如果任一元素模式无法绑定,也许你可以返回一个元组,或者抛出。

在任何情况下,您通常可以通过将组成语句包装到函数中以产生表达式来解决这些初始化问题。一旦你有了一个表达式,你就可以用它的评估结果来初始化一个常量。

于 2009-11-24T01:55:44.370 回答