jsd*_*sdw 9 haskell aeson haskell-lens
我一直在玩Aeson和镜头包(镜头 - aeson,从核心镜头包中迁移),并且一直在努力让它们一起工作.
作为一个玩具示例,我有一个类型:
data Colour = Yellow | Green | Blue
Run Code Online (Sandbox Code Playgroud)
和FromJSON实例:
instance FromJSON Colour where
parseJSON (String s) = return $ case s of
"blue" -> Blue
"green" -> Green
_ -> Yellow
parseJSON _ = mzero
Run Code Online (Sandbox Code Playgroud)
到现在为止还挺好.
现在,假设我有一些嵌套的JSON数据,我想从中提取出来:
{
"info": {
"colour": "yellow"
},
/* other props */
}
Run Code Online (Sandbox Code Playgroud)
我不关心其余的,只有这种"颜色"的价值.更糟糕的是,让我们说JSON并不是特别一致,所以有时我也会这样
{ "item": { "colour": "yellow" } }
Run Code Online (Sandbox Code Playgroud)
和其他时间
{ "random": {"item_colour": "yellow"} }
Run Code Online (Sandbox Code Playgroud)
我希望能够尽可能轻松地获取颜色值,然后理想地使用我的FromJSON实例将其理想地解析为颜色.这是一个玩具示例,但数据类型可能包含许多字段等,而不是Color.
我开始看着镜头 - 艾森的东西,这让我有了希望; 它允许非常容易地窥视JSON结构.例:
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
Just (String "yellow")
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _String
Just "yellow"
Run Code Online (Sandbox Code Playgroud)
但我无法通过我的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
Run Code Online (Sandbox Code Playgroud)
我最接近解决的是重新编码然后解码上面的结果,例如:
> encode $ "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
"\"yellow\""
Run Code Online (Sandbox Code Playgroud)
这让我回想起我想要的JSON编码数据.在一个更复杂的情况下,如果该数据是一个对象或数组,我可以decode在它上面运行,因为我通常会回到我更复杂的类型,但解码不喜欢不正确的JSON; 不包含在数组或对象语法中的东西.此外,做解码然后编码看起来非常混乱,并不是非常高效.
我对镜头和Aeson整体都是新手(而Haskell就此而言,虽然我已经逐渐理解了像monads/applicatives这样的东西,所以慢慢地取得了进步!).你们将如何完成这项工作?
我的一般动机是我将使用大量的JSON数据,但实际上只关心它的片段,因此每次我需要从JSON中的不同位置获取这些片段时,我们宁愿避免声明数据类型,而只是声明我关心的位的类型.
请注意,我使用镜头-aeson-1和镜头4.4.0.1,而不是使用有点不同的aeson镜头(可能与答案相关)!
提前致谢!詹姆士
你可以使用_JSON棱镜.您只需要编写一个ToJSON实例Colour:
instance ToJSON Colour where
toJSON Yellow = String "yellow"
toJSON Blue = String "blue"
toJSON Green = String "green"
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
parseColour :: String -> Maybe Colour
parseColour j = j ^? key "info" . key "colour" . _JSON
-- point-free: parseColor = preview $ key "info" . key "colour" . _JSON
-- In GHCi
?: parseColour "{ \"info\": { \"colour\": \"yellow\" } }"
Just Yellow
Run Code Online (Sandbox Code Playgroud)