unable to create an implicit read for JSON - scala

My server code will receive the following JSON
{
"signin-info":{
"email" : "someemail",
"password": "password"
}
}
I have created the following 2 case classes to represent this structure.
case class UserSigninInfo(
email:String,
password:String
)
case class UserSignin(
signinInfo: UserSigninInfo
)
To read the JSON, I have created the following 2 Reads
implicit val userSigninInfoReads:Reads[UserSigninInfo] = (
(JsPath \ "email").read[String] and
(JsPath \ "password").read[String]
)(UserSigninInfo.apply _)
implicit val userSigninReads:Reads[UserSignin] = (
(JsPath \ "signin-info").read[UserSigninInfo]
)(UserSignin.apply _)
But for userSigninReads, I am getting the following compilation error. What am I doing wrong?
overloaded method value read with alternatives:
[error] (t: models.UserSigninInfo)play.api.libs.json.Reads[models.UserSigninInfo] <and>
[error] (implicit r: play.api.libs.json.Reads[models.UserSigninInfo])play.api.libs.json.Reads[models.UserSigninInfo]
[error] cannot be applied to (models.UserSigninInfo => models.UserSignin)
[error] (JsPath \ "signin-info").read[UserSigninInfo]
[error] `

The solution to construct reads for your main object is:
implicit val userSigninReads : Reads[UserSignin] =
(JsPath \ "signin-info").read[UserSigninInfo].map(UserSignin(_))
This is because it only has a single field.
Your construction was essentailly:
((JsPath \ "signin-info").read[UserSigninInfo])(UserSignin.apply _)
which is simply
(JsPath \ "signin-info").read[UserSigninInfo](UserSignin.apply _)
because it just adds extra ().
read method indeed has two alternatives:
def read[T](t: T) = Reads.pure(t)
def read[T](implicit r: Reads[T]): Reads[T] = Reads.at[T](this)(r)
It expects either implicit reads or explicit value, but you are passing a function, this is why compiler complains.
When you combine multiple fields with and, different object is created, e.g. FunctionalBuilder[M]#CanBuild2[A, B] for 2 elements, and it has apply method to construct final Reads instance.
As you only have one object you can create your Reads by reading UserSigninInfo from the path you want and putting the result into UserSignin with map.

Related

Handling non-default nested Json Reads

So I have following JSON:
{
"senderEmail" : "sender#email.com",
"recipientEmails" : ["first#email.com", "second#email.com"]
}
and would like to map it to case class:
case class Payload (senderEmail: String, recipientEmails: Seq[String])
using Play's Json Reads with email validator.
While it's trivial for a senderEmail, I'm having trouble with recipientEmails since it's both Seq and email so this will not work:
implicit val payloadRead: Reads[Payload] = (
(JsPath \ "senderEmail").read[String](Reads.email) and
(JsPath \ "recipientEmails").read[Seq[String]](Reads.seq))(Payload.apply _)
I'm getting overloaded method value read with alternatives.
So how can I combine both Reads.seq and Reads.email?
Just keep it simple ...
scala> import play.api.libs.json._
scala> import play.api.libs.functional.syntax._
scala> case class Payload (senderEmail: String, recipientEmails: Seq[String])
defined class Payload
scala> implicit val reads: Reads[Payload] = (
| (JsPath \ "senderEmail").read(Reads.email) and
| (JsPath \ "recipientEmails").read(Reads.seq(Reads.email))
| )(Payload.apply _)
reads: play.api.libs.json.Reads[Payload] = play.api.libs.json.Reads$$...
scala> Json.parse("""{
| "senderEmail" : "sender#email.com",
| "recipientEmails" : ["first#email.com", "second#email.com"]
| }""").validate[Payload]
res0: play.api.libs.json.JsResult[Payload] = JsSuccess(Payload(sender#email.com,Vector(first#email.com, second#email.com)),)

overloaded method value filter with alternatives: in play-framework 2.6

I upgraded to play 2.6 from play 2.4 i am getting this compile time error
[error] myproject/app/models/jsonparsing/DirectUserSignUpJsonValidation.scala:27: overloaded method value filter with alternatives:
[error] (error: play.api.libs.json.JsonValidationError)(f: String => Boolean)play.api.libs.json.Reads[String] <and>
[error] (f: String => Boolean)play.api.libs.json.Reads[String]
[error] cannot be applied to (play.api.data.validation.ValidationError)
[error] filter(ValidationError("Password length is less than 8"))(_.length >= 8).
here is the code
case class DirectUserSignUpValidation(firstName: String,
lastName: String,
email: String,
password: String) extends Serializable
object DirectUserSignUpValidation {
var validationErrorMsg=""
implicit val readDirectUser: Reads[DirectUserSignUpValidation] = (
(JsPath \ "firstName").read(minLength[String](1)) and
(JsPath \ "lastName").read(minLength[String](1)) and
(JsPath \ "email").read(email) and
(JsPath \ "password").read[String].
filter(ValidationError("Password length is less than 8"))(_.length >= 8).
filterNot(ValidationError("Password is all numbers"))(_.forall(_.isDigit)).
filterNot(ValidationError("Password is all letters"))(_.forall(_.isLetter))
)(DirectUserSignUpValidation.apply _)
}
The new play-json libs now work with JsonValidationError and you are using the old ValidationError.

Scala play framework: complex read to match multiple keys for same field in case class

i'm a scala newbie...
let's say i have a case class like this:
case class Event(name: Option[String]) {}
i want to use the Play framework to parse it. however, sometimes i get a json payload where the first letter of the key is uppercase and sometimes lowercase. like so:
lowercase
{
"name": "group_unsubscribe",
}
uppercase
{
"Name": "group_unsubscribe",
}
how can i account for these possibilities using a complex reads?
i have tried with things like:
implicit val reads: Reads[Event] = (
((JsPath \ "name").readNullable[String] or
(JsPath \ "Name").readNullable[String])
)(Event.apply _)
but no joy :(
You need to re-write your Reads as:
implicit val reads: Reads[Event] = (
(JsPath \ "name").readNullable[String] orElse
(JsPath \ "Name").readNullable[String]
).map(Event(_))
Update 1 taking into account the comments:
import play.api.libs.json.Reads
implicit val reads: Reads[Event] = (
(JsPath \ "name").read[String] orElse
(JsPath \ "Name").read[String]
).map(name => Event(Option(name)))
Note: this implementation assumes that either "name" or "Name" will always be present in the incoming JSON document.
In order to capture the possibility of failure, you should use .validate[T] instead of .as[T].
Update 2 taking into account further comments:
Whether you have one or more attributes in your type doesn't change much. If your type had another field called somethingElse you would need to adapt your Reads to something like:
implicit val reads: Reads[Event] = (
((JsPath \ "name").read[String] orElse
(JsPath \ "Name").read[String]).map(Option(_)) ~
(JsPath \ "somethingElse").read[String]
)(Event.apply _)

read Json into Scalaz Tree

Scala newbie here.
I use Play to provide a json API for reading and writing a directory like structure. Therefore I use Scalaz.Tree, which provides ways of traversing, updating and rebuilding the Tree.
Formatting the Tree into json works well:
case class File(id: String = BSONObjectID.generate.toString(), name: String, noteBookId: String = null)
implicit val fileFormat: Format[File] = Json.format[File]
implicit def treeWrites: Writes[Tree[File]] =
new Writes[Tree[File]] {
def writes(o: Tree[File]) = o match {
case Node(file, children) => Json.obj(
"name" -> file.name,
"id" -> file.id,
"children" -> JsArray(children.map(Json.toJson(_))),
"notebookId" -> file.noteBookId
)
}
}
Reading json into a Tree however, fails
implicit def treeReads: Reads[Tree[File]] = (
//(__ \ "children").lazyRead(Reads.seq[File](treeReads)) and
(__ \ "children").read[Tree[File]] and
(__ \ "name").read[String] and
(__ \ "notebookid").read[String] and // <-- this is line 41, where the error message points at!!
(__ \ "id").read[String]
)(apply _)
implicit val treeFormat: Format[Tree[File]] = Format(treeReads, treeWrites)
The error I am getting:
[error] /home/dikken/Development/core-service-spaas/app/models/dirTree.scala:41: overloaded method value apply with alternatives:
[error] [B](f: B => (scalaz.Tree[model.treedir.File], String, String, String))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and>
[error] [B](f: (scalaz.Tree[model.treedir.File], String, String, String) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B]
[error] cannot be applied to ((=> Nothing) => scalaz.Tree[Nothing])
[error] (__ \ "id").read[String] and
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
Does this mean I have to pattern match on a case where I have a Tree of Nothing? And how excatly should I do that?
Any help appreciated! Tx!
I'm going to assume that apply _ is actually File.apply _, which cannot work here. File.apply accepts the parameters of the case class File (of which there are three). With JSON combinators, it is trying to pass the four parameters above to File.apply, which does not mix. It also does not produce a Tree[File]. What you need to do is replace File.apply with a method that accepts (children, notebookid, name, id) as parameters, and produces a Tree[File].
Here's a somewhat crude one that will work:
def jsonToTree(children: Seq[Tree[File]], name: String, notebookid: String, id: String): Tree[File] =
Tree.node(File(id, name, notebookid), children.toStream)
The Reads will now look more like this:
implicit def treeReads: Reads[Tree[File]] = (
(__ \ "children").lazyRead[Seq[Tree[File]]](Reads.seq(treeReads)).orElse(Reads.pure(Nil)) and
(__ \ "name").read[String] and
(__ \ "notebookid").read[String] and
(__ \ "id").read[String]
)(jsonToTree _)
You were closer with the commented out line as well. Because this is a recursive structure, we need to use lazyRead.
Testing:
val js = Json.parse("""{
"id": "1",
"name": "test",
"notebookid": "abc",
"children": [
{
"id": "2",
"name": "test222",
"notebookid": "ijk"
},
{
"id": "3",
"name": "test333",
"notebookid": "xyz"
}
]
}""")
scala> val tree = js.as[Tree[File]]
tree: scalaz.Tree[File] = <tree>
scala> tree.rootLabel
res8: File = File(1,test,abc)
scala> tree.subForest
res9: Stream[scalaz.Tree[File]] = Stream(<tree>, ?)
This can also be done (certainly in different ways) without combinators, as well (provided there is an implicit Reads[File] available):
implicit def treeReads: Reads[Tree[File]] = new Reads[Tree[File]] {
def reads(js: JsValue): JsResult[Tree[File]] = {
js.validate[File] map { case file =>
(js \ "children").validate[Stream[Tree[File]]].fold(
_ => Tree.leaf(file),
children => Tree.node(file, children)
)
}
}
}

scala trouble with play framework JSON serialization from case class

Play 2.2.1, scala 2.10
// PersonModel.scala
case class PersonModel(name: String, age: Long)
object PersonModel2 {
implicit object PersonModelFormat extends Format[PersonModel] {
def reads(json: JsValue): PersonModel = PersonModel(
(json \ "name").as[String],
(json \ "age").as[Long])
def writes(u: PersonModel): JsValue = JsObject(List(
"name" -> JsString(u.name),
"age" -> JsNumber(u.age)))
}
sbt says
[error] PersonModel.scala:15: overriding method reads in trait Reads of type (json: play.api.libs.json.JsValue)play.api.libs.json.JsResult[models.PersonModel];
[error] method reads has incompatible type
[error] def reads(json: JsValue): PersonModel = PersonModel(
[error] ^
In this case, since you're not doing fancy things with the output json (like changing the key names in the resulting json object), I'd go for:
case class PersonModel(name: String, age: Long)
import play.api.libs.json._
implicit val personModelFormat = Json.format[PersonModel]
This way, you can, for example
scala> val j = PersonModel("julien", 35)
j: PersonModel = PersonModel(julien,35)
scala> println(Json.toJson(j))
{"name":"julien","age":35}
More info can be found here
HTH,
Julien
Things have changed in recent versions, I'd say for the better. In 2.2.x, you'd do it this way, using the new functional syntax and combinators:
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val PersonModelFormat: Format[PersonModel] = (
(__ \ "name").format[String] and
(__ \ "age").format[Long]
)(PersonModel.apply, unlift(PersonModel.unapply))
Much shorter!
The documentation for 2.2.x http://www.playframework.com/documentation/2.2.1/ScalaJsonCombinators provides a good explanation for the rationale for the change.
For single usage there is an inline solution:
Json.writes[PersonModel].writes(personModelInstance) // returns JsObject
From documentation:
macro-compiler replaces Json.writes[User] by injecting into compile chain the exact code you would write yourself