I have the following JSON snippet:
{
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04n"
}
],
"main": {
"temp": 271.979,
"pressure": 1024.8,
"humidity": 100,
"temp_min": 271.979,
"temp_max": 271.979,
"sea_level": 1028.51,
"grnd_level": 1024.8
},
"id": 6332485,
"name": "Queensbridge Houses",
"cod": 200
}
I want to get parse the following type from it:
data WeatherResponse = WeatherResponse
{ temp :: Double
, humidity :: Double
, weatherMain :: T.Text
} deriving Show
I've been trying to use the following code to do it, but I keep running into errors. I've finally got all the types to match up, but it parses incorrectly and don't really understand where it's failing.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Aeson
import Data.Aeson.Types (Parser, Array)
import Data.Time (defaultTimeLocale, formatTime, getZonedTime)
import qualified Data.ByteString.Lazy as BL
import qualified Data.Vector as V
import qualified Data.Text as T
data WeatherResponse = WeatherResponse
{ temp :: Double
, humidity :: Double
, weatherMain :: T.Text
} deriving Show
lambda3 :: Value -> Parser T.Text
lambda3 o = do
withText "main" (\t -> do
return t
) o
parseInner :: Value -> Parser T.Text
parseInner a = withArray "Inner Array" (lambda3 . (V.head)) a
instance FromJSON WeatherResponse where
parseJSON =
withObject "Root Object" $ \o -> do
mainO <- o .: "main"
temp <- mainO .: "temp"
humidity <- mainO .: "humidity"
weatherO <- o .: "weather"
weatherMain <- parseInner weatherO
return $ WeatherResponse temp humidity weatherMain
getSampleData = BL.readFile "/home/vmadiath/.xmonad/weather.json"
main = do
text <- getSampleData
let (result :: Either String WeatherResponse) = eitherDecode text
putStrLn . show $ result
I simply get the following output which doesn't give me enough to know where I've gone wrong.
$ runhaskell lib/code.hs
Left "Error in $: expected main, encountered Object"
I've put the entire thing in a gist viewable here
I'd like to know what's wrong with the code, and how I can fix it. If you have suggestions of how to write this in a more readable way I'd love to know that too. Currently I'm mostly annoyed with the two separate functions lambda3
and parseInner
is annoying)
In my opinion, you are making this too complicated. Something like this should work:
instance FromJSON WeatherResponse where
parseJSON (Object v) = do
weatherValue <- head <$> v .: "weather"
WeatherResponse <$> ((v .: "main") >>= (.: "temp"))
<*> ((v .: "main") >>= (.: "humidity"))
<*> weatherValue .: "main"
It's output:
[nix-shell:~/haskell-sample]$ ./weather
Right (WeatherResponse {temp = 271.979, humidity = 100.0, weatherMain = "Clouds"})
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