使用Json.obj和getOrElse在Play 2.3中编写选项

9

从激活器控制台可以使用此功能:

scala> import play.api.libs.json._
import play.api.libs.json._

scala> val testVal = Some("foo")
testVal: Some[String] = Some(foo)

scala> Json.obj("myJson" -> testVal)
res0: play.api.libs.json.JsObject = {"myJson":"foo"}

这也可以运行:
scala> Json.obj("myJson" -> testVal.get)
res3: play.api.libs.json.JsObject = {"myJson":"foo"}

这个失败了:

scala> Json.obj("myJson" -> testVal.getOrElse(""))
 <console>:12: error: type mismatch;
  found   : Object
  required: play.api.libs.json.Json.JsValueWrapper
          Json.obj("myJson" -> testVal.getOrElse(""))

但是这个可以运行:
scala> val testVal2 = testVal.getOrElse("")
testVal2: String = foo

scala> Json.obj("myJson" -> testVal2)
res2: play.api.libs.json.JsObject = {"myJson":"foo"}

为什么编译器会拒绝我的第三个示例?testVal.getOrElse("")的计算结果是一个字符串,那么为什么编译器在上面的第三个示例中认为它是Object
2个回答

11

你还可以帮助一下getOrElse方法,直接定义它的返回类型。

Json.obj("myJson" -> testVal.getOrElse[String](""))

这个答案比testVal.fold选项容易理解得多。 - nVitius
简洁而直观 - Jake

10
Json.obj的参数是重复的(String, JsValueWrapper),当你传递某些变量时,编译器将尝试使用在Play JSON库中(或者对于你的类型是Writes)定义的转换将你的类型隐式转换为JsValueWrapper
这里的问题在于getOrElse的逆变性。因为getOrElse的签名是这样的:
def getOrElse[B >: A](default:  B): B
这意味着如果你有一个Option[String],你可以向getOrElse提供一个非String的值,那么Option[String]现在很可能会变成Option[Any]。因此,编译器不会寻找到JsValueWrapper的隐式转换,从而导致失败。
如果你在Option上使用不变的fold,这个问题就会消失。
scala> val testVal = Some("foo")
testVal: Some[String] = Some(foo)

scala> Json.obj("myJson" -> testVal.fold("")(identity))
res7: play.api.libs.json.JsObject = {"myJson":"foo"}

我想我明白了。然而,就在我的最后一个例子中,我仍然有些困惑它为什么能够工作。为什么编译器会对val的处理方式与将getOrElse表达式内联放置不同呢?它们计算出相同的结果,并且毫无疑问它总是一个字符串。 - imagio
3
在你最后的例子中,类型从赋值中推断为String。但在代码中,编译器只会从getOrElse的类型参数中推断类型。也就是说,它不会往后看以查找字面量String,以便使用隐式转换。 - Michael Zajac

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接