title: Parsing Nested JSON in Haskell with Aeson
body: <p>I'm trying to parse JSON from a RESTful API. The returned JSON is highly nested and may/may not include certain fields. Here is an example of some returned data:</p>
  
  <pre><code>{
      resultSet : {
          location : [{
                  desc : "Tuality Hospital/SE 8th Ave MAX Station",
                  locid : 9843,
                  dir : "Eastbound",
                  lng : -122.978016886765,
                  lat : 45.5212880911494
              }
          ],
          arrival : [{
                  detour : false,
                  status : "estimated",
                  locid : 9843,
                  block : 9024,
                  scheduled : "2014-03-02T16:48:15.000-0800",
                  shortSign : "Blue to Gresham",
                  dir : 0,
                  estimated : "2014-03-02T16:48:15.000-0800",
                  route : 100,
                  departed : false,
                  blockPosition : {
                      at : "2014-03-02T16:16:43.579-0800",
                      feet : 3821,
                      lng : -122.9909514,
                      trip : [{
                              progress : 171494,
                              desc : "Hatfield Government Center",
                              pattern : 140,
                              dir : 1,
                              route : 100,
                              tripNum : "4365647",
                              destDist : 171739
                          }, {
                              progress : 0,
                              desc : "Cleveland Ave",
                              pattern : 10,
                              dir : 0,
                              route : 100,
                              tripNum : "4365248",
                              destDist : 3577
                          }
                      ],
                      lat : 45.5215368,
                      heading : 328
                  },
                  fullSign : "MAX Blue Line to Gresham",
                  piece : "1"
              }, {
                  detour : false,
                  status : "estimated",
                  locid : 9843,
                  block : 9003,
                  scheduled : "2014-03-02T17:05:45.000-0800",
                  shortSign : "Blue to Gresham",
                  dir : 0,
                  estimated : "2014-03-02T17:05:45.000-0800",
                  route : 100,
                  departed : false,
                  blockPosition : {
                      at : "2014-03-02T16:34:33.787-0800",
                      feet : 3794,
                      lng : -122.9909918,
                      trip : [{
                              progress : 171521,
                              desc : "Hatfield Government Center",
                              pattern : 140,
                              dir : 1,
                              route : 100,
                              tripNum : "4365648",
                              destDist : 171739
                          }, {
                              progress : 0,
                              desc : "Cleveland Ave",
                              pattern : 10,
                              dir : 0,
                              route : 100,
                              tripNum : "4365250",
                              destDist : 3577
                          }
                      ],
                      lat : 45.5216054,
                      heading : 345
                  },
                  fullSign : "MAX Blue Line to Gresham",
                  piece : "1"
              }
          ],
          queryTime : "2014-03-02T16:35:21.039-0800"
      }
  }
  </code></pre>
  
  <p>As you can see, the JSON schema starts with a <code>resultSet</code> which contains a <code>location</code>, <code>arrival</code>, and <code>queryTime</code>. The <code>location</code> in turn, contains a list of locations, <code>arrival</code> contains a list of arrivals, and <code>queryTime</code> is just a UTC time. Then, an <code>arrival</code> can contain a <code>blockPosition</code>, which can contain a <code>trip</code>, etc. Lots of nesting. Lots of optional fields.</p>
  
  <p>To hold all this, I've created a set of new data types. The data types are nested similarly. For each data type, I have an instance of FromJSON (from the Aeson library).</p>
  
  <pre><code>-- Data Type Definitions and FromJSON Instance Definitions ---------------------
  
  
  data ResultSet
       = ResultSet     { locations    :: LocationList
                        ,arrivals     :: ArrivalList
                        ,queryTime    :: String
                       } deriving Show
  
  instance FromJSON ResultSet where
    parseJSON (Object o) =
      ResultSet &lt;$&gt; ((o .: "resultSet") &gt;&gt;= (.: "location"))
                &lt;*&gt; ((o .: "resultSet") &gt;&gt;= (.: "arrival"))
                &lt;*&gt; ((o .: "resultSet") &gt;&gt;= (.: "queryTime"))
    parseJSON _ = mzero
  
  data TripList        = TripList     {triplist     :: [Trip]}     deriving Show
  
  instance FromJSON TripList where
    parseJSON (Object o) =
      TripList &lt;$&gt; (o .: "trip")
    parseJSON _ = mzero
  
  data LocationList    = LocationList {locationList :: [Location]} deriving Show
  
  instance FromJSON LocationList where
    parseJSON (Object o) =
      LocationList &lt;$&gt; (o .: "location")
    parseJSON _ = mzero
  
  data Location
       = Location      { loc_desc           :: String
                        ,loc_locid          :: Int
                        ,loc_dir            :: String
                        ,loc_lng            :: Double
                        ,loc_lat            :: Double
                       } deriving Show
  
  instance FromJSON Location where
    parseJSON (Object o) =
      Location &lt;$&gt; (o .: "desc")
                &lt;*&gt; (o .: "locid")
                &lt;*&gt; (o .: "dir")
                &lt;*&gt; (o .: "lng")
                &lt;*&gt; (o .: "lat")
    parseJSON _ = mzero
  
  data ArrivalList     = ArrivalList  {arrivalList  :: [Arrival]}  deriving Show
  
  instance FromJSON ArrivalList where
    parseJSON (Object o) =
      ArrivalList &lt;$&gt;  (o .: "arrival")
    parseJSON _ = mzero
  
  data Arrival
       = Arrival       { arr_detour         :: Bool
                        ,arr_status         :: String
                        ,arr_locid          :: Int
                        ,arr_block          :: Int
                        ,arr_scheduled      :: String
                        ,arr_shortSign      :: String
                        ,arr_dir            :: Int
                        ,estimated      :: Maybe String
                        ,route          :: Int
                        ,departed       :: Bool
                        ,blockPosition  :: Maybe BlockPosition
                        ,fullSign       :: String
                        ,piece          :: String
                       } deriving Show
  
  instance FromJSON Arrival where
    parseJSON (Object o) =
      Arrival &lt;$&gt; (o .: "detour")
              &lt;*&gt; (o .: "status")
              &lt;*&gt; (o .: "locid")
              &lt;*&gt; (o .: "block")
              &lt;*&gt; (o .: "scheduled")
              &lt;*&gt; (o .: "shortSign")
              &lt;*&gt; (o .: "dir")
              &lt;*&gt; (o .:? "estimated")
              &lt;*&gt; (o .: "route")
              &lt;*&gt; (o .: "departed")
              &lt;*&gt; (o .:? "blockPosition")
              &lt;*&gt; (o .: "fullSign")
              &lt;*&gt; (o .: "piece")
    parseJSON _ = mzero
  
  data BlockPosition  
       = BlockPosition { bp_at                 :: String
                        ,bp_feet               :: Int
                        ,bp_lng                :: Double
                        ,bp_trip               :: Trip
                        ,bp_lat                :: Double
                        ,bp_heading            :: Int 
                        } deriving Show
  
  instance FromJSON BlockPosition where
    parseJSON (Object o) =
      BlockPosition &lt;$&gt; (o .: "at")
                &lt;*&gt; (o .: "feet")
                &lt;*&gt; (o .: "lng")
                &lt;*&gt; (o .: "trip")
                &lt;*&gt; (o .: "lat")
                &lt;*&gt; (o .: "heading")
    parseJSON _ = mzero
  
  data Trip           
       = Trip          { trip_progress      :: Int
                        ,trip_desc          :: String
                        ,trip_pattern       :: Int
                        ,trip_dir           :: Int
                        ,trip_route         :: Int
                        ,trip_tripNum       :: Int
                        ,trip_destDist      :: Int
                       } deriving Show
  
  instance FromJSON Trip where
    parseJSON (Object o) =
      Trip &lt;$&gt; (o .: "progress")
           &lt;*&gt; (o .: "desc")
           &lt;*&gt; (o .: "pattern")
           &lt;*&gt; (o .: "dir")
           &lt;*&gt; (o .: "route")
           &lt;*&gt; (o .: "tripNum")
           &lt;*&gt; (o .: "destDist")
    parseJSON _ = mzero
  </code></pre>
  
  <p>Now, the problem: Retrieving the data is easy.  I can show the raw JSON by</p>
  
  <pre><code>json &lt;- getJSON stopID
  putStrLn (show (decode json :: (Maybe Value)))
  </code></pre>
  
  <p>But when I try to get the ResultSet data, it fails with <code>Nothing</code>.</p>
  
  <pre><code>putStrLn (show (decode json :: Maybe ResultSet))
  </code></pre>
  
  <p>However, if I remove the nested data and simply try to get the <code>queryString</code> field (by removing the fields from the data type and instance of FromJSON, it succeeds and returns the queryString field.</p>
  
  <pre><code>data ResultSet
       = ResultSet     { 
                        queryTime    :: String
                       } deriving Show
  
  instance FromJSON ResultSet where
    parseJSON (Object o)
     = ResultSet &lt;$&gt; ((o .: "resultSet") &gt;&gt;= (.: "queryTime"))
    parseJSON _ = mzero
  </code></pre>
  
  <p>What am I doing wrong? Is this the easiest method of parsing JSON in Haskell? I'm a total noob at this (a student), so please be gentle.</p>