I'm trying to write a FromJSON function for Aeson.
The 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"
      }
    }
  ]
}
The ADT: data Movie = Movie {id::String, title::String}
My attempt:
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
This gives Couldn't match expected type 'Parser t0' with actual type 'Maybe a0' In the first argument of 'head'.
As you can see, I'm trying to pick the first of the movies in the Array, but I wouldn't mind getting a list of Movies either (in case there are several in the Array).
If you really want to parse a single Movie from a JSON array of movies, you can do something like this:
instance FromJSON Movie where
    parseJSON (Object o) = do
        movieValue <- head <$> o .: "movies"
        Movie <$> movieValue .: "id" <*> movieValue .: "title"
    parseJSON _ = mzero
But the safer route would be to parse a [Movie] via newtype wrapper:
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
                        It's usually easiest to match the structure of your ADTs and instances to the structure of your JSON.
Here, I've added a newtype MovieList to deal with the outermost object so that the instance for Movie only has to deal with a single movie. This also gives you multiple movies for free via the FromJSON instance for lists.
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
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With