How to Play & Joda: How to serialize and deserialize DateTime by Scala? - json

I'm using Play 2.3.7, Scala version 2.11.4.
I have this kind of class. I would like to serialize and deserialize object into json and from json to object.
case class Person(var id: Int = 0,
var first_name: String = "",
var last_name: String = "",
var email: String = "",
var date_of_birth: DateTime = new DateTime())
After reading documents I found I need own implicit read and writer. So I tried as follows:
implicit val personWrites: Writes[Person] = (
(__ \ "id").write[Int] ~
(__ \ "first_name").write[String] ~
(__ \ "last_name").write[String] ~
(__ \ "email").write[String] ~
(__ \ "date_of_birth").write[DateTime])
(unlift(Person.unapply))
implicit val userReads: Reads[Person] = (
(__ \ "id").read[Int] ~
(__ \ "first_name").read[String] ~
(__ \ "last_name").read[String] ~
(__ \ "nickname").read[String] ~
(__ \ "date_of_birth").read[DateTime]
)(Person.apply _)
I get compiler error: overloaded method value apply with alternatives ......
Please inform me how to do it? Thanks!

You don't need to write your own reads / writes unless they are not symmetric or you are doing something custom. Json has a format method that creates an formatter from case classes. It also has default formatters for some things including Joda DateTime classes.
implicit val personFormatter:Format[Person] = Json.format[Person]

Related

Scala/Play How to use JSON read to handle JSON value that can be string or array and always return Set[String]

I have the following class in my play server and the JSON Reads used for validation
case class Foo(name: String, link: String)
val fooReads = (
(__ \ "name").read[String] ~
(__ \ "link").read[String]
)(Foo.apply _)
I have changed the model to allow multiple links instead of one so i can expect array of strings in the input JSON:
case class Foo(name: String, link: Set[String])
val fooReads = (
(__ \ "name").read[String] ~
(__ \ "link").read[Set[String]] // it works this way
)(Foo.apply _)
I want to make it back compatible so it can handle JSON value of "link" with string along with array of strings
You can use the operator orElse on json Reads to create your custom reader:
val readsSet: Reads[Set[String]] = Reads.of[Set[String]].orElse(Reads.of[String].map(str => Set(str)))
val fooReads = (
(__ \ "name").read[String] ~
(__ \ "link").read[Set[String]](readsSet)
)(Foo.apply _)
You can use Reads.orElse combinator:
val fooReads: Reads[Foo] = (
(__ \ "name").read[String] ~
(__ \ "link").read(Reads.of[String].map(str => Set(str)).
orElse(Reads.of[Set[String]]))
)(Foo.apply _)

Json format with Play 2.4 / Scala

I know this question has been asked thousand times but I can't figure why this code cannot work properly.
case class Partner
(_id: Option[BSONObjectID], name: String, beacon: Option[Beacon])
class PartnerFormatter #Inject() (val beaconDao: BeaconDao){
implicit val partnerReads: Reads[Partner] = (
(__ \ "_id").readNullable[String]and
(__ \ "name").read[String] and
(__ \ "beacon").read[String]
)((_id, name, beaconID) => Partner(_id.map(BSONObjectID(_)), name, Await.result(beaconDao.findById(beaconID), 1 second))))
implicit val partnerWrites: Writes[Partner] = (
(JsPath \ "_id").writeNullable[String].contramap((id: Option[BSONObjectID]) => Some(id.get.stringify)) and
(JsPath \ "name").write[String] and
(JsPath \ "beacon").writeNullable[String].contramap((beacon: Option[Beacon]) => Some(beacon.get._id.get.stringify))
)(unlift(Partner.unapply))
}
And I'm facing
No Json deserializer found for type models.Partner. Try to implement an implicit Reads or Format for this type
Or
No Json deserializer found for type models.Partner. Try to implement an implicit Writes or Format for this type
Shouldn't it be working ?
You need to define the readers and writers in Partner object.
This is an example, https://github.com/luongbalinh/play-mongo/blob/master/app/models/User.scala

