By 'historical data' I just mean dates as key, and value on that day as value.
For example, often govt institutes or uni's research division compile date about earthquakes, rainfalls, market movement, etc. in this format
{
"Meta Data": {
"1: Country": "SomeCountry",
"2: Region": "SomeRegion",
"3: Latest Recording": "2018-11-16"
},
"EarthQuakes": {
"2018-11-16": {
"Richter": "5.2508"
},
"2018-11-09": {
"Richter": "4.8684"
},
"2018-11-02": {
"Richter": "1.8399"
},
...
...
...
"1918-11-02": {
"Richter": "1.8399"
}
}
Usually it'll have a "Meta Data" section and other one would contain the values/data.
I as a beginner know of two ways to parse these type of documents.
Either you go with general parsing shown in Aeson's documentation where you define data types like this
Data MetaData = MetaData { country :: String, region :: String, latestRec :: String } deriving (Show, Eq, Generic)
Make it an instance of FromJSON
instance FromJSON MetaData where
parseJSON = withObject "MetaData" $
\v -> do
metaData <- v .: pack "Meta Data"
country <- metaData .: pack "1: Country"
region <- metaData .: pack "2: Region"
latestRec <- metaData .: pack "3: Latest Recording"
return MetaData{..}
With of course RecordWildCard
and DeriveGenerics
extensions enabled.
The problem I see with this approach is that it can't be easily implemented for the "EarthQuakes" section.
I'll have to define each and every single date
earthQuakes <- v .: "EarthQuakes"
date1 <- earthQuakes .: "2018-11-16"
date2 <- earthQuakes .: "2018-11-06"
date3 <- earthQuakes .: "2018-11-02"
...
...
dateInfinity <- earthQuakes .: "1918-11-16"
A better approach would be just parsing all the data as default JSON values by decoding the link into Object
type
thisFunction = do
linksContents <- simpleHttp "somelink"
let y = fromJust (decode linksContents :: Object)
z = aLotOfFunctionCompositions y
return z
where the aLotOfFunctionCompositions
would first convert Object
to maybe HashMap
having [(k, v)]
pairs. Then I would map an unConstruct
function to get the value out of default constructors like
unConstruct (DefaultType value) = case (DefaultType value) of
DefaultType x -> x
and finally you would get a nice list!
The problem with this approach is the aLotOfFunctionComposition
.
That is just an example! But in reality it can look as ugly and unreadable as this
let y = Prelude.map (\(a, b) -> (decode (encode a) :: Maybe String, decode (encode (snd (Prelude.head b))) :: Maybe String)) x
z = Prelude.map (\(a, b) -> (fromJust a, fromJust b)) y
a = Prelude.map (\(a, b) -> (a, read b :: Double)) z
b = Prelude.map (\(a, b) -> (Prelude.filter (/= '-') a, b)) a
c = Prelude.map (\(a, b) -> (read a :: Int, b)) b
This is snippet from a working code I made.
So my question is this: Is there a better/cleaner way of decoding these sorts of JSON files where you have a lot of "dates" keys and you need to parse them into workable datatypes?