Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I parse this JSON with Aeson?

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)

like image 486
Varun Madiath Avatar asked Sep 25 '22 09:09

Varun Madiath


1 Answers

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"})
like image 159
Sibi Avatar answered Sep 28 '22 04:09

Sibi