2014-05-17 7 views
9

Die Pipes.Aeson Bibliothek setzt die folgende Funktion:Streaming Parsen von JSON in Haskell mit Pipes.Aeson

decode :: (Monad m, ToJSON a) => Parser ByteString m (Either DecodingError a) 

Wenn ich evalStateT mit diesem Parser und ein Datei-Handle als Argument verwenden, ein einzelnes JSON-Objekt ist Aus der Datei lesen und analysieren.

Das Problem ist, dass die Datei mehrere Objekte (alle vom gleichen Typ) enthält und ich möchte sie falten oder reduzieren, wie sie gelesen werden.

Pipes.Parse bietet:

foldAll :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Parser a m b 

aber wie Sie dies gibt einen neuen Parser zu sehen - ich kann nicht denken Sie an einen Weg, um den ersten Parser als Argument zu liefern.

Es sieht aus wie ein Parser ist eigentlich ein Produzent in einem StateT Monade-Transformator. Ich habe mich gefragt, ob es eine Möglichkeit gibt, den Producer aus dem StateT zu extrahieren, so dass evalStateT auf den foldAll Parser und der Producer aus dem decode Parser angewendet werden kann.

Dies ist jedoch wahrscheinlich völlig falsch Ansatz.

Meine Frage, kurz:
Beim Parsen einer Datei mit Pipes.Aeson, was ist der beste Weg, um alle Objekte in der Datei zu falten?

Antwort

4

Anstatt decode zu verwenden, können Sie die decodedparsing lens von Pipes.Aeson.Unchecked verwenden. Es verwandelt einen Produzenten von ByteString in einen Produzenten von geparsten JSON-Werten.

{-# 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 

Der Rückgabewert von intProducer ist ein bisschen erschreckend, aber es bedeutet lediglich, dass intProducer Oberflächen entweder mit einem Parsing-Fehler und die nicht geparste Bytes nach dem Fehler, oder mit dem Rückgabewert des ursprünglichen Erzeuger (die () ist in unserem Fall).

können wir den Rückgabewert ignorieren:

intProducer' :: Monad m => Producer Int m() 
intProducer' = intProducer >> return() 

und stecken Sie den Hersteller in eine fold von Pipes.Prelude, wie sum:

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

In GHCI:

λ :main 
10 

Beachten Sie auch, dass die Funktionen purely und impurely können Sie auf Produzentenfalten anwenden, die im foldl Paket definiert sind.

+2

Sie können auch zoomen decodiert (foldAll Schritt beginnen getan) ' –

+0

@GabrielGonzalez Ah ja, ich habe vergessen, man kann auch' Zoom' verwenden, um Linsen auf 'Parser's anzuwenden. – danidiaz

+0

@GabrielGonzalez Es scheint auch, dass die Verwendung von "Zoom decodiert ..." Parsing-Fehler leichter zu handhaben macht. – danidiaz