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

时间:2016-06-21 17:13:31

标签: json scala scalaz lenses argonaut

假设以下简单的JSON文档:

       {
         "key" : "val1"
       }

我想更新“key”的值,但同时也要更改其类型,因此从字符串更改为int。现在,使用类似下面的HCursor,可以直接进行:

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

通过“撤消”上面的位置,我最终得到一个新的json,其中“key”具有数值而不是String,这是完美的。

使用镜头可以做同样的事吗?我试着做以下事情:

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

但是虽然我没有收到错误但它也不起作用,最后我最终得到了原始的json文档未经修改。如果我尊重数据类型,那么事情就会发挥作用。有没有理由认为镜片只能用于修改相同的数据类型?或者我只是做了一件非常错误的事情:)

1 个答案:

答案 0 :(得分:4)

不,没有什么特别错误 - 你几乎就在那里。问题是这条道路:

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中以使其成为{{1}}类型排队。