Reputation: 15783
Let's say I have a class like this:
abstract class SomeSuperClass(name: String)
case class SomeClass(someString: String, opt: Option[String]) extends SomeSuperClass("someName")
I want to serialize this class and be able to add the name
field, this was my first approach:
implicit def serialize: Writes[SomeClass] = new Writes[SomeClass] {
override def writes(o: SomeClass): JsValue = Json.obj(
"someString" -> o.someString,
"opt" -> o.opt,
"name" -> o.name
)
}
This returns null
if there's a None
, so I changed my implementation following the documentation to this:
implicit def serialize: Writes[SomeClass] = (
(JsPath \ "someString").write[String] and
(JsPath \ "opt").writeNullable[String] and
(JsPath \ "name").write[String]
)(unlift(SomeClass.unapply))
This doesn't compile, it works only if I remove the name field:
[error] [B](f: B => (String, Option[String], String))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B] <and>
[error] [B](f: (String, Option[String], String) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.OWrites])play.api.libs.json.OWrites[B]
[error] cannot be applied to (api.babylon.bridge.messaging.Command.SomeClass => (String, Option[String]))
[error] (JsPath \ "opt").writeNullable[String] and
How can I add a field which is not strictly present in a case class and has an optional field?
I'm using play-json 2.3.0.
Upvotes: 2
Views: 1007
Reputation: 8901
Instead of using the default (compiler generated) unapply method, which knows nothing about your inherited values, you can write your own extractor for the JSON writes which takes a SomeClass
instance and returns a (String, Option[String], String)
tuple, i.e:
implicit def serialize: Writes[SomeClass] = (
(JsPath \ "someString").write[String] and
(JsPath \ "opt").writeNullable[String] and
(JsPath \ "name").write[String]
)(s => (s.someString, s.opt, s.name))
This gives you:
Json.toJson(SomeClass("foo", None))
// {"someString":"foo","name":"someName"}
Jspm.toJson(SomeClass("foo", Some("bar")))
// {"someString":"foo","opt":"bar","name":"someName"}
Upvotes: 4