3

I have such an ugly part of code:

request <- parseUrl "someUrl.com"
res <- withManager $ httpLbs request
case decode $ responseBody res :: Maybe Aeson.Value of
  Just (Aeson.Object jsonObject) ->  
    case (HashMap.lookup "aaa" jsonObject) of
      Just a ->
        case a of 
          Aeson.Object jsonObject2 ->
            case (HashMap.lookup "bbb" jsonObject2) of
              Just b ->
                case b of
                  Aeson.Object jsonObject3 ->
                    case (HashMap.lookup "ccc" jsonObject3) of
                      Just c -> 
                        case c of
                          Array d -> 
                            case (d ! 1) of
                              String a  -> print a
                              _ -> error "error!!!"
                          _ -> error "error!!"
                      _ -> error "error!!"
                  _ -> error "error!!" 
              _ -> error "error!!"
          _ -> error "error!!"
      _ -> error "error!!"

_ -> error "Invalid JSON"

It does work well but it doesn't look good. How do I simplify it? I'm positive there is a way to do this. Note that I'm not using any custom data type for parsing a JSON and don't want to.

2 Answers 2

8

This problem—producing deep dives into large, complex data structures, is exactly what lens seeks to solve. The idea of Lenses isn't tough, but the package lens can be scary. I've written a few tutorials on this concept, however, which may be useful

In any case, for your particular example, we can replace this nested case with something like

print $ responseBody res ^!? key "aaa" 
                         .   key "bbb"
                         .   key "ccc"
                         .   ix 1 
                         .   _String

though I would recommend avoiding things like error to indicate failure. The (^?) combinators (also called preview) would help.

Sign up to request clarification or add additional context in comments.

Comments

5

do syntax for Maybe monad:

request <- parseUrl "someUrl.com"
res <- withManager $ httpLbs request
let toPrint =
  do Aeson.Object jsonObject <- decode $ responsBody res
     Aeson.Object jsonObject2 <- HashMap.lookup "aaa" jsonObject
     Aeson.Object jsonObject3 <- HashMap.lookup "bbb" jsonObject2
     Array d <- HashMap.lookup "ccc" jsonObject3
     String a <- return $ d ! 1
     return a
case toPrint of
    Just a -> print a
    Nothing -> "Invalid JSON"

2 Comments

I think the second last line of the do can be let (String a) = d ! 1 rather than cycling it through a Maybe.
Well, here we specifically want fail function to be called if pattern doesnt't match. So, return it is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.