玩anorm,从json创建一个模型而不在json中传递anorm PK值

时间:2013-05-21 09:10:38

标签: json playframework anorm

object Users  {

  implicit object UserReads extends Reads[User] {
    def reads(json: JsValue) = JsSuccess(User(
      Id((json \ "id").as[String].toLong),
      (json \ "name").as[String],
      (json \ "email").as[String]
  }
  implicit object UserWrites extends Writes[User] {
    def writes(user: User) = JsObject(Seq(
      "id" -> JsNumber(user.id.get),
      "name" -> JsString(user.name),
      "email" -> Json.toJson(user.email)
  }

  def view(id: Long) = Action { implicit request =>
    Ok(Json.toJson(User.find(id)))
  }

  def all() = Action { implicit request =>
    Ok(Json.toJson(User.findAll()))
  }

  def save = Action(parse.json) { request =>
    val userJson = request.body
    val user = userJson.as[User]
    try {
      User.create(user)
      Ok("Saved")
    } catch {
      case e: IllegalArgumentException => BadRequest("Error")
    }
  }
}


case class User(
  id: Pk[Long] = NotAssigned,
  name: String = "",
  email: String = "")

以上两个是我的控制器&模型,当我使用angular JS将用户数据[id,name,email]发送为json时,它会在数据库中创建User对象。但是当我只输入[name,email]或[name]时它应该可以创建,因为电子邮件可能为null。如果我没有错,我应该在User的读写方法中调整这些,是吗?。

另外,我们可以为不同的目标进行两次读/写操作,如果是这样的话,怎么能实现呢 - 抛出一些光。感谢。

以下内容确定了一个问题:

case class User(
  id: Pk[Long] = NotAssigned,
  name: String = "",
  email: Option[String])

implicit object UserReads extends Reads[User] {
    def reads(json: JsValue) = JsSuccess(User(
      Id((json \ "id").as[String].toLong),
      (json \ "name").as[String],
      (json \ "email").asOpt[String])
  }
  implicit object UserWrites extends Writes[User] {
    def writes(user: User) = JsObject(Seq(
      "id" -> JsNumber(user.id.get),
      "name" -> JsString(user.name),
      "email" -> Json.toJson(user.email))
  }

现在电子邮件可以为null,但是id-PK [Long]

2 个答案:

答案 0 :(得分:2)

我没有编译它,但它应该工作:

case class User(
  id: Pk[Long] = NotAssigned,
  name: String = "",
  email: Option[String])

implicit object UserReads extends Reads[User] {
    def reads(json: JsValue) = JsSuccess(User(
      (json \ "id").asOpt[Long].map(id => Id[Long](id)).getOrElse(NotAssigned)
      (json \ "name").as[String],
      (json \ "email").asOpt[String])
  }
  implicit object UserWrites extends Writes[User] {
    def writes(user: User) = JsObject(Seq(
      "id" -> JsNumber(user.id.get),
      "name" -> JsString(user.name),
      "email" -> Json.toJson(user.email))
  }

基本上,您将ID视为Option[Long],就像使用电子邮件一样。然后检查它是否已设置,如果是,则使用Id[Long](id创建Pk实例,如果不是,则提供NotAssigned Pk单例实例。

其他提示

或者您可以尝试使用

implicit val jsonFormatter = Json.format[User]

它将在编译时直接为您创建ReadsWrites。 如果您直接使用anorm对象,则存在两个问题:

首先,您需要Pk [Long]的格式

implicit object PkLongFormat extends Format[Pk[Long]] {
  def reads(json: JsValue): JsResult[Pk[Long]] = {
    json.asOpt[Long].map {
      id => JsSuccess(Id[Long](id))
    }.getOrElse(JsSuccess(NotAssigned))
  }
  def writes(id: Pk[Long]): JsNumber = JsNumber(id.get)
}

第二个它不起作用,如果你根本不发送一个id,甚至没有值null,所以你的客户端需要发送{"id": null, "name": "Tim"},因为它甚至没有尝试调用可以处理它的PkFormatter JsUndefined,但只是给你一个“validate.error.missing-path”错误

如果您不想发送空ID,则无法使用宏Json.format[User]

答案 1 :(得分:0)

类型anorm.Pk在形式和功能上几乎与scala.Option完全相同。避免为可能包含的所有类型编写具体的ReadsWritesOptionReadsOptionWrites实现之后的示例如下:

implicit def PkWrites[T](implicit w: Writes[T]): Writes[Pk[T]] = new Writes[Pk[T]] {
  def writes(o: Pk[T]) = o match {
    case Id(value) => w.writes(value)
    case NotAssigned => JsNull
  }
}

implicit def PkReads[T](implicit r: Reads[T]): Reads[Pk[T]] = new Reads[Pk[T]] {
  def reads(js: JsValue): JsResult[Pk[T]] =
    r.reads(js).fold(e => JsSuccess(NotAssigned), v => JsSuccess(Id(v)))
}

这样,您支持每个TReads[T]的{​​{1}},并且在处理Writes[T]Id时更可靠。< / p>