如何使用 argonaut lens 修改 JSON 的值和类型?

How to modify value and type in JSON using argonaut lens?

假设以下简单 JSON 文档:

       {
         "key" : "val1"
       }

我想更新 "key" 的值,但同时也更改其类型,因此将其从字符串更改为 int。现在,使用像下面这样的 HCursor 就可以直接做到:

 val cursor = js.hcursor
 val position = (cursor --\ "key") >-> (_ => jNumber(1))

通过 "undoing" 上面的位置我最终得到一个新的 json 其中 "key" 有一个数值而不是一个字符串,这是完美的。

是否可以使用镜头来做同样的事情?我尝试执行以下操作:

val lense = jObjectPL >=>
          jsonObjectPL("key") >=>
          jNumberPL
lense.mod(_ => JsonBigDecimal(1), js)

但是虽然我没有收到错误,但它也不起作用,最后我得到了未修改的原始 json 文档。不过,如果我尊重数据类型,事情就会按预期进行。是否有理由将镜头仅用于修改相同的数据类型?或者我只是做了一些非常错误的事情:)

不,没有什么大错——你快完成了。问题是这条路径:

jObjectPL >=> jsonObjectPL("key") >=> jNumberPL

导航到 "key" 的 JSON 号码。你的 js 键上没有 JSON 数字,所以镜头没有指向任何东西,修改也不会影响任何东西。

您只需移除镜头的最后一步即可解决此问题:

val lens = jObjectPL >=> jsonObjectPL("key")

这只是导航到 "key" 字段,但不限制它是什么类型的 JSON 值。然后你可以把它改成任何你想要的:

scala> val lens = jObjectPL >=> jsonObjectPL("key")
lens: scalaz.PLensFamily[...

scala> lens.mod(_ => jNumber(JsonBigDecimal(1)), js)
res0: argonaut.Json = {"key":1}

请注意,由于镜头指向的是 Json 值,而不是 JsonNumber,因此您必须将 JsonBigDecimal 包裹在 jNumber 中才能使类型排队。