使用lens向嵌套的Map中添加键和值

13

我正在努力解决使用Aeson lenses操作JSON时遇到的问题。我的任务很简单,只需向JSON中嵌套的对象添加一个键即可。我已经通过以下方式成功更改了现有的键:

> :set -XOverloadedStrings
> import Control.Lens
> import Data.Aeson
> import Data.Aeson.Lens
> "{ \"a\": { \"b\": 10 } }" & key "a" . key "b" .~ String "jee"
"{\"a\":{\"b\":\"jee\"}}"

但是当我尝试让它处理新的键时,它只是默默地失败了,没有添加它:

> "{ \"a\": { \"b\": 10 } }" & key "a" . key "c" .~ String "jee"   
"{\"a\":{\"b\":10}}"

我肯定是做错了什么事情,但是我想我已经没有法力理解到底出了什么问题。

您能向我指出正确的方向吗?

谢谢!

2个回答

20

正如dfeuer所指出的那样,at 可以将元素插入到映射表中,而 keyix 则只是在元素存在时遍历这些元素。我们可以这样做:

> "{ \"a\": { \"b\": 10 } }" & key "a" . _Object . at "c" ?~ String "foo"
"{\"a\":{\"b\":10,\"c\":\"foo\"}}

at 是一个聚焦于 Maybe element 的镜头,我们可以通过设置为 Just 的某个元素来进行插入,也可以通过设置为 Nothing 来进行删除。 at "c" ?~ String "foo" 就相当于 at "c" .~ Just (String "foo")

如果我们想要进行嵌套插入,就可以使用 non 来定义要插入的默认值:

> "{ \"a\": { \"b\": 10 } }" & key "a" . _Object . at "c" . non (Object mempty) . _Object . at "d" ?~ String "foo"
"{\"a\":{\"b\":10,\"c\":{\"d\":\"foo\"}}}"

这句话比较长,我们可以将其中一些部分拆分出来:

> let atKey k = _Object . at k
> "{ \"a\": { \"b\": 10 } }" & key "a" . atKey "c" . non (Object mempty) . atKey "d" ?~ String "foo"

感谢您为我的案例提供了一个可行的示例和额外的解释! - SkyWriter
在这种情况下,set 在运行时可能是无意义的,因此(一个版本的).~/set 返回 Maybe 是有意义的。 - Ari Fordsham

3
key 基于 ix,其文档表明它不足以完成您想要的操作,并指向 Control.Lens.At.at。我非常确定这应该对您有帮助。基本思路是从 _Object prism 开始将 JSON 文本转换为对象,然后使用 at key 获取该字段的镜头,作为 Maybe。然后您可以将其更改为您想要的内容,即 Just
只要您希望沿着路径前进的所有对象都存在,这样做就可以很好地工作。如果您希望(可能)从零开始创建一系列单字段对象的链,您可能会发现事情更加繁琐。幸运的是,您可能不需要这样做。

谢谢你指出key在我的情况下为什么不起作用的原因。我真的尝试查看源代码,但它甚至更加让人困惑 :-) 猜想需要时间来理解这个问题。 - SkyWriter
@SkyWriter,如果您熟悉模型/视图术语,您可以将_Object视为提供Text模型的完整Object“视图”,但仅当该Text成功解析为Object时。假设视图成功,您可以通过该视图检查和修改Text模型。 - dfeuer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接