Json Scala object serialization in Play2.2.1 framework - json

So, I've just recently started learning Scala. Sorry for my incompetence in advance.
I tried to look up my answer on stackoverflow. I was able to find several related topics, but I didn't spot my problem.
I'm trying to send a json response based on a Scala object. I have an Action and I'm doing the following:
def oneCredential = Action {
val cred = Credential("John", "Temp", "5437437")
Ok(Json.toJson(cred))
}
I've created a case class and appropriate implicit Writes[T] for it
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.util._
case class Credential(name: String, account: String, password: String)
object Credential{
implicit val credentialWrites = (
(__ \ "name").write[String] and
(__ \ "account").write[String] and
(__ \ "password").write[String]
)(Credential)
}
When I'm trying to run this, I've the following error: "Overloaded method value [apply] cannot be applied to (models.Credential.type)". Also, I tried this
implicit val credentialWrites = (
(__ \ "name").write[String] and
(__ \ "account").write[String] and
(__ \ "password").write[String]
)(Credential.apply _)
Fail. The error: could not find implicit value for parameter fu: play.api.libs.functional.Functor[play.api.libs.json.OWrites]
Then this:
implicit val credentialWrites = (
(__ \ "name").writes[String] and
(__ \ "account").writes[String] and
(__ \ "password").writes[String]
)(Credential)
Another fail: "value writes is not a member of play.api.libs.json.JsPath Note: implicit value credentialWrites is not applicable here because it comes after the application point and it lacks an explicit result type". Right, I understood the first part of an error, but not the second.
Finally I found a shorthand solution:
implicit val credentialWrites = Json.writes[Credential]
With this I've got no errors and the code finally worked. I've found the solution on this blog. It's said that the shorthand form is exactly the same as the one with "writes" above. But this "long" form didn't work for me.
Why is shorthand version working, while the long one isn't? Can somebody explain this?
Thank you!
PS Scala version: 2.10.2

The definitions you've given would work for Reads, but Writes needs a different kind of argument at the end. Take the following example:
case class Baz(foo: Int, bar: String)
val r = (__ \ 'foo).read[Int] and (__ \ 'bar).read[String]
val w = (__ \ 'foo).write[Int] and (__ \ 'bar).write[String]
r can be applied to a function (Int, String) => A to get a Reads[A], which means we can use it as follows (these are all equivalent):
val bazReader1 = r((foo: Int, bar: String) => Baz(foo, bar))
val bazReader2 = r(Baz.apply _)
val bazReader3 = r(Baz)
What we're doing is lifting the function into the applicative functor for Reads so that we can apply it to our Reads[Int] and Reads[String] (but you don't need to care about that if you don't want to).
w takes a different kind of argument (again, you don't need to care, but this is because Writes has a contravariant functor—it doesn't have an applicative functor):
val bazWriter1 = w((b: Baz) => (b.foo, b.bar))
We could write this equivalently as the following:
val bazWriter2 = w(unlift(Baz.unapply))
Here we're using the case class's automatically generated extractor, unapply, which returns an Option[(Int, String)]. We know in this case that it'll always return a Some, so we can use unlift (which comes from the functional syntax package, and just calls the standard library's Function.unlift) to turn the Baz => Option[(Int, String)] into the required Baz => (Int, String).
So just change your final line to )(unlift(Credential.unapply)) and you're good to go.

Related

Scala PlayJson Cyclic Reference

Context
I have a case class which is an item in a hierarchy, which refers to itself like so:
case class Node(
name: String,
children: Option[Seq[Node]] = None
)
I would like a PlayJson Format for this.
Usually, you can just do:
implicit lazy val formatter = Json.format[MyCaseClass]
But this doesn't work.
Why?
PlayJson uses a Scala macro to produce a Format for the case class, it will go through all fields, when it gets to the field children it will look for an existing formatter for Node which it hasn't constructed yet, ending with a compilation error:
No implicit format for Option[Seq[Node]] available.
[error] implicit lazy val formatter = Json.format[Node]
Questions
What's the best way to approach this?
Is this a known issue with PlayJson format macro?
This is something that can be found under recursive types in play-json docs:
import play.api.libs.functional.syntax._
import play.api.libs.json.{Reads, Writes, _}
case class Node(name: String, children: Option[Seq[Node]] = None)
implicit lazy val nodeReads: Reads[Node] = (
(__ \ "name").read[String] and
(__ \ "children").lazyReadNullable(Reads.seq[Node](nodeReads))
)(Node)
implicit lazy val nodeWrites: Writes[Node] = (
(__ \ "name").write[String] and
(__ \ "children").lazyWriteNullable(Writes.seq[Node](nodeWrites))
)(unlift(Node.unapply))
Since in that case Reads and Writes are symmetrical, you can create the whole thing as a single Format:
implicit lazy val nodeFormat: Format[Node] = (
(__ \ "name").format[String] and
(__ \ "children").lazyFormatNullable(Reads.seq[Node](nodeFormat), Writes.seq[Node](nodeFormat))
)(Node.apply, unlift(Node.unapply))

