Given the JsonExample in the monocle project, I would like to create a lens where a set call will either replace a value in a key/value pair, or create the key/value pair if it doesnt already exist.
However this seems to represented with either an index (which can compose type safe) or an at, which does not type safe
//for replacing:
(jsObject composeOptional index("age") composePrism jsNumber).set(45)
//for creating:
(jsObject composeLens at("age")).set(JsNumber(45)) <- will accept any old json
Is what I am after possible?
Also could I extend it, such that if age was nested in another JsObject, for example:
val n = (jsObject composeOptional index("nested") composePrism
jsObject composeOptional index("age") composePrism jsNumber).set(45)
Where the key/value pair for "nested" didnt yet exist, that it would create the object at nested and then add the field
n(JsObject(Map.empty)) -> JsObject(Map("nested" -> JsObject("age" -> JsNumber(45)))
let's have a look at index
and at
signature for JsObject
:
def at(field: String): Lens[JsObject, Option[Json]]
def index(field: String): Optional[JsObject, Json]
at
is a Lens
so its target ('Option[Json]') is always present. It means that we can add
, delete
and update
the Json
element at any field of a JsonObject
.
import argonaut._, Argonaut._
import monocle.function._
(jObjectPrism composeLens at("name")).set(Some(jString("John")))(Json())
> res0: argonaut.Json = {"name":"John"}
(jObjectPrism composeLens at("name")).set(Some(jString("Robert")))(res0)
> res1: argonaut.Json = {"name":"Robert"}
(jObjectPrism composeLens at("name")).set(None)(res0)
> res2: argonaut.Json = {}
On the other hand, index
is an Optional
so it is target (Json
) may or may not be there. It means that index
can only update
values but cannot add
or delete
.
(jObjectPrism composeLens index("name")).set(jString("Robert"))(Json())
> res3: argonaut.Json = {}
(jObjectPrism composeLens index("name")).set(jString("Robert"))(res0)
> res4: argonaut.Json = {"name":"Robert"}
So to come back to your original question, if you want to add
or update
value at a particular field, you need to use at
and wrap the Json
in a Some
(see res1
), it will overwrite or create the Json
at that field.