2014-03-03 5 views
7

RESTful API에서 JSON을 구문 분석하려고합니다. 반환 된 JSON은 중첩되어 있으며 특정 필드를 포함 할 수도 있고 포함하지 않을 수도 있습니다. 당신이 볼 수 있듯이, JSON 스키마는 location, arrivalqueryTime 포함하는 resultSet 시작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" 
    } 
} 

: 여기에 몇 가지의 예는 데이터를 반환합니다. location은 차례로 위치 목록을 포함하고 arrival에는 도착 목록이 포함되어 있고 queryTime은 UTC 시간입니다. 그런 다음 arrivalblockPosition을 포함 할 수 있으며 trip 등을 포함 할 수 있습니다. 둥지가 많이 있습니다. 선택 필드가 많이 있습니다.

이 모든 것을 유지하기 위해 일련의 새로운 데이터 유형을 만들었습니다. 데이터 유형은 비슷하게 중첩됩니다. 각 데이터 유형에 대해 Aeson 라이브러리의 FromJSON 인스턴스가 있습니다.

-- 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와 함께 실패합니다. 내가 중첩 된 데이터를 제거하고 간단 FromJSON의 데이터 유형 및 인스턴스에서 필드를 제거합니다 (queryString 필드를 얻기 위해 시도하는 경우

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

그러나, 성공 및 쿼리 문자열 필드를 반환합니다.

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

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

내가 잘못하고있는 것은 무엇입니까? 이것은 하스켈에서 JSON을 파싱하는 가장 쉬운 방법인가요?이 학생 (학생)의 총 멍청이입니다. 따라서 부드럽게하십시오.

+0

문제는 당신의 파서가 약간의 오류가 어느 쪽이든 아마 또는 당신이하지 생각처럼 입력이 보이지 않습니다. 오류 메시지를 가져 와서 여기에 붙여 넣으려면 디코드 대신에 두 decode 함수를 사용해야합니다. – Reite

+0

스타일 포인트 : ResultSet 용 parseJSON에서 ResultSet을 한 번 추출한 다음 질문을해야합니다. 동일한 값에 대해 세 번의 별도 조회가 수행됩니다. –

+0

@Reite : 두 코드 중 하나를 사용하면 실제로 mzero 오류가 나타납니다.이것은 FromJSON 인스턴스가 옳지 않다는 것을 말해줍니다. 내 패턴 일치가'parseJSON _ = mzero' 패턴으로 떨어지는 것 같습니다. 하지만 왜? 들어오는 JSON을 검사했습니다. 위치 입력란이 있습니다. –

답변

8

문제를 해결했습니다. 반환 된 JSON 객체 목록에 대한 데이터 유형을 만듭니다. PLE, 위치리스트로서 반환 위치 데이터를 들어 I 파싱하려고 할 때 그리고,

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

: I는 [Arrival]의리스트를 포함하는 Arrivals 데이터 유형을 설정 하였다

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

JSON, 나중에 ResultSet에 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