2013-05-14 13 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

あなたが本当に映画のJSON配列から単一Movieを解析したい場合は、このような何か行うことができます。

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

をしかし、より安全なルートが[Movie]newtype経由でラッパーを解析することであろう:

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の構造に合わせるのが最も簡単です。

ここでは、Movieのインスタンスが1つのムービーのみを処理するように、最も外側のオブジェクトを扱う新しいタイプMovieListを追加しました。これはリストのためにFromJSONインスタンスを介して複数のムービーを無料で提供します。

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:いずれの方法でもうまくいきます。それはあなたのアプリケーションの残りの部分に依存します。これらのフィールドがデコード直後に常に追加された場合、残りの関数が常に 'Just'であるべき' Maybe'を処理する必要がないように、新しい型を作ることは理にかなっているかもしれません。一方、これらのフィールドがオプションの場合は、それらを「未了」に保つことが理にかなっています。 – hammar

+0

@ハマー:大丈夫、ありがとう! – mb21

関連する問題