2013-05-14 5 views
9

Aeson의 FromJSON 함수를 작성하려고합니다.Aeson이 포함 된 중첩 JSON의 구문 분석 배열

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

ADT : data Movie = Movie {id::String, title::String}

내 시도가 :

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 

Couldn't match expected type 'Parser t0' with actual type 'Maybe a0' In the first argument of 'head'을 제공합니다.

알다시피, 나는 Array에서 첫 번째 영화를 고르려고 노력하고 있지만, 영화의 목록을 얻는 것도 괜찮습니다.

답변

10

, 당신은 다음과 같이 수행 할 수 있습니다

instance FromJSON Movie where 
    parseJSON (Object o) = do 
     movieValue <- head <$> o .: "movies" 
     Movie <$> movieValue .: "id" <*> movieValue .: "title" 
    parseJSON _ = mzero 

을하지만 안전한 경로는 newtype 래퍼를 통해 [Movie]을 구문 분석하는 것 :

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 
8

일반적으로 ADT 및 인스턴스의 구조를 JSON의 구조와 일치시키는 것이 가장 쉽습니다.

여기서 가장 새로운 객체를 처리하기 위해 MovieList이라는 새로운 유형을 추가하여 Movie의 인스턴스는 하나의 동영상 만 처리해야합니다. 또한 목록의 FromJSON 인스턴스를 통해 여러 영화를 무료로 제공합니다. 당신이 정말로 영화의 JSON 배열에서 단일 Movie을 구문 분석하려면

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 
+0

감사합니다! 나는 다른 유형을 소개하는 것을 생각하지 않았다. 후속 조치를 요청해도 될까요? 'Movie' 타입을'filePath' 나'myRating'과 같은 몇개의 필드를 포함하도록 확장하고 싶다면'myMovie'라는 새로운 타입을 추가하거나'Movie'에 약간의'Maybe' 필드를 추가 할 것을 권한다. '디코드 (decode)'를 입력하고 입력하십시오. (필자는 ADT가 불변이므로 모든 필드로 새 인스턴스를 만드는 것을 의미 할 것입니다.) – mb21

+1

@ mb21 : 두 방법 모두 잘 작동합니다. 나머지 응용 프로그램에 따라 다릅니다. 이러한 필드가 디코딩 직후에 항상 추가되면, 나머지 유형의 함수가 항상 '그냥'이어야하는 'Maybe'를 처리 할 필요가 없도록 새로운 유형을 만드는 것이 합리적 일 수 있습니다. 다른 한편으로, 만일 그 필드가 선택 사항이라면, 그것들을'Maybe '에 보관하는 것이 합리적입니다. – hammar

+0

@ 해머 : 좋습니다, 많이 고마워요! – mb21