Reputation: 3252
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)
Upvotes: 4
Views: 241
Reputation: 48644
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"})
Upvotes: 3