简体   繁体   中英

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)

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"})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM