Say I have a JSON ByteString that looks something like
{
messages: [
{...},
{...}
]
}
I'd like to use lens to get a list/vector of messages out of it. I have a function toMessage
that can turn a Value
into a Maybe Message
.
I've tried this composition key "messages" . values . to toMessage
(to
is from Control.Lens.Getter
but the result is Maybe Message
and it simply becomes Nothing
.
Currently I'm doing this
msgsJson <- c ^? key "messages"
let msgs = toList $ mapMaybe message $ msgsJson ^.. values
(mapMaybe
is from witherable
, toList
is to convert the Vector
into a list)
but I'd like to know if there's a way to compose various lenses to get a single lens that does this.
Hmm, this works for me:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Data.ByteString (ByteString)
import Control.Lens
import Data.Aeson
import Data.Aeson.Lens
newtype Message =
Message Integer
deriving (Show)
toMessage :: Value -> Maybe Message
toMessage json_ = do
i <- json_ ^? key "a" . _Integer
return (Message i)
input :: ByteString
input = "{\"messages\":[{\"a\":1},{\"a\":2},{\"a\":3}]}"
main :: IO ()
main =
print (input ^.. (key "messages" . values . to toMessage))
λ> main
[Just (Message 1),Just (Message 2),Just (Message 3)]
If you're getting Nothing
, it could mean that your JSON is invalid (you can test it with decode json :: Maybe Value
). Or that any other optic in the composition is failing. With long dotted optics it's sometimes hard to tell exactly which one is failing except by lopping parts off the end and retrying. But your composed optic key "messages" . values . to toMessage
should Just Work™.
By the way:
msgJson <- c ^? key "messages"
let msgs = toList $ mapMaybe message $ msgsJson ^.. values
should be the same as
msgJson <- c ^? key "messages"
let msgs = toList $ msgsJson ^.. (values . to message . _Just)
and
msgJson <- c ^? key "messages"
let msgs = msgsJson ^.. (values . to message . _Just)
and
let msgs = c ^.. (key "messages" . values . to message . _Just)