vex*_*ace 4 json scala optional
我正在尝试按照教程https://www.jamesward.com/2012/02/21/play-framework-2-with-scala-anorm-json-coffeescript-jquery-heroku,但当然play-scala已更改从本教程开始(就像我发现的每个教程的情况一样).我正在使用2.4.3这要求我实际学习如何工作,不一定是坏事.
给我带来麻烦的一件事是getOrElse方法.
这是我的Bar.scala模型
package models
import play.api.db._
import play.api.Play.current
import anorm._
import anorm.SqlParser._
case class Bar(id: Option[Long], name: String)
object Bar {
val simple = {
get[Option[Long]]("id") ~
get[String]("name") map {
case id~name => Bar(id, name)
}
}
def findAll(): Seq[Bar] = {
DB.withConnection { implicit connection =>
SQL("select * from bar").as(Bar.simple *)
}
}
def create(bar: Bar): Unit = {
DB.withConnection { implicit connection =>
SQL("insert into bar(name) values ({name})").on(
'name -> bar.name
).executeUpdate()
}
}
}
Run Code Online (Sandbox Code Playgroud)
和我的BarFormat.scala Json格式化程序
package models
import play.api.libs.json._
import anorm._
package object Implicits {
implicit object BarFormat extends Format[Bar] {
def reads(json: JsValue):JsResult[Bar] = JsSuccess(Bar(
Option((json \ "id").as[Long]),
(json \ "name").as[String]
))
def writes(bar: Bar) = JsObject(Seq(
"id" -> JsNumber(bar.id.getOrElse(0L)),
"name" -> JsString(bar.name)
))
}
}
Run Code Online (Sandbox Code Playgroud)
为了完整性,我的Application.scala控制器:
package controllers
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import javax.inject.Inject
import javax.inject._
import play.api.i18n.{ I18nSupport, MessagesApi, Messages, Lang }
import play.api.libs.json._
import views._
import models.Bar
import models.Implicits._
class Application @Inject()(val messagesApi: MessagesApi) extends Controller with I18nSupport {
val barForm = Form(
single("name" -> nonEmptyText)
)
def index = Action {
Ok(views.html.index(barForm))
}
def addBar() = Action { implicit request =>
barForm.bindFromRequest.fold(
errors => BadRequest,
{
case (name) =>
Bar.create(Bar(None, name))
Redirect(routes.Application.index())
}
)
}
def listBars() = Action { implicit request =>
val bars = Bar.findAll()
val json = Json.toJson(bars)
Ok(json).as("application/json")
}
Run Code Online (Sandbox Code Playgroud)
和路线
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
POST /addBar controllers.Application.addBar
GET / controllers.Application.index
GET /listBars controllers.Application.listBars
# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
Run Code Online (Sandbox Code Playgroud)
当我尝试运行我的项目时,我收到以下错误:
现在bar.id被定义为一个Option [Long],所以bar.id.getOrElse(0L)应该返回一个Long,据我所知,但它显然返回了Any.谁能帮我理解为什么?
谢谢!
这就是Scala中类型推断的工作方式......
首先,隐式转换Int为BigDecimal:
scala> (1 : Int) : BigDecimal
res0: BigDecimal = 1
Run Code Online (Sandbox Code Playgroud)
该转换允许Int在构造选项之前进行转换:
scala> Some(1) : Option[BigDecimal]
res1: Option[BigDecimal] = Some(1)
Run Code Online (Sandbox Code Playgroud)
如果我们尝试getOrElse自己的类型可以修复,我们得到预期的类型Int:
scala> Some(1).getOrElse(2)
res2: Int = 1
Run Code Online (Sandbox Code Playgroud)
但是,这不起作用(你遇到的问题):
scala> Some(1).getOrElse(2) : BigDecimal
<console>:11: error: type mismatch;
found : Any
required: BigDecimal
Some(1).getOrElse(2) : BigDecimal
^
Run Code Online (Sandbox Code Playgroud)
在执行类型推断之后,Scala的隐式转换最后一次启动.这是有道理的,因为如果你不知道类型,你怎么知道需要应用什么转换.Scala可以看到这BigDecimal是预期的,但它有一个Int基于它的类型的结果Option.所以它试图扩大型,无法找到任何匹配BigDecimal中Int的类型层次和失败,出现错误.
这是有效的,但是因为类型在变量声明中是固定的:
scala> val v = Some(1).getOrElse(2)
v: Int = 1
scala> v: BigDecimal
res4: BigDecimal = 1
Run Code Online (Sandbox Code Playgroud)
所以我们需要以某种方式帮助编译器 - 任何类型的注释或显式转换都可以工作.选择你喜欢的任何一个:
scala> (Some(1).getOrElse(2) : Int) : BigDecimal
res5: BigDecimal = 1
scala> Some(1).getOrElse[Int](2) : BigDecimal
res6: BigDecimal = 1
scala> BigDecimal(Some(1).getOrElse(2))
res7: scala.math.BigDecimal = 1
Run Code Online (Sandbox Code Playgroud)