JSON implicit Reads/Writes in Playframerowk with one element not working

Say I have to classes that I will be using it for read and write JSON
case class OrganizationData(var kind: Option[String],var id: Option[String],var organizationReference: OrganizationReferenceData,var objectHash: Option[String],var friendlyName: Option[String])
case class OrganizationReferenceData(var organizationId:String)
The following are the read and write
implicit val organizationWrites = (
(__ \ "kind").writeNullable[String] and
(__ \ "id").writeNullable[String] and
(__ \ "organizationReference").write[OrganizationReferenceData] and
(__ \ "objectHash").writeNullable[String] and
(__ \ "friendlyName").writeNullable[String]
) ( unlift( OrganizationData.unapply ) )
implicit val OrganizationReferenceDataWrites:Writes[OrganizationReferenceData] = (
(__ \ "organizationId").write[String]
) ( unlift( OrganizationReferenceData.unapply ) )
when I try to compile this I am getting the following error.
Error:(54, 34) overloaded method value write with alternatives:
(t: String)(implicit w: play.api.libs.json.Writes[String])play.api.libs.json.OWrites[play.api.libs.json.JsValue] <and>
(implicit w: play.api.libs.json.Writes[String])play.api.libs.json.OWrites[String]
cannot be applied to (config.core.OrganizationReferenceData => String)
(__ \ "organizationId").write[String]
^
Am I missing some thing here? One weird thing that I have seen is if I add another field in "OrganizationReferenceDataWrites" class and have the write element it compiles. SO if we cannot have a single element that what is the best practice to do it?
I'm not sure why you are getting that message, but an alternative way to do it may be:
implicit val OrganizationReferenceDataWrites = new Writes[OrganizationReferenceData] {
def writes(organizationReferenceData: OrganizationReferenceData) = Json.obj(
"organizationId" -> organizationReferenceData.organizationId)
}
Caveat: I don't know my combinators well enough to know if this is exactly equivalent.
BTW Are you using mutable (var) variables for a reason?

case class to json (partial) conversion

I have following case class:
case class Test(name: String, email: String, phone: String)
So in order to be able to serialize to JSON I wrote:
implicit val testWrites: Writes[Test] = (
(__ \ "name").write[String] and
(__ \ "email").write[String] and
(__ \ "phone").write[String]
)(unlift(Test.unapply))
I want to use this as something like DTO object, so I can exclude some fields while serizalizing.
Let's we say I want to show only name and email fields.
I tried something like this:
implicit val testWrites: Writes[Test] = (
(__ \ "name").write[String] and
(__ \ "email").write[String]
)(unlift(Test.unapply))
But this is giving me compile error -> Application does not take parameters.
Does anyone know what is the problem, and how I can achieve mentioned idea?
Play JSON combinators often take advantage of the unapply method that is automatically generated in the companion object of a case class.
For your case class:
case class Test(name: String, email: String, phone: String)
The unapply method looks like this:
def unapply(test: Test): Option[(String, String, String)] = Some((test.name, test.email, test.phone))
It returns the field values of the case class tupled and wrapped in Option. For example:
val test: Test = Test("John Sample", "fake#email.com", "1-800-NOT-NULL")
Test.unapply(test) // returns Some(("John Sample", "fake#email.com", "1-800-NOT-NULL"))
unlift transforms the unapply function into a PartialFunction[Test, (String, String, String)], which is then used to map an instance of Test to a tuple, which is then used to serialize the class.
You need not use Test.unapply. It's only convenient to use it when you want to serialize the entire class. If you only want some fields, you can define a similar function Test => Option[(String, String)]:
def simpleExtractor(test: Test): Option[(String, String)] = Some(test.name, test.email)
And then use it in the JSON combinators:
implicit val testWrites: Writes[Test] = (
(__ \ "name").write[String] and
(__ \ "email").write[String]
)(unlift(simpleExtractor))
Similarly, JSON Reads often takes advantage of the automatically generated apply method for case classes. Test.apply _ is a function (String, String, String) => Test-- basically the opposite as unapply, as you might have guessed. The JSON API assembles a tuple with the fields specified in the Reads, then passes that tuple through Test.apply _, which produces the deserialized Test.
To do generate a Reads that will only read two fields you could define another apply-like function:
def simpleBuilder(name: String, email: String): Test = Test(name, email, "default")
implicit val testReads: Reads[Test] = (
(__ \ "name").read[String] and
(__ \ "email").read[String]
)(unlift(simpleBuilder _))
Though personally I prefer not to do this, and define a default value within the Reads itself:
implicit val testReads: Reads[Test] = (
(__ \ "name").read[String] and
(__ \ "email").read[String] and
(__ \ "phone").read[String].orElse(Reads.pure("default"))
)(unlift(Test.apply _))

