I'm using Data.Aeson to parse some JSON into a Record type. From time to time data is added to the JSON and this breaks my code as Aeson complains something to the effect of:
expected Object with 21 name/value pairs but got 23 name/value
I'd really prefer to parse the JSON in a fault tolerant way -- I don't care if more fields are added to the JSON at a later date, just parse whatever you can! Is there a way to achieve this fault tolerance? Here's my code:
myRecordFromJSONString :: BS.ByteString -> Maybe MyRecord
myRecordFromJSONString s = case Data.Attoparsec.parse json s of
Done _rest res -> Data.Aeson.Types.parseMaybe parseJSON res
_ -> Nothing
I should add that I'm using deriveJSON from Data.Aeson.TH to generate the parsing code. If I write the FromJSON code manually it's fault tolerant but I'd like to not have to do that...
If you are using GHC 7.2 or 7.4, the new generics support in aeson
doesn't check for extra fields. I'm not sure if this is by design or not but we use it for the same reason.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson
import qualified Data.Aeson.Types
import Data.Attoparsec
import qualified Data.ByteString as BS
import Data.ByteString.Char8 ()
import GHC.Generics
data MyRecord = MyRecord
{ field1 :: Int
} deriving (Generic, Show)
instance FromJSON MyRecord
myRecordFromJSONString :: BS.ByteString -> Maybe MyRecord
myRecordFromJSONString s = case Data.Attoparsec.parse json s of
Done _rest res -> Data.Aeson.Types.parseMaybe parseJSON res
_ -> Nothing
main :: IO ()
main = do
let parsed = myRecordFromJSONString "{ \"field1\": 1, \"field2\": 2 }"
print parsed
Running this would fail with the TH derived instance due to 'field2' not existing in the record. The Generic
instance returns the desired result:
Just (MyRecord {field1 = 1})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With