Play anonymous, create a model from json without going through an abnormal PK value in json

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 = "") 

The above two are my controller and model, when I send user data [id, name, email] as json using angular JS, it creates a User object in the database. But it should be able to create when I enter only [name, email address] or just [name], as the email can be zero. If I’m not mistaken, I have to configure them in the method of reading and writing the user, right ?.

In addition, can we have two reads / records for different purposes, if so, how can this be achieved - throw out some kind of light. Thanks.

One problem with the following has been fixed:

 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)) } 

Now the email may be null, but what about id - PK [Long]

+4
source share
2 answers

I did not compile this, but it should work:

 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)) } 

Basically you accept the identifier as Option[Long ], as you did with the email. Then you check if it is installed, and yes, you create an instance of Pk using Id[Long](id ), and if you do not provide an instance of NotAssigned Pk singleton.

Extra tip

Alternatively you can try using

 implicit val jsonFormatter = Json.format[User] 

It will create Reads and Writes for you directly at compile time. There are two problems with this if you use the anorm object directly:

First you need a format for 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) } 

Secondly, it does not work if you do not send an identifier at all, even with a null value, so your client needs to send {"id": null, "name": "Tim"} , because it does not even try to call PkFormatter, which could handle JsUndefined but just gives you the error "validate.error.missing-path"

If you do not want to send null identifiers, you cannot use the Json.format[User] macro

+2
source

The type anorm.Pk almost similar to scala.Option in form and function. Avoid writing specific Reads and Writes for all types that may contain. An example following the implementations of OptionReads and OptionWrites is as follows:

 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))) } 

Thus, you support every T for which there is a corresponding Reads[T] or Writes[T] , and it is more reliable when handling Id and NotAssigned .

0
source

Source: https://habr.com/ru/post/1481901/


All Articles