I am trying to create a proper OFormat that works correctly for encode and decode.
Here is the situation: I have this endpoint giving me JSON. One of the fields sometimes look like an integer. Sometimes it looks like an integer in a string. Sometimes it looks like random string. Sometimes the key/value pair is missing all together. So I created a custom decoder to handle those 4 cases. And that works great. But I need a custom encoder to match. And that I have not been able to figure out.
Here is my custom serde:
package object Example {
implicit class JsPathOps(jsPath: JsPath) {
def forceReadNullableInt: Reads[Option[Int]] =
jsPath.readNullable[Int] or
jsPath.readNullable[String]
.map(_.flatMap(string => Try(string.toInt).toOption))
def forceFormatNullableInt: OFormat[Option[Int]] =
OFormat(forceReadNullableInt, JsPath.writeNullable[Int])
}
}
Here is an example representative case class:
case class User(name: String, age: Option[Int])
object User {
implicit val format: Format[User] =
(
(JsPath \ "name").format[String] and
(JsPath \ "age").forceFormatNullableInt
)(
User.apply,
unlift(User.unapply)
)
}
And here is an object with main to try it out:
object App {
def main(args: Array[String]): Unit = {
val json1in: String = """{"name": "abc"}"""
val json2in: String = """{"name": "def", "age": 123}"""
val user1: User = Json.parse(json1in).as[User]
val user2: User = Json.parse(json2in).as[User]
val json1out: String = Json.toJson(user1).toString
val json2out: String = Json.toJson(user2).toString
println(s"1 in = ${json1in}")
println(s"1 out = ${json1out}")
println(s"1 match = ${json1in == json1out}")
println(s"2 in = ${json2in}")
println(s"2 out = ${json2out}")
println(s"2 match = ${json2in == json2out}")
}
}
Both of the decodes work great. But when it gets to the encoding of user2, I get the error java.lang.RuntimeException: when empty JsPath, expecting JsObject. Not sure what I am doing wrong. Nothing I have tried works.
How do I create a proper OWrites to match the Reads in my OFormat?
I am using Scala 2.12 and Play 2.6, if that makes any difference.
It's worth remembering that
OWrites[T]is ultimately just a function from aTto aJsValue. There are some DSLs and macros to make common cases easy, but you can always fall back to its "essence":