2014-03-03 80 views
7

我试图从RESTful API中解析JSON。返回的JSON高度嵌套,可能/可能不包含某些字段。下面是一些例子返回的数据:用Aeson解析Haskell中的嵌套JSON

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

正如你所看到的,JSON模式与含有locationarrival,并queryTime一个resultSet开始。 location依次包含位置列表,arrival包含到达列表,queryTime只是UTC时间。然后,一个arrival可以包含一个blockPosition,它可以包含一个trip等等很多的嵌套。大量的可选字段。

为了保持这一切,我创建了一组新的数据类型。数据类型嵌套相似。对于每种数据类型,我都有一个FromJSON实例(来自Aeson库)。

-- 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 <$> ((o .: "resultSet") >>= (.: "location")) 
       <*> ((o .: "resultSet") >>= (.: "arrival")) 
       <*> ((o .: "resultSet") >>= (.: "queryTime")) 
    parseJSON _ = mzero 

data TripList  = TripList  {triplist  :: [Trip]}  deriving Show 

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

data LocationList = LocationList {locationList :: [Location]} deriving Show 

instance FromJSON LocationList where 
    parseJSON (Object o) = 
    LocationList <$> (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 <$> (o .: "desc") 
       <*> (o .: "locid") 
       <*> (o .: "dir") 
       <*> (o .: "lng") 
       <*> (o .: "lat") 
    parseJSON _ = mzero 

data ArrivalList  = ArrivalList {arrivalList :: [Arrival]} deriving Show 

instance FromJSON ArrivalList where 
    parseJSON (Object o) = 
    ArrivalList <$> (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 <$> (o .: "detour") 
      <*> (o .: "status") 
      <*> (o .: "locid") 
      <*> (o .: "block") 
      <*> (o .: "scheduled") 
      <*> (o .: "shortSign") 
      <*> (o .: "dir") 
      <*> (o .:? "estimated") 
      <*> (o .: "route") 
      <*> (o .: "departed") 
      <*> (o .:? "blockPosition") 
      <*> (o .: "fullSign") 
      <*> (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 <$> (o .: "at") 
       <*> (o .: "feet") 
       <*> (o .: "lng") 
       <*> (o .: "trip") 
       <*> (o .: "lat") 
       <*> (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 <$> (o .: "progress") 
     <*> (o .: "desc") 
     <*> (o .: "pattern") 
     <*> (o .: "dir") 
     <*> (o .: "route") 
     <*> (o .: "tripNum") 
     <*> (o .: "destDist") 
    parseJSON _ = mzero 

现在,问题:检索数据很容易。我可以

json <- getJSON stopID 
putStrLn (show (decode json :: (Maybe Value))) 

显示原始JSON但是,当我试图让ResultSet中的数据,它失败Nothing

putStrLn (show (decode json :: Maybe ResultSet)) 

但是,如果我删除嵌套数据,而只是试图让queryString场(由FromJSON的数据类型,例如删除字段,它成功并返回查询字符串场。

data ResultSet 
    = ResultSet  { 
         queryTime :: String 
        } deriving Show 

instance FromJSON ResultSet where 
    parseJSON (Object o) 
    = ResultSet <$> ((o .: "resultSet") >>= (.: "queryTime")) 
    parseJSON _ = mzero 

我在做什么错?这是在Haskell中解析JSON最简单的方法吗?我是这个(学生)的总noob,所以请保持温柔

+0

这个问题可能是,要么你的解析器有一些错误或输入并不像你想象的那样。您应该尝试使用anyDecode函数而不是解码来获取错误消息并将其粘贴到此处。 – Reite

+0

样式点:在ResultSet的parseJSON中,您应该提取ResultSet一次,然后询问它。您正在为相同的值进行三次单独查找。 –

+0

@Reite:使用任一代码确实揭示了一个mzero错误。这告诉我,我的FromJSON实例是不正确的。它看起来像我的模式匹配正在落入'parseJSON _ = mzero'模式。但为什么?我已经检查了传入的JSON。位置字段在那里。 –

回答

8

我解决了我的问题。为返回的JSON对象列表创建数据类型PLE,用于位置数据,这是返回的位置的列表:

resultSet : { 
    location : [{ 
     desc : "Tuality Hospital/SE 8th Ave MAX Station", 
     locid : 9843, 
     dir : "Eastbound", 
     lng : -122.978016886765, 
     lat : 45.5212880911494 
    } 
    ], 

我被设置含有[Arrival]列表的Arrivals数据类型:

data ArrivalList  = ArrivalList {arrivalList :: [Arrival]} deriving Show 

然后,当我试图解析JSON,我试图将一个ArrivalList填充到我的ResultSet中,后者用于解析其中的JSON数据。但是因为ArrivalList不是JSON对象,所以它失败了。

解决方法是不使用自定义数据类型的列表。相反,将一个列表分配给一个JSON!Array对象,该对象稍后可以被解析为它自己的对象和子对象。

data ResultSet 
     = ResultSet  { 
         locations :: !Array 
         ,arrivals  :: !Array 
         ,queryTime :: String 
         } deriving Show 

全部放在一起:

data ResultSet 
    = ResultSet  { 
         locations :: !Array 
         ,arrivals  :: !Array 
         ,queryTime :: String 
        } deriving Show 

instance FromJSON ResultSet where 
    parseJSON (Object o) = ResultSet <$> 
         ((o .: "resultSet") >>= (.: "location")) 
        <*> ((o .: "resultSet") >>= (.: "arrival")) 
        <*> ((o .: "resultSet") >>= (.: "queryTime")) 
    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 <$> (o .: "desc") 
       <*> (o .: "locid") 
       <*> (o .: "dir") 
       <*> (o .: "lng") 
       <*> (o .: "lat") 
    parseJSON _ = mzero