Search code examples
haskellaeson

How can I decode JSON using a custom `parseJSON` - a function rather than the function related to the instance for `fromJSON`?


This function:

eitherDecode :: FromJSON a => ByteString -> Either String a

Has a small limitation that I can't have an additional implementation of a decode that is NOT the one from FromJSON a.

In other words I'm looking for some way to pass my own Bytestring -> Either String a parsing function.


Okay... So I'll have to define my own function for this it seems.

It's defined as:

-- | Like 'decode' but returns an error message when decoding fails.
eitherDecode :: (FromJSON a) => L.ByteString -> Either String a
eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON

Looks like ifrom is what I need to modify which is defined as:

-- | Convert a value from JSON, failing if the types do not match.
ifromJSON :: (FromJSON a) => Value -> IResult a
ifromJSON = iparse parseJSON

Well eitherFormatError is not exported from Aeson so this basically seems like I might be going down the wrong approach.


Solution

  • After a bit of type juggling...

    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    module AesonExtra where
    
    import Data.Aeson
    import Data.Aeson.Types
    import qualified Data.ByteString.Lazy as L
    import qualified Data.HashMap.Strict as HashMap
    import Data.Foldable (toList)
    import Data.String.Conversions
    import Data.Text (Text)
    
    eitherDecodeCustom :: (Value -> Parser a) -> L.ByteString -> Either String a
    eitherDecodeCustom f x = do 
      xx <- eitherDecode x :: Either String Value
      parseEither f xx