我一直在使用 Aeson 和镜头包(lens-aeson,从核心镜头包迁移而来),并且一直在努力让它们一起工作。
作为一个玩具示例,我有一个类型:
data Colour = Yellow | Green | Blue
和 FromJSON 实例:
instance FromJSON Colour where
parseJSON (String s) = return $ case s of
"blue" -> Blue
"green" -> Green
_ -> Yellow
parseJSON _ = mzero
到目前为止,一切都很好。
现在,假设我有一些嵌套的 JSON 数据,我想从中提取这些数据:
{
"info": {
"colour": "yellow"
},
/* other props */
}
我不关心其余的,只关心这个“颜色”值。更糟糕的是,假设 JSON 不是特别一致,所以有时我会
{ "item": { "colour": "yellow" } }
和其他时间
{ "random": {"item_colour": "yellow"} }
我希望能够尽可能轻松地获取颜色值,然后使用我的 FromJSON 实例将其理想地解析为颜色。这是一个玩具示例,但数据类型可能具有多个字段等,而不是颜色。
我开始关注lens-aeon的东西,这让我燃起了希望;它允许非常轻松地查看 JSON 结构。例子:
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
Just (String "yellow")
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _String
Just "yellow"
但我找不到一种方法来通过我的 parseJSON 调用来运行它以返回Just Yellow
。 parseJSON 看起来很接近,因为它采用正确的类型(可能是至少内部的东西),但之后就崩溃了。理想情况下,我能够执行以下操作之一:
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _ParseJSON :: Maybe Colour
Just Yellow
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _Colour
Just Yellow
我最接近弄清楚的方法是重新编码然后解码上述结果,例如:
> encode $ "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
"\"yellow\""
它返回了我想要的 JSON 编码数据。在更复杂的情况下,如果该数据是对象或数组,我可以运行decode
就像我通常会返回更复杂的类型一样,但解码不喜欢不正确的 JSON;没有包含在数组或对象语法中的东西。此外,先解码再编码似乎非常混乱并且性能不佳。
我对镜头和 Aeson 整体来说相当陌生(对于这个问题,还有 Haskell,尽管我已经开始理解诸如 monads/applicatives 之类的东西,但进展缓慢!)。你们打算如何完成这件事?
我的总体动机是,我将处理大量 JSON 数据,但实际上只关心它的片段,因此每次需要从 JSON 中的不同位置获取这些片段时,我宁愿避免声明数据类型,而只是声明我关心的位的类型。
请注意,我使用的是lens-aeson-1和lens-4.4.0.1,而不是aeson-lens,它的工作方式略有不同(它可能与答案相关)!
提前致谢!
詹姆士