Question

I need to parse json API responses which don't have strict structure:

{
 response: { /* any object here */ }
}

How is it possible to write parseResponse which will leave ability to parse (or choose a Parser for it) for later use?

My last try is below. I don't like it because it doesn't allow to choose response type as Aeson's decode does.

data APIResponse =
  APIResponse { response :: Value } deriving (Show,Generic)

instance FromJSON APIResponse

parseResponse :: BC.ByteString -> Either String Value
parseResponse resp =
  case eitherDecode . BLC.fromStrict $ resp of
    Right x -> Right $ response x
    Left msg -> Left msg
Était-ce utile?

La solution

I like to think of Aeson as happening in two distinct steps, the parsing from ByteString -> Value and the mapping from FromJSON a => Value -> a. If you don't immediately know what the correct mapping is you can simply parse to a Value as you've done. Later at runtime when you decide the proper mapping you can do that later.

import qualified Data.HashMap.Strict as Hm

decodeResponse :: ByteString -> Either String Value
decodeResponse bs = do
  val <- eitherDecode bs
  case val of
    Object o -> case Hm.lookup "response" o of
                  Nothing   -> fail "Invalid structure, missing 'response' key"
                  Just resp -> return resp
    _ -> fail "Invalid structure, expected an object"

To perform a mapping without a parse, to convert a Value to a FromJSON a => a, you use the parse/parseEither/parseMaybe family and run the parser generated by parseJSON

mapValue :: FromJSON a => Value -> Either String a
mapValue = parseString parseJSON
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top