I'm very new to the magic of lenses, so I'm having some trouble with this.
With reference to: https://www.fpcomplete.com/user/tel/lens-aeson-traversals-prisms
a JSON object can be traversed in the following way:
val ^? nth 0 . key "someObject" . key "version" . nth 2
for a JSON object that resembles:
"[{\"someObject\": {\"version\": [1, 0, 3]}}]"
The Maybe monad is used throughout, so if any 'accessor' fails, I get a Nothing
.
I would like to propagate the failure too, so that I know what accessor failed.
The only way I can think of doing it would be pass an array of accessors, apply them sequentially, and return an error at any point that failed. Something like this:
import Data.Aeson
import Data.Text
import Data.Vector ((!?))
import qualified Data.HashMap.Strict as HM
data MyAccessor = Nth Int | Key Text
withFailure :: Value -> [MyAccessor] -> Either String Value
withFailure val [] = Right val
withFailure val (x:xs) = case x of
Nth i -> case val of
(Array val') -> case (val' !? i) of
Just e -> withFailure e xs
_ -> Left $ "Could not get index " ++ (show i)
_ -> Left $ "Expected JSON array for index " ++ (show i)
Key k -> case val of
(Object val') -> case (HM.lookup k val') of
Just e -> withFailure e xs
_ -> Left $ "Could not get key " ++ (unpack k)
_ -> Left $ "Expected JSON object for key " ++ (unpack k)
So with this:
-- val = [[1,0,3], {"name" : "value"}]
> withFailure val [Nth 1, Key "name", Key "hello"]
Left "Expected JSON object for key hello"
> withFailure val [Nth 1, Key "name"]
Right (String "value")
Is there a more elegant way of doing this? Making an Either-ish monad for lens to use, that results in like what withFailure
is?