播放:将案例类序列化为json的问题

时间:2014-01-06 19:16:24

标签: json scala playframework

我正在实现一个REST API,如果出现错误,我想发回一条像这样的Json消息:

{"errors":[
  {"timestamp":"14-Jan-2014 20:00:01","message":"Oops... error 1","code":1500}
  {"timestamp":"14-Jan-2014 20:01:04","message":"Oops... error 2","code":1503}
]}

以下是我的Writes

import org.joda.time.DateTime
import play.api.libs.json._
import play.api.libs.functional.syntax._

class Error private(
  val timestamp: String,
  val message: String,
  val code: Int
) {
  override def hashCode = (timestamp + message + code).hashCode
}

object Error {

  def apply(message: String, code: Int) =
    new Error(DateTime.now.toString(""), message, code)

  def unapply(error: Error) = {
    if (error eq null) null
    else Some((
      error.timestamp,
      error.message,
      error.code
    ))
  }
}

case class Errors(dummy: String, errors: List[Error])

object Errors {

  val errorWrites: Writes[Error] = (
    (__ \ 'timestamp).write[String] ~
    (__ \ 'message).write[String] ~
    (__ \ 'code).write[Int]
  )(unlift(Error.unapply))

  implicit val errorsWrites: Writes[Errors] = (
    (__ \ 'errors).write[List[Error]](errorWrites)
  )(unlift(Errors.unapply))
}

上面的代码无法编译,我总是收到以下错误:

[test] $ compile
[info] Compiling 1 Scala source to /home/j3d/Projects/test/target/scala-2.10/classes...
[error] /home/j3d/Projects/test/app/models/Errors.scala:88: overloaded method value write with alternatives:
[error]   (t: List[models.core.Error])(implicit w: play.api.libs.json.Writes[List[models.core.Error]])play.api.libs.json.OWrites[play.api.libs.json.JsValue] <and>
[error]   (implicit w: play.api.libs.json.Writes[List[models.core.Error]])play.api.libs.json.OWrites[List[models.core.Error]]
[error]  cannot be applied to (play.api.libs.json.Writes[models.core.Error])
[error]     (__ \ 'errors).write[List[Error]](errorWrites)
[error]                         ^
[error] one error found
[error] (core/compile:compile) Compilation failed
[error] Total time: 0 s, completed Jan 6, 2014 8:02:30 PM

我错过了什么吗?

1 个答案:

答案 0 :(得分:2)

嗯,contramap对我有用:

object Errors {

  implicit val errorWrites: Writes[Error] = (
    (__ \ 'timestamp).write[String] ~
    (__ \ 'message).write[String] ~
    (__ \ 'code).write[Int]
  )(unlift(Error.unapply))

  val foo: Writes[Errors] =  
    (__ \ 'errors).write[List[Error]].contramap( (e:Errors) => e.errors )

}

直接方法也有效:

implicit val errorsWrites = new Writes[Errors] {
  def writes(e: Errors): JsValue = {
    Json.obj(
        "errors" -> Json.toJson(e.errors)
    )
  }
}

使用Writes apply方法:

implicit val errorsWrites2 = Writes[Errors] {(e:Errors) => 
    Json.obj("errors" -> Json.toJson(e.errors))
}

我还没有找到让json功能构建器只接受一个值的方法。 但我认为这并不重要,因为上述方法都有效。

从评论讨论中,如果你想为Errors明确指定Writes [Error],你应该这样做:

val listErrorWrites: Writes[List[Error]] = Writes { l : List[Error] =>
    Json.arr(l.map(Json.toJson(_)(errorWrites)))
  }

implicit val errorsWrites4: Writes[Errors] = (__ \ 'errors).write[List[Error]](listErrorWrites).contramap((e:Errors) => e.errors)