2017-01-10 7 views
4

特定のディレクトリ内のすべてのjsonファイルをデータ型の結果に解析します。DataText.Lazy.IOを使用してAesonでJSONファイルを解析する方法

だから、私はそれがコンパイルされ、私はに走った

import qualified Data.Text.Lazy.IO as T 
import qualified Data.Text.Lazy.Encoding as T 

getFileContent :: FilePath -> IO B.ByteString 
getFileContent path = T.encodeUtf8 `fmap` T.readFile path 

、レイジー延ByteStringにファイルをロードするためにData.Text.Lazy.IOで始まったデコード機能に

decodeResult :: Data.ByteString.Lazy.ByteString -> Maybe Result 

を持っていますファイルが多すぎると問題が発生したので、withFileを使用してください。このバージョンで

import System.IO 
import qualified Data.ByteString.Lazy as B 
import qualified Data.Text.Lazy.IO as T 
import qualified Data.Text.Lazy.Encoding as T 

getFileContent :: FilePath -> IO (Maybe Result) 
getFileContent path = withFile path ReadMode $ \hnd -> do 
    content <- T.hGetContents hnd 
    return $ (decodeAnalytic . T.encodeUtf8) content 

loadAllResults :: FilePath -> IO [Result] 
loadAllResults path = do 
    paths <- listDirectory path 
    results <- sequence $ fmap getFileContent (fmap (path ++) $ filter (endswith ".json") paths) 
    return $ catMaybes results 

、怠惰なIOが評価やったことがなかった、それは常に空のリストを返すようです。しかし、getFileContentの中にコンテンツを印刷すると、すべて正常に動作しているようです。

getFileContent :: FilePath -> IO (Maybe Result) 
getFileContent path = withFile path ReadMode $ \hnd -> do 
    content <- T.hGetContents hnd 
    print content 
    return $ (decodeAnalytic . T.encodeUtf8) content 

私は何が欠けているのか分からないので、このタイプのものにコンジットを使用する必要がありますか?

+0

簡単な答えは、はい、コンジットなどを使用しています。より複雑な答えは、あなたの 'loadAllResults'は*信じられないほど*怠惰です。単純に' loadAllResults x'を実行しても実際にはファイルが読み込まれません。結果リストを評価しようとすると、同時にすべてのファイルを開き、それらを読み込もうとします。怠惰が 'hGetContents'から来ているので、' withFile'はあなたを助けません。 - 怠惰なテキストIOに切り替えてみてください。 – user2407038

答えて

5

一般的に言えば、私はとなります。は、JSONファイルのような任意のサイズのデータ​​を解析するためにストリーミングライブラリを使用することをお勧めします。しかし、aesonを使用してJSONを解析する特定のケースでは、aesonライブラリ自体が最終的にメモリ内のファイル全体をValueタイプとして表すため、メモリオーバーランの懸念はそれほど重要ではありません。そのため、厳密なバイトテストI/Oを使用することができます。私は、コンジットと厳密なI/Oの両方をJSON値の解析に使用する例を挙げました。 (私は、導管のバージョンは、私はわからないんだけど、すでにいくつかのライブラリに存在すると思います)

#!/usr/bin/env stack 
{- stack --resolver lts-7.14 --install-ghc runghc 
    --package aeson --package conduit-extra 
-} 
import   Control.Monad.Catch  (MonadThrow, throwM) 
import   Control.Monad.IO.Class (MonadIO, liftIO) 
import   Data.Aeson    (FromJSON, Result (..), eitherDecodeStrict', 
              fromJSON, json, Value) 
import   Data.ByteString   (ByteString) 
import qualified Data.ByteString   as B 
import   Data.Conduit   (ConduitM, runConduitRes, (.|)) 
import   Data.Conduit.Attoparsec (sinkParser) 
import   Data.Conduit.Binary  (sourceFile) 

sinkFromJSON :: (MonadThrow m, FromJSON a) => ConduitM ByteString o m a 
sinkFromJSON = do 
    value <- sinkParser json 
    case fromJSON value of 
     Error e -> throwM $ userError e 
     Success x -> return x 

readJSONFile :: (MonadIO m, FromJSON a) => FilePath -> m a 
readJSONFile fp = liftIO $ runConduitRes $ sourceFile fp .| sinkFromJSON 

-- Or using strict I/O 
readJSONFileStrict :: (MonadIO m, FromJSON a) => FilePath -> m a 
readJSONFileStrict fp = liftIO $ do 
    bs <- B.readFile fp 
    case eitherDecodeStrict' bs of 
     Left e -> throwM $ userError e 
     Right x -> return x 

main :: IO() 
main = do 
    x <- readJSONFile "test.json" 
    y <- readJSONFileStrict "test.json" 
    print (x :: Value) 
    print (y :: Value) 

EDITは言及を忘れ:私は強くあなたのJSONを読み込むためのテキスト形式のI/Oを使用してに対してをお勧めしますファイル。 JSONファイルはUTF-8でエンコードする必要がありますが、テキスト入出力機能では、システム設定で文字エンコードに指定されているものを使用します。 Data.ByteString.readFileなどに依存すると、より信頼性が高くなります。私はもっ​​と詳細に入ったin a recent blog post

+1

詳細な回答ありがとうございました! –

関連する問題