2

考虑 Scala 解释器中的以下内容:

scala> JSON.parseFull("""{"name":"jack","greeting":"hello world"}""")
res6: Option[Any] = Some(Map(name -> jack, greeting -> hello world))

为什么 Map 在 Some() 中返回?我该如何使用它?

我想将值放在 xml 模板中:

<test>
  <name>name goes here</name>
  <greeting>greeting goes here</greeting>
</test>

从 Some(thing) 中获取我的地图并在 xml 中获取这些值的 Scala 方式是什么?

4

4 回答 4

6

你可能应该使用这样的东西:

res6 collect { case x: Map[String, String] => renderXml(x) }

在哪里:

def renderXml(m: Map[String, String]) = 
  <test><name>{m.get("name") getOrElse ""}</name></test>

collecton 方法采用Option[A]a PartialFunction[A, B]and 是filter(通过谓词)和map(通过函数)的组合。那是:

opt collect pf
opt filter (a => pf isDefinedAt a) map (a => pf(a))

两者是等价的。当您有一个可选值时,您应该使用map, flatMap,filtercollect来转换程序中的选项,避免通过模式匹配或通过方法提取get选项的内容。你永远不应该使用-Option.get这是你做错了的典型标志。应该避免模式匹配,因为它代表程序中的一个分支,因此会增加圈复杂性 - 您可能希望这样做的唯一时间可能是为了提高性能


实际上,您遇到的问题是该parseJSON方法的结果是 an Option[Any](原因是它是 an Option,大概是解析可能不会成功,并且是一种比,好吧,Option更优雅的处理方式)。nullnull

但是我上面的代码的问题是,case x: Map[String, String]由于类型擦除,无法在运行时检查(即 scala 可以检查选项是否包含 aMap但不能检查Map' 的类型参数都是String. 代码会给你一个未经检查的警告。

于 2012-02-15T08:03:47.230 回答
6

AnOption被返回是因为parseFull根据输入具有不同的可能返回值,或者它可能根本无法解析输入(给None)。因此,除了将Map键与值相关联的可选项之外,List如果 JSON 字符串表示数组,则还可以返回可选项。

例子:

scala> import scala.util.parsing.json.JSON._
import scala.util.parsing.json.JSON._

scala> parseFull("""{"name":"jack"}""")
res4: Option[Any] = Some(Map(name -> jack))

scala> parseFull("""[ 100, 200, 300 ]""")
res6: Option[Any] = Some(List(100.0, 200.0, 300.0))

您可能需要模式匹配才能实现您想要的,如下所示:

scala> parseFull("""{"name":"jack","greeting":"hello world"}""") match {
     |   case Some(m) => Console println ("Got a map: " + m)
     |   case _ =>
     | }
Got a map: Map(name -> jack, greeting -> hello world)

现在,如果你想生成 XML 输出,你可以使用上面的方法来迭代键/值对:

import scala.xml.XML

parseFull("""{"name":"jack","greeting":"hello world"}""") match {
  case Some(m: Map[_,_]) =>
    <test>
      {
        m map { case (k,v) =>
          XML.loadString("<%s>%s</%s>".format(k,v,k))
        }
      }
    </test>

  case _ =>
}
于 2012-02-15T08:30:31.050 回答
5

parseFull返回 anOption因为字符串可能不是有效的 JSON(在这种情况下它将返回None而不是Some)。

从 a 中获取值的常用方法Some是对其进行模式匹配,如下所示:

result match {
    case Some(map) =>
        doSomethingWith(map)
    case None =>
        handleTheError()
}

如果您确定输入将始终有效,因此您不需要处理无效输入的情况,您可以使用 上的get方法,Option在调用时会抛出异常None

于 2012-02-15T08:02:03.763 回答
4

你有两个不同的问题。

  1. 它的类型为Any.
  2. 您的数据在 aOption和 a内Map

假设我们有数据:

val x: Option[Any] = Some(Map("name" -> "jack", "greeting" -> "hi"))

并假设如果有要返回的内容,我们想要返回适当的 XML,否则不返回。然后我们可以collect用来收集那些我们知道如何处理的部分:

val y = x collect {
  case m: Map[_,_] => m collect {
    case (key: String, value: String) => key -> value
  }
}

(请注意我们如何将映射中的每个条目分开以确保它将字符串映射到字符串——否则我们将不知道如何进行。我们得到:

y: Option[scala.collection.immutable.Map[String,String]] =
  Some(Map(name -> jack, greeting -> hi))

好的,这样更好!现在,如果您知道XML 中需要哪些字段,您可以询问它们:

val z = for (m <- y; name <- m.get("name"); greet <- m.get("greeting")) yield {
  <test><name>{name}</name><greeting>{greet}</greeting></test>
}

在这种(成功的)案例中产生

z: Option[scala.xml.Elem] =
  Some(<test><name>jack</name><greeting>hi</greeting></test>)

并且在不成功的情况下会产生None.

如果你想将你在 map 中发现的任何东西包装在 form<key>value</key>中,那就需要做更多的工作,因为 Scala 对标签没有很好的抽象:

val z = for (m <- y) yield <test>{ m.map { case (tag, text) => xml.Elem(null, tag, xml.Null, xml.TopScope, xml.Text(text)) }}</test>

这再次产生

z: Option[scala.xml.Elem] =
  Some(<test><name>jack</name><greeting>hi</greeting></test>)

(您可以使用get来获取 an 的内容,但如果 the为空(即) Option,它将引发异常。)OptionNone

于 2012-02-15T12:14:35.363 回答