Потоковый разбор 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 на самом деле является Producer в преобразователе монады StateT. Мне было интересно, есть ли способ извлечь Producer из StateT, чтобы evalStateT можно было применить к парсеру foldAll и Producer из парсера декодирования.

Хотя, наверное, это совершенно неправильный подход.

Коротко о моем вопросе:
Как при синтаксическом анализе файла с помощью Pipes.Aeson лучше всего свернуть все объекты в файле?


person immutablestate    schedule 17.05.2014    source источник


Ответы (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 ()

И подключите производителя к fold из Pipes.Prelude, например sum:

main :: IO ()
main = do
    total <- P.sum intProducer'
    putStrLn $ show total

В ГКИ:

λ :main
10

Также обратите внимание, что функции чисто и нечисто позволяют применять к сгибам производителей, определенным в пакете foldl.

person danidiaz    schedule 17.05.2014
comment
Вы также можете сделать zoom decoded (foldAll step begin done) - person Gabriel Gonzalez; 18.05.2014
comment
@GabrielGonzalez Ах да, я забыл, что можно также использовать zoom для применения линз к Parsers. - person danidiaz; 18.05.2014
comment
@GabrielGonzalez Кроме того, кажется, что использование zoom decoded ... упрощает обработку ошибок синтаксического анализа. - person danidiaz; 18.05.2014