How to 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 the following from it:

 data WeatherResponse = WeatherResponse { temp :: Double , humidity :: Double , weatherMain :: T.Text } deriving Show 

I am trying to use the following code for this, but I run into errors all the time. I finally got all types to match, but it doesn’t parse correctly and does not understand where it doesn’t work.

 {-# 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 just get the following conclusion, which doesn't let me know enough where I made a mistake.

 $ runhaskell lib/code.hs Left "Error in $: expected main, encountered Object" 

I put the whole thing in a gist viewable here

I would like to know what is wrong with the code, and how can I fix it. If you have suggestions on how to write this in a more readable way, I would also like to know. Currently, I am mostly annoyed by two separate functions lambda3 and parseInner annoying)

+5
source share
1 answer

In my opinion, you are doing it too hard. 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" 

Output:

 [nix-shell:~/haskell-sample]$ ./weather Right (WeatherResponse {temp = 271.979, humidity = 100.0, weatherMain = "Clouds"}) 
+4
source

Source: https://habr.com/ru/post/1243035/


All Articles