module Data.ByteString.Streaming.Aeson
( DecodingError(..)
, encode
, decode
, decoded
, streamParse
) where
import Control.Exception (Exception)
import Control.Monad.Trans
import qualified Control.Monad.Trans.State.Strict as S
import Control.Monad.Trans.State.Strict (StateT(..))
import qualified Data.Aeson as Ae
import qualified Data.Attoparsec.ByteString as Attoparsec
import qualified Data.ByteString as S
import qualified Data.ByteString.Internal as S (isSpaceWord8)
import Data.Data (Data, Typeable)
import qualified Data.Attoparsec.ByteString.Streaming as PA
import Data.ByteString.Streaming
import Data.ByteString.Streaming.Internal
import qualified Data.ByteString.Streaming as B
import Streaming
import Streaming.Internal (Stream(..))
import Streaming.Prelude (yield)
import qualified Data.JsonStream.Parser as J
import Data.JsonStream.Parser (ParseOutput (..))
type ParsingError = ([String],String)
data DecodingError
= AttoparsecError ParsingError
| FromJSONError String
deriving (Show, Eq, Data, Typeable)
instance Exception DecodingError
encode :: (Monad m, Ae.ToJSON a) => a -> ByteString m ()
encode = fromLazy . Ae.encode
decode
:: (Monad m, Ae.FromJSON a)
=> StateT (ByteString m x) m (Either DecodingError a)
decode = do
mev <- StateT (PA.parse Ae.json')
return $ case mev of
Right l -> Left (AttoparsecError l)
Left v -> case Ae.fromJSON v of
Ae.Error e -> Left (FromJSONError e)
Ae.Success a -> Right a
decoded :: (Monad m, Ae.FromJSON a) =>
ByteString m r
-> Stream (Of a) m (Either (DecodingError, ByteString m r) r)
decoded = consecutively decode
where
consecutively
:: (Monad m)
=> StateT (ByteString m r) m (Either e a)
-> ByteString m r
-> Stream (Of a) m (Either (e, ByteString m r) r)
consecutively parser = step where
step p0 = do
x <- lift $ nextSkipBlank p0
case x of
Left r -> Return (Right r)
Right (bs, p1) -> do
(mea, p2) <- lift $ S.runStateT parser (Chunk bs p1)
case mea of
Right a -> do
yield a
step p2
Left e -> Return (Left (e, p2))
nextSkipBlank p0 = do
x <- nextChunk p0
case x of
Left _ -> return x
Right (a,p1) -> do
let a' = S.dropWhile S.isSpaceWord8 a
if S.null a' then nextSkipBlank p1
else return (Right (a', p1))
streamParse
:: (Monad m) =>
J.Parser a
-> ByteString m r
-> Stream (Of a) m (Maybe String, ByteString m r)
streamParse parser input = loop input (J.runParser parser) where
loop bytes p0 = case p0 of
ParseFailed s -> return (Just s,bytes)
ParseDone bs -> return (Nothing, chunk bs >> bytes)
ParseYield a p1 -> yield a >> loop bytes p1
ParseNeedData f -> do
e <- lift $ nextChunk bytes
case e of
Left r -> return (Just "Not enough data",return r)
Right (bs, rest) -> loop rest (f bs)