-- | Stream Aeson, fruity, spicy, well carbonated. -- -- Saison represents JSON document as well-formed token stream. -- This approach is potentially faster than document model (i.e. representing -- document as an ADT) used in @aeson@. Also the approach is more -- flexible, as more structure is preseved, especially key-value -- pairs in records are not ordered. -- -- Saison is proof-of-concept package at the moment. -- -- The tokens aren't exactly as in JSON, but in similar grammar. -- There are no commas, but each item in arrays is prepended -- with zero-width marker (to differentiate from the end): -- -- @ -- VALUE = LITERAL | TEXT | NUMBER | "[" ARRAY | "{" RECORD -- LITERAL = "null" -- | "true" -- | "false" -- TEXT = ... -- NUMBER = ... -- ARRAY = ITEM VALUE ARRAY -- | "]" -- ITEM = epsilon -- RECORD = KEY VALUE RECORD -- | "}" -- KEY = TEXT -- @ -- -- Haskell types reflect this grammar: -- -- * @VALUE@ is 'Tokens' -- * @LITERAL@ is 'Lit' -- * @ARRAY@ is 'TkArray' -- * @RECORD@ is 'TkRecord'. -- module Saison ( -- * Types Tokens (..), Lit (..), TkArray (..), TkRecord (..), -- * Conversion to/from Value toValue, fromValue, -- * Parsing eitherDecodeStrict, FromTokens (..), skipValue, -- ** Record RecordParser, runRecordParser, requiredField, optionalField, skippedField, (<.:>), (<.:?>), ) where import Prelude () import Prelude.Compat import Data.ByteString (ByteString) import Data.Text (Text) import qualified Data.ByteString as BS import Saison.Decoding.Class import Saison.Decoding.Parser import Saison.Decoding.Result import Saison.Decoding.Tokens import Saison.Decoding.Value import Saison.Decoding.Record ------------------------------------------------------------------------------- -- Decoding ------------------------------------------------------------------------------- -- | Parse a value from strict 'ByteString'. eitherDecodeStrict :: FromTokens a => ByteString -> Either String a eitherDecodeStrict bs = unResult (fromTokens (tokens bs)) Left $ \x bs' -> let bs'' = skipSpace bs' in if BS.null bs'' then Right x else Left $ "Unexpected data after the JSON value: " ++ showBeginning bs'' -- | 'requiredField' using 'FromTokens' parser. (<.:>) :: FromTokens a => RecordParser (a -> b) -> Text -> RecordParser b p <.:> n = p <*> requiredField n fromTokens (<.:?>) :: FromTokens a => RecordParser (Maybe a -> b) -> Text -> RecordParser b p <.:?> n = p <*> optionalField n fromTokens infixl 4 <.:>, <.:?>