I have following two implicits.
implicit val readObjectIdFormat = new Reads[ObjectId] {
def reads(jv: JsValue): JsResult[ObjectId] = {
JsSuccess(new ObjectId(jv.as[String]))
}
}
implicit val visitorFormat = (
(__ \ "_id").formatOpt[ObjectId] and
(__ \ "visitorId").format[String] and
(__ \ "referralUrl").formatOpt[String] and
(__ \ "ipAddress").formatOpt[String] and
(__ \ "promotionId").format[String])(Visitor)
Though readObjectIdFormat is defined at compile time it keeps complaining following on "(__ \ "_id").formatOpt[ObjectId]" line
No Json formatter found for type org.bson.types.ObjectId. Try to implement an implicit
Format for this type.
versions : Play 2.1-RC2, Scala 2.10
Any idea why it's not recognizing readObjectIdFormat ?
Others gave the good answer, use Format instead.
By the way, you could handle parse errors.
This implementation is working fine for me:
implicit val objectIdFormat: Format[ObjectId] = new Format[ObjectId] {
def reads(json: JsValue) = {
json match {
case jsString: JsString => {
if ( ObjectId.isValid(jsString.value) ) JsSuccess(new ObjectId(jsString.value))
else JsError("Invalid ObjectId")
}
case other => JsError("Can't parse json path as an ObjectId. Json content = " + other.toString())
}
}
def writes(oId: ObjectId): JsValue = {
JsString(oId.toString)
}
}
You are implementing Reads and you need implement Format instead.
implicit val readObjectIdFormat = new Format[ObjectId] {
def reads(jv: JsValue): JsResult[ObjectId] = {
JsSuccess(new ObjectId(jv.as[String]))
}
def writes(o: A): JsValue = JsString(...)
}
Or you need to use the read instead of format (note I assume this works for read, haven't tested it).
implicit val visitorRead = (
(__ \ "_id").readOpt[ObjectId] and
(__ \ "visitorId").read[String] and
(__ \ "referralUrl").readOpt[String] and
(__ \ "ipAddress").readOpt[String] and
(__ \ "promotionId").read[String])(Visitor)
From documentation: Format[T] extends Reads[T] with Writes[T]
Format is a read + write.
Write an implicit writeObjectIdFormat then
implicit val formatObjectIdFormat =
Format(readObjectIdFormat, writeObjectIdFormat)
Related
I'm performing a standard mapping of JSON to a case class using PlayJson. I'd like to transform the value that gets mapped to a member, Test.foo below, if the validation succeeds. Is it possible to work that into the definition of a Reads converter?
val json = .....
case class Test(foo:String, bar:String)
val readsTest: Reads[Test] = (
(__ \ "foo").read[String](minLength(5)) and // And I want to transform this value if the validation succeeds
(__ \ "bar").read[String](minLength(10))
)(Test.apply _)
json.validate[Test] match {
case s: JsSuccess[Test] => s.get
case e: JsError => false
}
Reads.map can do just that, for example, say we want to reverse the value of foo field, then we could call .map(v => v.reverse) on the Reads like so
(__ \ "foo").read[String](minLength[String](5)).map(v => v.reverse)
Here is a working example
val json =
"""
|{
| "foo": "abcdefghijkl",
| "bar": "012345678910"
|}
|""".stripMargin
case class Test(foo: String, bar: String)
val readsTest: Reads[Test] = (
(__ \ "foo").read[String](minLength[String](5)).map(v => v.reverse)
(__ \ "bar").read[String](minLength[String](10))
)(Test.apply _)
Json.parse(json).validate[Test](readsTest)
which outputs
JsSuccess(Test(lkjihgfedcba,012345678910),)
i have to check either the password field is alphanumeric or not and if not it will throw custom Validation error i am using play-framework but getting compile time error
value checkAlphanumeric is not a member of
play.api.libs.json.Reads[String]
- value checkAlphanumeric is not a member of
play.api.libs.json.Reads[String]
i am unable to achive my desired outcome i am doing it wrong that's why i need here is the code
case class userUserSignUpValidation(firstName: String,
lastName: String,
email: String,
password: String) extends Serializable
object UserSignUpValidation {
val allNumbers = """\d*""".r
val allLetters = """[A-Za-z]*""".r
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(minLength[String](8)checkAlphanumeric))(UserSignUpValidation.apply _)
def checkAlphanumeric(password:String)={
val allNumbers = """\d*""".r
val allLetters = """[A-Za-z]*""".r
val errors = password match {
case allNumbers() => Seq(ValidationError("Password is all numbers"))
case allLetters() => Seq(ValidationError("Password is all letters"))
case _ => Nil
}
}
i am getting the error on this line
(JsPath \ "password").read(minLength[String](8)checkAlphanumeric))(UserSignUpValidation.apply _)
what is the right way to implement an above scenario
Your problem is that you cannot use your checkAlphanumeric method that way. What you probably want is a filter on the Reads, so I would suggest doing something as follow (I changed the implementation for the check, using pre-existing methods):
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(minLength[String](8).
filterNot(ValidationError("Password is all numbers"))(_.forall(_.isDigit)).
filterNot(ValidationError("Password is all letters"))(_.forall(_.isLetter))
)) (UserSignUpValidation.apply _)
Well I wonder why don't you run validations inside case class?
case class userUserSignUpValidation(firstName: String,
lastName: String,
email: String,
password: String) {
assert(!password.matches("""[A-Za-z]*""") && !password.matches("""\d*"""), "Invalid password")
// have other field validations here
}
And in you UserSignUpValidation use a implicit formatter like this:
object UserSignUpValidation {
implicit val userFormatter = JSON.format[userUserSignUpValidation]
// de-serialization code here
}
I have the following Json as var dataObject ={"files": ["code.R", "data.cv", "input.txt"]}.
I am posting this json as a body from the client side and I want to parse the Json and read these files names in the server side in play scala.
Please help
Because you have only one field, you can't use json combinators,
But you can do as follow:
case class Selection(files:List[String])
object Selection{
implicit val selectionReads = (__ \ 'files).read[List[String]].map{ l => Selection(l) }
implicit val selectionWrites = (__ \ 'files).write[List[String]].contramap { (selection: Selection) => selection.files}
//You can replace the above 2 lines with this line - depends on you.
implicit val selectionFormat: Format[Selection] = (__ \ 'files).format[List[String]].inmap(files => Selection(files), (selection: Selection) => selection.files)
}
Make sure you import:
import play.api.libs.functional.syntax._
This is the documentation: https://www.playframework.com/documentation/2.5.x/ScalaJson
And the solution is simple:
import play.api.libs.json._
val json: JsValue = Json.parse("{ "files": ["code.R","data.csv","input.txt"] }")
val files = (json \ "files").get
I need to serialize a String and an Option[Boolean]:
val myWrites = (
(__ \ "box").write(
(
(__ \ "name").write[String] ~
(__ \ "default").writeNullable[Boolean]
).tupled
)
)
If Option[Boolean] is Some then I'd expect
{
"box": {
"name": "John",
"default": true
}
}
... while if Option[Boolean] is None I'd expect
{
"box": {
"name": "John"
}
}
Given the following variables...
val name = "John"
val default = Some(true)
... how do I pass them to the Writes? I've tried this:
myWrites.writes(name, defaul)
... but it doesn't compile:
No Json serializer found for type play.api.libs.functional.FunctionalBuilder[play.api.libs.json.OWrites]#CanBuild2[String,Option[Boolean]].
Try to implement an implicit Writes or Format for this type.
[error] (__ \ "box").write(
I think its just a typo in your writes. you have defaul vs default
I was able to use
import play.api.libs.json._
import play.api.libs.functional.syntax._
val myWrites = (
(__ \ "box").write(
(
(__ \ "name").write[String] ~
(__ \ "default").writeNullable[Boolean]
).tupled
)
)
myWrites.writes("hi",Some(true))
and I got back
res0: play.api.libs.json.JsObject = {"box":{"name":"hi","default":true}}
In Play 2.1 we use something like below to get a Creature Object out of a JSON through reads.
implicit val creatureReads = (
(__ \ "name").read[String] and
(__ \ "isDead").read[Boolean] and
(__ \ "weight").read[Float]
)(Creature.apply _)
Being relative new in Scala, I'm trying to understand if there is any other way to build the Creature object without using the Apply method? Would it be possible to have an anonymous function to create the object instead of relying on the apply?
I have use cases where most of the fields in my objects could be missing, but I would like to still build the object out of what I have. Is it better to just define one READ for the object and use readnullable for each of the fields?
I also could have complex conditionals, so would it be cleaner to just define custom functions to build it instead of trying to capture all cases in one Reader?
Yes of course, the apply method is just a method that takes all the case classes' parameters. This roughly translates to the following:
implicit val creatureReads = (
(__ \ "name").read[String] and
(__ \ "isDead").read[Boolean] and
(__ \ "weight").read[Float]
)((name: String, isDead: Boolean, weight: Float) => new Creature(name, isDead, weight))
For missing fields you should indeed use readNullable and wrap your classes fields to Option. If there are sensible defaults for your optional fields, you can use orElse(Reads.pure(value)) instead.
Let's say weight is optional and isDead is false by default:
implicit val creatureReads = (
(__ \ "name").read[String] and
(__ \ "isDead").read[Boolean].orElse(Reads.pure(false)) and
(__ \ "weight").readNullable[Float]
)(Creature.apply _)
Sometimes you don't even want to read something from JSON. In that case, one possibility is passing the value explicitly:
def creatureReads(createdAt: DateTime) = (
(__ \ "name").read[String] and
(__ \ "isDead").read[Boolean].orElse(Reads.pure(false)) and
(__ \ "weight").readNullable[Float] and
(__ \ "createdAt").read(createdAt)
)(Creature.apply _)
I find this to be much more readable:
implicit val createReads = new Reads[Creature] {
override def reads(json: JsValue): JsResult[Creature] = {
val creature = Creature(
name = (json \ "name").as[String],
isDead = (json \ "isDead").as[Boolean],
weight = (json \ "weight").as[Float]
)
JsSuccess(creature)
}
}