I am trying to implement a function writing Doubles that might have a value of Infinity (which does not exist in JSON).
Here are some examples of what I am trying to achieve:
Input: Double.PositiveInfinity
Output:
{
"positiveInfinty": true,
"negativeInfinty": false,
"value": null
}
Input: 12.3
Output:
{
"positiveInfinty": false,
"negativeInfinty": false,
"value": 12.3
}
So far, I have created an enhanced JsPath class, and added my function named writeInfinite
:
case class EnhancedJsPath(jsPath: JsPath) {
private def infinityObject(pos: Boolean, neg: Boolean, value: Option[Double]): JsObject = Json.obj(
"positiveInfinity" -> pos,
"negativeInfinity" -> neg,
"value" -> value
)
def writeInfinite: OWrites[Double] = OWrites[Double] { d =>
if (d == Double.PositiveInfinity) { infinityObject(true, false, None) }
else if (d == Double.NegativeInfinity) { infinityObject(false, true, None) }
else { infinityObject(false, false, Some(d)) }
}
}
object EnhancedJsPath {
implicit def jsPathToEnhancedJsPath(jsPath: JsPath): EnhancedJsPath = EnhancedJsPath(jsPath)
}
The code all compiles, and here is the test I am using :
case class Dummy(id: String, value: Double)
object Dummy {
implicit val writes: Writes[Dummy] = (
(JsPath \ "id").write[String] and
(JsPath \ "value").writeInfinite
)(unlift(Dummy.unapply))
}
test("EnhancedJsPath.writesInfinite should set positive infinity property") {
val d = Dummy("123", Double.PositiveInfinity, Some(Double.PositiveInfinity))
val result = Json.toJson(d)
val expected = Json.obj(
"id" -> "123",
"value" -> Json.obj(
"positiveInfinity" -> true,
"negativeInfinity" -> false,
"value" -> JsNull
)
)
assert(result == expected)
}
The test is failing because the value of result
is:
{
"id": "123",
"positiveInfinity": true,
"negativeInfinity": false,
"value": null
}
Instead of:
{
"id": "123",
"value": {
"positiveInfinity": true,
"negativeInfinity": false,
"value": null
}
}
I can't figure out how to modify my writeInfinite
to respect the path.
Taking a look at JsPath#write[A]
or JsPath#writeNullable[A]
helps:
def write[T](implicit w: Writes[T]): OWrites[T] = Writes.at[T](this)(w)
Writes.at
accepts a JsPath
, which is how write
and writeNullable
preserves the path they are called on. All you need to do is wrap your current implementation of writeInfinite
with it, passing the jsPath
value along:
def writeInfinite: OWrites[Double] = Writes.at(jsPath)(OWrites[Double] { d =>
if (d == Double.PositiveInfinity) infinityObject(true, false, None)
else if (d == Double.NegativeInfinity) infinityObject(false, true, None)
else infinityObject(false, false, Some(d))
})
And it works:
scala> Json.toJson(d)
res5: play.api.libs.json.JsValue = {"id":"123","value":{"positiveInfinity":true,"negativeInfinity":false,"value":null}}