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", "[email protected]", "1-800-NOT-NULL")
Test.unapply(test) // returns Some(("John Sample", "[email protected]", "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 _))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With