当前位置:  开发笔记 > 前端 > 正文

如何使用Data.Text.Lazy.IO用Aeson解析JSON文件

如何解决《如何使用Data.Text.Lazy.IO用Aeson解析JSON文件》经验,为你挑选了1个好方法。

我想将给定目录中的所有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

所以我不确定我错过了什么,我应该使用导管来做这类事吗?



1> Michael Snoy..:

一般而言,我建议使用流媒体库,用于解析任意大小的数据,如一个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和类似更可靠.我在最近的博客文章中详细介绍了一下.

推荐阅读
mobiledu2402851203
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有