Custom Json Writes with combinators - not all the fields of the case class are needed

I'm trying to write a custom Json serializer in play for a case class but I don't want it to serialize all the fields of the class. I'm pretty new to Scala, so that is surely the problem but this is what I tried so far:
case class Foo(a: String, b: Int, c: Double)
Now the default way of doing this, as far as I saw in the examples is:
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
(__ \ "c").write[Double]
) (unlift(Foo.unapply))
But what if I want to omit "c" from the Json output? I've tried this so far but it doesn't compile:
implicit val fooWritesAlt: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
) (unlift({(f: Foo) => Some((f.a, f.b))}))
Any help is greatly appreciated!
If you are using Playframework 2.2 (not sure about earlier versions, but it should work as well) try this:
implicit val writer = new Writes[Foo] {
def writes(foo: Foo): JsValue = {
Json.obj("a" -> foo.a,
"b" -> foo.b)
}
}
What I usually do is convert a field to None and use writeNullable on it:
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int]
(__ \ "c").writeNullable[Double].contramap((_: Double) => None)
) (unlift(Foo.unapply))
Instead of specifying a combinator which produces an OWrites instance, one can directly construct an OWrites just as well:
val ignore = OWrites[Any](_ => Json.obj())
implicit val fooWrites: Writes[Foo] = (
(__ \ "a").write[String] and
(__ \ "b").write[Int] and
ignore
) (unlift(Foo.unapply))
This will ignore any value of the case class at that position and simply always return an empty JSON object.
This is very easy to do with the Play's JSON transformers:
val outputFoo = (__ \ 'c).json.prune
and then apply transform(outputFoo) to your existing JsValue:
val foo: Foo
val unprunedJson: JsValue = Json.toJson(foo)
unprunedJson.transform(outputFoo).map { jsonp =>
Ok(Json.obj("foo" -> jsonp))
}.recoverTotal { e =>
BadRequest(JsError.toFlatJson(e))
}
see here http://mandubian.com/2013/01/13/JSON-Coast-to-Coast/
What version of Play are you using? fooWritesAlt compiles for me using 2.1.3. One thing to note is that I needed to explicitly use the writes object to write the partial JSON object, i.e.
fooWritesAt.writes(Foo("a", 1, 2.0))
returns
{"a": "a", "b": 2}

setting default values with Play! Json Combinators

I'm using play!'s json combinators to validate, read and write JSON. Is it possible to specify default values in reads or writes if they are not set?
validation of json is done like this (where json is a JsValue):
json.validate[Pricing]
My code is:
case class Pricing(
_id: ObjectId = new ObjectId,
description: String,
timeUnit: TimeUnit.Value,
amount: Double = 0.0) {
#Persist val _version = 1
}
my reads and writes:
implicit val pricingReads: Reads[Pricing] = (
(__ \ "_id").read[ObjectId] and
(__ \ "description").read[String] and
(__ \ "timeUnit").read[TimeUnit.Value] and
(__ \ "amount").read[Double]
)(Pricing.apply _)
implicit val pricingWrites: Writes[Pricing] = (
(__ \ "_id").write[ObjectId] and
(__ \ "description").write[String] and
(__ \ "timeUnit").write[TimeUnit.Value] and
(__ \ "amount").write[Double]
)(unlift(Pricing.unapply))
so if I would recieve a Json like:
{"description": "some text", "timeUnit": "MONTH"}
I get errors, that fields _id and amount are missing. Is there any possiblity to set the default values without adding it directy to the JsValue?
Thanks in advance!
I'd rather use Options:
case class Pricing(
_id: Option[ObjectId],
description: String,
timeUnit: TimeUnit.Value,
amount: Option[Double]) {
#Persist val _version = 1
}
and replace your pricingReads with this:
implicit val pricingReads: Reads[Pricing] = (
(__ \ "_id").readNullable[ObjectId] and
(__ \ "description").read[String] and
(__ \ "timeUnit").read[TimeUnit.Value] and
(__ \ "amount").readNullable[Double]
)(Pricing.apply _)
Then your code will work on missing fields and yo will be able to do this:
_id.getOrElse(new ObjectId)

Play Framework 2.1.1 Json Writes nested objects

I've been trying to get my head around this all afternoon to no avail, Play 2.1.1 overhauled how the Json reads and writes work.
Basically I have a wrapper object that looks like:
case class CombinedUser(user: SocialUser, userdetails: UserDetails)
as you can see it contains 2 classes that I want to serialize and deserialize to json.
But I don't understand how I can get and set the fields in the subclasses using the new design.
For example
implicit val combinedUser2Reads = (
(__ \ "email").read[String] and
(__ \ "providerid").read[String] and
(__ \ "firstname").read[String] and
(__ \ "lastname").read[String] and
(__ \ "fullname").read[String] and
(__ \ "avatarurl").read[String] and
(__ \ "address1").read[String] and
(__ \ "address2").read[String] and
(__ \ "address3").read[String] and
(__ \ "city").read[String] and
(__ \ "country").read[String] and
(__ \ "phone").read[String] and
(__ \ "publickey").as[String]
)(CombinedUser2.apply _)
I want a json blob that has most of the subclasses fields, all strings.
Because it uses the apply stuff, I don't see how I can create the subclasses before the mapping.
Any help or guidance is much appreciated.
Thanks
Tom
You can do it this way:
case class SocialUser(firstName: String, lastName: String)
case class UserDetails(avatarUrl: String, phone: String)
case class CombinedUser(user: SocialUser, userDetails: UserDetails)
implicit val combinedUserReads: Reads[CombinedUser] = (
(__ \ "user").read((
(__ \ "firstName").read[String] and
(__ \ "lastName").read[String]
)(SocialUser)) and
(__ \ "userDetails").read((
(__ \ "avatarUrl").read[String] and
(__ \ "phone").read[String]
)(UserDetails))
)(CombinedUser)
However its better to create separated Reads:
implicit val socialUserReads = (
(__ \ "firstName").read[String] and
(__ \ "lastName").read[String]
)(SocialUser)
implicit val userDetailsReads = (
(__ \ "avatarUrl").read[String] and
(__ \ "phone").read[String]
)(UserDetails)
implicit val combinedUserReads: Reads[CombinedUser] = (
(__ \ "user").read[SocialUser] and
(__ \ "userDetails").read[UserDetails]
)(CombinedUser)
Edit: for simple case classes, its possible to do:
implicit val socialUserReads = Json.format[SocialUser]
implicit val userDetailsReads = Json.format[UserDetails]
implicit val combinedUserReads = Json.format[CombinedUser]
Here is quite comprehensive introduction to JSON Reads and more.
How about partial objects? If I don't want to fill in every field in
the constructor, can I pass empties or do I overload the constructor
or similar?
Use Option:
case class CombinedUser(user: SocialUser, userDetails: Option[UserDetails])
//reads
implicit val combinedUserReads: Reads[CombinedUser] = (
(__ \ "user").read[SocialUser] and
(__ \ "userDetails").readOpt[UserDetails]
)(CombinedUser)
//writes
implicit val combinedUserWrites: Writes[CombinedUser] = (
//socialUserWrites and userDetailsWrites must be in scope
(__ \ "user").write[SocialUser] and
(__ \ "userDetails").write[Option[UserDetails]]
)(unlift(CombinedUser.unapply))
val json = Json.obj(
"user" -> Json.obj(
"firstName" -> "Homer",
"lastName" -> "Simpson"
)
)
Json.fromJson[CombinedUser](json)
//JsSuccess(CombinedUser(SocialUser(Homer,Simpson),None),)