Потоковый анализ 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.

Другие вопросы по тегам