Defaults for missing properties in play 2 JSON formats

I have an equivalent of the following model in play scala :
case class Foo(id:Int,value:String)
object Foo{
import play.api.libs.json.Json
implicit val fooFormats = Json.format[Foo]
}
For the following Foo instance
Foo(1, "foo")
I would get the following JSON document:
{"id":1, "value": "foo"}
This JSON is persisted and read from a datastore. Now my requirements have changed and I need to add a property to Foo. The property has a default value :
case class Foo(id:String,value:String, status:String="pending")
Writing to JSON is not a problem :
{"id":1, "value": "foo", "status":"pending"}
Reading from it however yields a JsError for missing the "/status" path.
How can I provide a default with the least possible noise ?
(ps: I have an answer which I will post below but I am not really satisfied with it and would upvote and accept any better option)
Play 2.6+
As per #CanardMoussant's answer, starting with Play 2.6 the play-json macro has been improved and proposes multiple new features including using the default values as placeholders when deserializing :
implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
For play below 2.6 the best option remains using one of the options below :
play-json-extra
I found out about a much better solution to most of the shortcomings I had with play-json including the one in the question:
play-json-extra which uses [play-json-extensions] internally to solve the particular issue in this question.
It includes a macro which will automatically include the missing defaults in the serializer/deserializer, making refactors much less error prone !
import play.json.extra.Jsonx
implicit def jsonFormat = Jsonx.formatCaseClass[Foo]
there is more to the library you may want to check: play-json-extra
Json transformers
My current solution is to create a JSON Transformer and combine it with the Reads generated by the macro. The transformer is generated by the following method:
object JsonExtensions{
def withDefault[A](key:String, default:A)(implicit writes:Writes[A]) = __.json.update((__ \ key).json.copyFrom((__ \ key).json.pick orElse Reads.pure(Json.toJson(default))))
}
The format definition then becomes :
implicit val fooformats: Format[Foo] = new Format[Foo]{
import JsonExtensions._
val base = Json.format[Foo]
def reads(json: JsValue): JsResult[Foo] = base.compose(withDefault("status","bidon")).reads(json)
def writes(o: Foo): JsValue = base.writes(o)
}
and
Json.parse("""{"id":"1", "value":"foo"}""").validate[Foo]
will indeed generate an instance of Foo with the default value applied.
This has 2 major flaws in my opinion:
The defaulter key name is in a string and won't get picked up by a refactoring
The value of the default is duplicated and if changed at one place will need to be changed manually at the other
The cleanest approach that I've found is to use "or pure", e.g.,
...
((JsPath \ "notes").read[String] or Reads.pure("")) and
((JsPath \ "title").read[String] or Reads.pure("")) and
...
This can be used in the normal implicit way when the default is a constant. When it's dynamic, then you need to write a method to create the Reads, and then introduce it in-scope, a la
implicit val packageReader = makeJsonReads(jobId, url)
An alternative solution is to use formatNullable[T] combined with inmap from InvariantFunctor.
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val fooFormats =
((__ \ "id").format[Int] ~
(__ \ "value").format[String] ~
(__ \ "status").formatNullable[String].inmap[String](_.getOrElse("pending"), Some(_))
)(Foo.apply, unlift(Foo.unapply))
I think the official answer should now be to use the WithDefaultValues coming along Play Json 2.6:
implicit def jsonFormat = Json.using[Json.WithDefaultValues].format[Foo]
Edit:
It is important to note that the behavior differs from the play-json-extra library. For instance if you have a DateTime parameter that has a default value to DateTime.Now, then you will now get the startup time of the process - probably not what you want - whereas with play-json-extra you had the time of the creation from the JSON.
I was just faced with the case where I wanted all JSON fields to be optional (i.e. optional on user side) but internally I want all fields to be non-optional with precisely defined default values in case the user does not specify a certain field. This should be similar to your use case.
I'm currently considering an approach which simply wraps the construction of Foo with fully optional arguments:
case class Foo(id: Int, value: String, status: String)
object FooBuilder {
def apply(id: Option[Int], value: Option[String], status: Option[String]) = Foo(
id getOrElse 0,
value getOrElse "nothing",
status getOrElse "pending"
)
val fooReader: Reads[Foo] = (
(__ \ "id").readNullable[Int] and
(__ \ "value").readNullable[String] and
(__ \ "status").readNullable[String]
)(FooBuilder.apply _)
}
implicit val fooReader = FooBuilder.fooReader
val foo = Json.parse("""{"id": 1, "value": "foo"}""")
.validate[Foo]
.get // returns Foo(1, "foo", "pending")
Unfortunately, it requires writing explicit Reads[Foo] and Writes[Foo], which is probably what you wanted to avoid? One further drawback is that the default value will only be used if the key is missing or the value is null. However if the key contains a value of the wrong type, then again the whole validation returns a ValidationError.
Nesting such optional JSON structures is not a problem, for instance:
case class Bar(id1: Int, id2: Int)
object BarBuilder {
def apply(id1: Option[Int], id2: Option[Int]) = Bar(
id1 getOrElse 0,
id2 getOrElse 0
)
val reader: Reads[Bar] = (
(__ \ "id1").readNullable[Int] and
(__ \ "id2").readNullable[Int]
)(BarBuilder.apply _)
val writer: Writes[Bar] = (
(__ \ "id1").write[Int] and
(__ \ "id2").write[Int]
)(unlift(Bar.unapply))
}
case class Foo(id: Int, value: String, status: String, bar: Bar)
object FooBuilder {
implicit val barReader = BarBuilder.reader
implicit val barWriter = BarBuilder.writer
def apply(id: Option[Int], value: Option[String], status: Option[String], bar: Option[Bar]) = Foo(
id getOrElse 0,
value getOrElse "nothing",
status getOrElse "pending",
bar getOrElse BarBuilder.apply(None, None)
)
val reader: Reads[Foo] = (
(__ \ "id").readNullable[Int] and
(__ \ "value").readNullable[String] and
(__ \ "status").readNullable[String] and
(__ \ "bar").readNullable[Bar]
)(FooBuilder.apply _)
val writer: Writes[Foo] = (
(__ \ "id").write[Int] and
(__ \ "value").write[String] and
(__ \ "status").write[String] and
(__ \ "bar").write[Bar]
)(unlift(Foo.unapply))
}
This probably won't satisfy the "least possible noise" requirement, but why not introduce the new parameter as an Option[String]?
case class Foo(id:String,value:String, status:Option[String] = Some("pending"))
When reading a Foo from an old client, you'll get a None, which I'd then handle (with a getOrElse) in your consumer code.
Or, if you don't like this, introduce an BackwardsCompatibleFoo:
case class BackwardsCompatibleFoo(id:String,value:String, status:Option[String] = "pending")
case class Foo(id:String,value:String, status: String = "pending")
and then turn that one into a Foo to work with further on, avoiding to have to deal with this kind of data gymnastics all along in the code.
You may define status as an Option
case class Foo(id:String, value:String, status: Option[String])
use JsPath like so:
(JsPath \ "gender").readNullable[String]

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}