Haskell:重用带有镜头、镜头-aeson 和嵌套 JSON 的 FromJSON 实例

标签 haskell haskell-lens aeson

我一直在玩 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-aeson的东西,这让我的希望大增。它允许非常容易地查看 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(它可能与答案相关)!

提前致谢!
詹姆士

最佳答案

您可以使用 _JSON 棱镜。你只需要写一个ToJSON Colour 的实例:

instance ToJSON Colour where
  toJSON Yellow = String "yellow"
  toJSON Blue   = String "blue"
  toJSON Green  = String "green"

然后你可以像这样使用它:
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

关于Haskell:重用带有镜头、镜头-aeson 和嵌套 JSON 的 FromJSON 实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25504231/

相关文章:

Haskell, Aeson - (ToJSON ByteString) 没有实例

list - 通过分布 [] 来简化代码

haskell - 仅在叶子上具有值的树

haskell - 提升(镜头)遍历来修复

json - 如何避免为 Data.Tree 创建孤立的 FromJSON 实例

haskell - 使用 Aeson 解析带有嵌套列表的列表

list - 如何在不多次遍历字符串的情况下跟踪字符串的多个属性?

Haskell - 定义函数

Haskell,控制.Lens : Couldn't match type ‘[Language.Haskell.TH.Syntax.Dec]’ with ‘Language.Haskell.TH.Syntax.Exp’

haskell - Haskell 中的镜头与 Clojure 中使用按键序列之间有哪些异同?