Haskell, Aeson - Is there a better way of parsing historical data? Haskell, Aeson - Is there a better way of parsing historical data? json json

Haskell, Aeson - Is there a better way of parsing historical data?


Put a Map in your data type. Aeson translates Map k vs to/from objects, where the vs are en-/de-coded via their own To-/From-JSON instances and the ks by To-/From-JSONKeys. It turns out that Day (from the time package) has perfectly suitable To-/From-JSONKey instances.

data EarthquakeData = EarthquakeData {    metaData :: MetaData,    earthquakes :: Map Day Earthquake} deriving (Eq, Show, Generic)instance FromJSON EarthquakeData where    parseJSON = withObject "EarthquakeData $ \v ->        EarthquakeData <$> v .: "Meta Data"        -- Map k v has a FromJSON instance that just does the right thing        -- so just get the payloads with (.:)        -- all this code is actually just because your field names are really !#$@~??        -- not an Aeson expert, maybe there's a better way                       <*> v .: "EarthQuakes"instance ToJSON EarthquakeData where    toJSON EarthquakeData{..} = object [ "Meta Data"   .= metaData                                       , "EarthQuakes" .= earthquakes                                       ]data MetaData = MetaData { country :: String, region :: String, latestRec :: Day } deriving (Eq, Show)instance FromJSON MetaData where    parseJSON = withObject "MetaData" $ \v ->        -- if you haven't noticed, applicative style is much neater than do        -- using OverloadedStrings avoids all the pack-ing static        MetaData <$> v .: "1: Country"                 <*> v .: "2: Region"                 <*> v .: "3: Latest Recording"instance ToJSON MetaData where    toJSON MetaData{..} = object [ "1: Country"          .= country                                 , "2: Region"           .= region                                 , "3: Latest Recording" .= latestRec                                 ]    toEncoding MetaData{..} = pairs $ "1: Country"          .= country                                   <> "2: Region"           .= region                                   <> "3: Latest Recording" .= latestRecdata Earthquake = Earthquake { richter :: Double } deriving (Eq, Show)-- Earthquake is a bit funky because your JSON apparently has-- numbers inside strings?-- only here do you actually need monadic operationsinstance FromJSON Earthquake where    parseJSON = withObject "Earthquake" $ \v ->        do string <- v .: "Richter"           stringNum <- parseJSON string           case readMaybe stringNum of             Just num -> return $ Earthquake num             Nothing -> typeMismatch "Double inside a String" stringinstance ToJSON Earthquake where    toJSON = object . return . ("Richter" .=) . show . richter    toEncoding = pairs . ("Richter" .=) . show . richter

I've tested this against your example JSON, and it appears to roundtrip encode and decode successfully.