9

我正在尝试FromJSON为 Aeson 编写一个函数。

JSON:

{
  "total": 1,
  "movies": [
    {
      "id": "771315522",
      "title": "Harry Potter and the Philosophers Stone (Wizard's Collection)",
      "posters": {
        "thumbnail": "http://content7.flixster.com/movie/11/16/66/11166609_mob.jpg",
        "profile": "http://content7.flixster.com/movie/11/16/66/11166609_pro.jpg",
        "detailed": "http://content7.flixster.com/movie/11/16/66/11166609_det.jpg",
        "original": "http://content7.flixster.com/movie/11/16/66/11166609_ori.jpg"
      }
    }
  ]
}

ADT:data Movie = Movie {id::String, title::String}

我的尝试:

instance FromJSON Movie where
    parseJSON (Object o) = do
       movies <- parseJSON =<< (o .: "movies") :: Parser Array
       v <- head $ decode movies
       return $ Movie <$>
           (v .: "movies" >>= (.: "id") ) <*>
           (v .: "movies" >>= (.: "title") )
    parseJSON _ = mzero

这给了Couldn't match expected type 'Parser t0' with actual type 'Maybe a0' In the first argument of 'head'.

如您所见,我正在尝试选择 中的第一部电影Array,但我也不介意获取电影列表(如果数组中有多个电影)。

4

2 回答 2

11

如果你真的想Movie从一个 JSON 电影数组中解析一个,你可以这样做:

instance FromJSON Movie where
    parseJSON (Object o) = do
        movieValue <- head <$> o .: "movies"
        Movie <$> movieValue .: "id" <*> movieValue .: "title"
    parseJSON _ = mzero

但更安全的方法是解析[Movie]vianewtype包装器:

main = print $ movieList <$> decode "{\"total\":1,\"movies\":[ {\"id\":\"771315522\",\"title\":\"Harry Potter and the Philosophers Stone (Wizard's Collection)\",\"posters\":{\"thumbnail\":\"http://content7.flixster.com/movie/11/16/66/11166609_mob.jpg\",\"profile\":\"http://content7.flixster.com/movie/11/16/66/11166609_pro.jpg\",\"detailed\":\"http://content7.flixster.com/movie/11/16/66/11166609_det.jpg\",\"original\":\"http://content7.flixster.com/movie/11/16/66/11166609_ori.jpg\"}}]}"

newtype MovieList = MovieList {movieList :: [Movie]}

instance FromJSON MovieList where
    parseJSON (Object o) = MovieList <$> o .: "movies"
    parseJSON _ = mzero

data Movie = Movie {id :: String, title :: String}

instance FromJSON Movie where
    parseJSON (Object o) = Movie <$> o .: "id" <*> o .: "title"
    parseJSON _ = mzero
于 2013-05-14T16:49:06.227 回答
8

通常最容易将 ADT 和实例的结构与 JSON 的结构相匹配。

在这里,我添加了一个新类型MovieList来处理最外层的对象,这样 for 的实例Movie只需要处理一个电影。这还通过FromJSON列表实例免费为您提供多部电影。

data Movie = Movie { id :: String, title :: String }

newtype MovieList = MovieList [Movie]

instance FromJSON MovieList where
  parseJSON (Object o) =
    MovieList <$> (o .: "movies")
  parseJSON _ = mzero

instance FromJSON Movie where
  parseJSON (Object o) =
    Movie <$> (o .: "id")
          <*> (o .: "title")
  parseJSON _ = mzero
于 2013-05-14T16:48:40.290 回答