Потоковый анализ JSON в Haskell с помощью Pipes.Aeson
Библиотека Pipes.Aeson предоставляет следующую функцию:
decode :: (Monad m, ToJSON a) => Parser ByteString m (Either DecodingError a)
Если я использую evalStateT с этим анализатором и дескриптором файла в качестве аргумента, один файл JSON считывается из файла и анализируется.
Проблема в том, что файл содержит несколько объектов (все одного типа), и я хотел бы свернуть или уменьшить их, когда они читаются.
Pipes.Parse предоставляет:
foldAll :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Parser a m b
но, как вы можете видеть, это возвращает новый синтаксический анализатор - я не могу представить способ предоставления первого синтаксического анализатора в качестве аргумента.
Похоже, что Parser на самом деле является источником в монадном преобразователе StateT. Я задавался вопросом, существует ли способ извлечения источника из StateT, чтобы evalStateT можно было применить к анализатору foldAll, а источник - из анализатора декодирования.
Это, вероятно, совершенно неправильный подход.
Мой вопрос, вкратце:
При анализе файла с помощью Pipes.Aeson, каков наилучший способ сложить все объекты в файле?
1 ответ
Вместо того, чтобы использовать decode
, вы можете использовать decoded
разбор объектива от Pipes.Aeson.Unchecked
, Оказывается, производитель ByteString
в производитель проанализированных значений JSON.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Pipes
import qualified Pipes.Prelude as P
import qualified Pipes.Aeson as A
import qualified Pipes.Aeson.Unchecked as AU
import qualified Data.ByteString as B
import Control.Lens (view)
byteProducer :: Monad m => Producer B.ByteString m ()
byteProducer = yield "1 2 3 4"
intProducer :: Monad m => Producer Int m (Either (A.DecodingError, Producer B.ByteString m ()) ())
intProducer = view AU.decoded byteProducer
Возвращаемое значение intProducer
немного страшно, но это только означает, что intProducer
заканчивается либо ошибкой синтаксического анализа и неразобранными байтами после ошибки, либо возвращаемым значением исходного производителя (который ()
в нашем случае).
Мы можем игнорировать возвращаемое значение:
intProducer' :: Monad m => Producer Int m ()
intProducer' = intProducer >> return ()
И подключить производителя в складку из Pipes.Prelude
, лайк sum
:
main :: IO ()
main = do
total <- P.sum intProducer'
putStrLn $ show total
В ghci:
λ :main
10
Также обратите внимание, что функции чисто и нечисто позволяют применять складки производителей, определенные в пакете foldl.