json - 使用 Aeson 在 Haskell 中解析嵌套 JSON

标签 json haskell aeson

我正在尝试从 RESTful API 解析 JSON。返回的 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 模式以 resultSet 开头。其中包含 location , arrival , 和 queryTime . 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)))

但是当我尝试获取 ResultSet 数据时,它失败了 Nothing .
putStrLn (show (decode json :: Maybe ResultSet))

但是,如果我删除嵌套数据并尝试获取 queryString字段(通过从 FromJSON 的数据类型和实例中删除字段,它成功并返回 queryString 字段。
data ResultSet
     = ResultSet     { 
                      queryTime    :: String
                     } deriving Show

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

我究竟做错了什么?这是在 Haskell 中解析 JSON 的最简单方法吗?我是这个(学生)的菜鸟,所以请温柔。

最佳答案

我解决了我的问题。我试图为返回的 JSON 对象列表创建数据类型。例如,对于位置数据,它作为位置列表返回:

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

我正在设置 Arrivals包含 [Arrival] 列表的数据类型:
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

关于json - 使用 Aeson 在 Haskell 中解析嵌套 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22135923/

相关文章:

haskell - 函数定义问题(没有…的实例源自)

exception - 与 aeson/attoparsec 进行管道,一旦源没有更多数据,如何无异常地干净退出

haskell - 如何查看解析后的Aeson Value?

javascript - 将 JSON 字符串解析为数组

haskell - Haskell 中的状态 Monad 和 'put' 函数

c# - 使用c#导入json数据

haskell - 如何模拟货币、货币和在货币之间交换货币的银行?

xml - 为什么这段代码会产生段错误?

json - 在 Data::Dumper 中是否有等效于 Perls 的 Dumper() 方法的 Go 语言?

php - 拉拉维尔 : Query to get a single column in response from a JSON column in mysql 5. 7