Haskell:将Lens,lens-aeson和嵌套JSON重用FromJSON实例 [英] Haskell: Reusing FromJSON instances with lenses, lens-aeson, and nested JSON

查看:84
本文介绍了Haskell:将Lens,lens-aeson和嵌套JSON重用FromJSON实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在与Aeson和镜头包装(lens-aeson,从核心镜头包装迁移而来)一起玩,并且一直在努力让他们一起工作.

I have been playing with Aeson and the lens package (lens-aeson, migrated from the core lens package), and have been sruggling to get them to work together.

作为一个玩具示例,我有一个类型:

As a toy example, I have a type:

data Colour = Yellow | Green | Blue

和FromJSON实例:

and the FromJSON instance:

instance FromJSON Colour where
    parseJSON (String s) = return $ case s of
        "blue" -> Blue
        "green" -> Green
        _ -> Yellow
    parseJSON _ = mzero

到目前为止一切都很好.

So far so good.

现在,说我有一些嵌套的JSON数据要从中提取出来:

Now, say I have some nested JSON data come in that I want to extract just this out of:

{
    "info": {
        "colour": "yellow"
    },
    /* other props */
}

我不在乎其余的,只在乎这个颜色"值.更糟糕的是,可以说JSON不是特别一致,所以有时候我有

I don't care about the rest, only this "colour" value. To make matters worse, lets say that the JSON isn't particularly consistent, so sometimes I have

{ "item": { "colour": "yellow" } }

和其他时间

{ "random": {"item_colour": "yellow"} }

我希望能够尽可能容易地获得颜色值,然后理想地使用我的FromJSON实例将其解析为理想的颜色.这是一个玩具示例,但是数据类型可能具有许多字段,而不是颜色,等等.

I want to be able to get at the colour value as easily as possible, and then parse it ideally using my FromJSON instance ideally into a colour. This is a toy example, but instead of Colour the data type might have a number of fields etc.

我开始看镜头原因的东西,这使我寄予了厚望.它允许非常容易地窥视到JSON结构.例如:

I started looking at the lens-aeson stuff and that got my hopes up; it allows very easy peering into a JSON structure. example:

> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
Just (String "yellow")
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _String
Just "yellow"

但是我找不到通过我的parseJSON调用来获取Just Yellow的方法. parseJSON似乎很接近,因为它采用了正确的类型(至少在里面),但是在那之后就崩溃了.理想情况下,我可以执行以下操作之一:

But I can't find a way to run that through my parseJSON call to get back Just Yellow. parseJSON seems close in that it takes the right type (thing inside the maybe as least) but then falls apart after that. Ideally I'd be able to do something like one of:

> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _ParseJSON :: Maybe Colour
Just Yellow
> "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour" . _Colour
Just Yellow

我要弄清楚的最接近的方法是重新编码,然后解码上述结果,例如:

The closest I've come to figuring it out is re-encoding then decoding the result of the above, eg:

> encode $ "{ \"info\": { \"colour\": \"yellow\" } }" ^? key "info" . key "colour"
"\"yellow\""

这使我得到了想要的JSON编码数据.在更复杂的情况下,如果该数据是对象或数组,则可以像往常一样取回我更复杂的类型,然后对它运行decode,但是解码不喜欢不正确的JSON.用数组或对象语法包装的东西.另外,先进行解码再进行编码似乎非常混乱,而且性能不高.

which gives me back the bit of JSON encoded data I want. In a more complex case, if that data was an object or array, I could then run decode on it as I would normally to get back my more complex type, but decode doesn't like improper JSON; things that arent wrapped in array or object syntax. In addition, doing decode then encode seems horribly messy and not very performant.

我对镜头和整个Aeson还是比较陌生的(就此而言,还包括Haskell,尽管我逐渐了解像monads/applicatives这样的东西,所以进展缓慢!).你们将如何完成这项工作?

I am rather new to lenses and Aeson as a whole (and Haskell for that matter, though I've come to understand things like monads/applicatives on the whole so slowly making progress!). How would you guys go about getting this done?

我的主要动机是,我将处理大量的JSON数据,但实际上只关心它的片段,因此宁愿避免每次我需要从不同位置取出这些片段时都声明数据类型. JSON,而是只声明我关心的位的类型.

My general motivation is that I will be working with a load of JSON data, but actually only care about fragments of it, and would thus rather avoid declaring a data type each time I need to get those fragments out from different places in the JSON, and instead just declare types for the bits I care about.

请注意,我使用的是lens-aeson-1和lens-4.4.0.1,而不是使用aeson-lens,后者的工作方式略有不同(可能与答案有关)!

Note that I am using lens-aeson-1 and lens-4.4.0.1, rather than aeson-lens which works a little differently (it might be relevant in answers)!

提前谢谢! 詹姆斯

推荐答案

您可以使用

You can use the _JSON prism. You only need to write a ToJSON instance for Colour:

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

然后您可以像这样使用它:

and then you can use it like this:

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:将Lens,lens-aeson和嵌套JSON重用FromJSON实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