Luka Horvat
Luka Horvat

Reputation: 4402

Getting a collection of values from a JSON ByteString using lens-aeson

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.

Upvotes: 3

Views: 152

Answers (1)

hao
hao

Reputation: 10228

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)

Upvotes: 2

Related Questions