我想将给定目录中的所有json文件解析为数据类型Result.
所以我有一个解码功能
decodeResult :: Data.ByteString.Lazy.ByteString -> Maybe Result
我从Data.Text.Lazy.IO开始将文件加载到Lazy ByteString中,
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
它编译了,但我遇到了太多文件打开的问题,所以我想也许我应该使用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
所以我不确定我错过了什么,我应该使用导管来做这类事吗?
一般而言,我会建议使用流媒体库,用于解析任意大小的数据,如一个JSON文件.但是,在使用aeson解析JSON的特定情况下,超出内存的问题并不像IMO那么重要,因为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)
编辑忘记提及:我强烈建议不要使用文本I/O来读取您的JSON文件.JSON文件应使用UTF-8编码,而文本I/O函数将使用系统设置指定的任何字符编码.依靠Data.ByteString.readFile
和类似更可靠.我在最近的博客文章中详细介绍了一下.