代码之家  ›  专栏  ›  技术社区  ›  Varun Madiath

如何用Aeson解析这个JSON?

  •  4
  • Varun Madiath  · 技术社区  · 8 年前

    我有以下JSON片段:

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

    我把整件事都放在一个可以看的要点上 here

    我想知道代码有什么问题,以及如何修复它。如果你有关于如何以更可读的方式写这篇文章的建议,我也很想知道。目前,我主要对这两个独立的功能感到恼火 lambda3 parseInner 令人讨厌)

    1 回复  |  直到 8 年前
        1
  •  3
  •   Sibi    8 年前

    在我看来,你把这件事弄得太复杂了。这样的方法应该有效:

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