如何使用Aeson解析这个JSON?

4

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
}

我希望能够解析以下类型的内容:

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

我一直在尝试使用以下代码来实现,但是一直遇到错误。最终我成功让所有类型匹配上了,但解析出错了,我不太明白它在哪里出错了。

{-# 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

我只是得到了下面的输出,这并没有给我足够的信息来知道我的问题在哪里。
$ runhaskell lib/code.hs
Left "Error in $: expected main, encountered Object"

我已经将整个内容放在了一个Gist中,可以在这里查看。
我想知道代码有什么问题,以及如何修复它。如果你有关于如何更易读地编写此代码的建议,我也很想知道。目前,我主要对两个独立的函数lambda3parseInner感到恼火。

通过 V.head,您可以获取 weather 数组中的第一个元素,该元素是一个对象,因此应为 lambda3 = withObject "weatherMain" (.: "main") - zakyggaps
1个回答

3
在我看来,您把这件事情搞得太复杂了。像这样的东西应该可以工作:

在我的观点中,您让这件事情变得过于复杂了。类似下面这样的内容应该可以达到预期效果:

instance FromJSON WeatherResponse where
  parseJSON (Object v) = do
      weatherValue <- head <$> v .: "weather"
      WeatherResponse <$> ((v .: "main") >>= (.: "temp"))
                          <*> ((v .: "main") >>= (.: "humidity"))
                          <*> weatherValue .: "main"

它的输出:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接