json-stream 0.3.0.3 → 0.3.0.4
raw patch · 4 files changed
+64/−8 lines, 4 filesPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
API changes (from Hackage documentation)
+ Data.JsonStream.Parser: arrayFound :: a -> a -> Parser a -> Parser a
+ Data.JsonStream.Parser: objectFound :: a -> a -> Parser a -> Parser a
Files
- Data/JsonStream/Parser.hs +35/−4
- changelog.md +6/−1
- json-stream.cabal +1/−1
- test/ParserSpec.hs +22/−2
Data/JsonStream/Parser.hs view
@@ -67,6 +67,9 @@ , filterI , takeI , toList+ -- * SAX-like parsers+ , arrayFound+ , objectFound ) where import Control.Applicative@@ -222,6 +225,34 @@ arrayOf :: Parser a -> Parser a arrayOf valparse = array' (const valparse) +-- | Generate start/end objects when an element is found, in between run a parser.+-- The inner parser is not run if an array is not found.+elemFound :: Element -> a -> a -> Parser a -> Parser a+elemFound elsearch start end parser = Parser $ moreData handle+ where+ handle tok el _+ | el == elsearch = Yield start (parseAndAppend (callParse parser tok))+ handle tok _ _ = callParse ignoreVal tok++ parseAndAppend (Failed err) = Failed err+ parseAndAppend (Yield v np) = Yield v (parseAndAppend np)+ parseAndAppend (MoreData (Parser np, ntp)) = MoreData (Parser (parseAndAppend . np), ntp)+ parseAndAppend (Done ctx ntp) = Yield end (Done ctx ntp)++-- | Generate start/end values when an array is found, in between run a parser.+-- The inner parser is not run if an array is not found.+objectFound :: a -> a -> Parser a -> Parser a+objectFound = elemFound ObjectBegin++-- | Generate start/end values when an object is found, in between run a parser.+-- The inner parser is not run if an array is not found.+--+-- > >>> let test = "[[1,2,3],true,[],false,{\"key\":1}]" :: ByteString+-- > >>> parseByteString (arrayOf (arrayFound 10 20 (1 .! integer))) test :: [Int]+-- > [10,2,20,10,20]+arrayFound :: a -> a -> Parser a -> Parser a+arrayFound = elemFound ArrayBegin+ -- | Match nith item in an array. arrayWithIndexOf :: Int -> Parser a -> Parser a arrayWithIndexOf idx valparse = array' itemFn@@ -258,7 +289,7 @@ objcontent _ (Yield v np) = Yield v (objcontent True np) objcontent _ (Failed err) = Failed err - getLongKey acc len _ el ntok =+ getLongKey acc !len _ el ntok = case el of StringEnd | Right key <- decodeUtf8' (BL.fromChunks $ reverse acc) ->@@ -332,12 +363,12 @@ longString :: Maybe Int -> Parser T.Text longString mbounds = Parser $ moreData (handle [] 0) where- handle acc len tok el ntok =+ handle acc !len tok el ntok = case el of JValue (AE.String str) -> Yield str (Done "" ntok) StringContent str | (Just bounds) <- mbounds, len > bounds -- If the string exceeds bounds, discard it- -> callParse (ignoreVal' 1) ntok+ -> callParse (ignoreStrRestThen (Parser $ Done "")) ntok | otherwise -> moreData (handle (str:acc) (len + BS.length str)) ntok StringEnd | Right val <- decodeUtf8' (BL.fromChunks $ reverse acc)@@ -462,7 +493,7 @@ handleLongString level _ (StringContent _) ntok = moreData (handleLongString level) ntok handleLongString 0 _ StringEnd ntok = Done "" ntok handleLongString level _ StringEnd ntok = moreData (handleTok level) ntok- handleLongString _ _ el _ = Failed $ "Unexpected element in handleLongStr: " ++ (show el)+ handleLongString _ _ el _ = Failed $ "Unexpected element in handleLongStr: " ++ show el handleTok :: Int -> TokenResult -> Element -> TokenResult -> ParseResult a handleTok 0 _ (JValue _) ntok = Done "" ntok
changelog.md view
@@ -1,2 +1,7 @@+# 0.3.0.4+- Fixed bug in safestring+- Fixed test so it doesn't depend on versions of other packages+- Added sax-like parsers+ # 0.3.0.3-Fixed wrong size of C structure in FFI that was causing a segfault.+- Fixed wrong size of C structure in FFI that was causing a segfault.
json-stream.cabal view
@@ -1,5 +1,5 @@ name: json-stream-version: 0.3.0.3+version: 0.3.0.4 synopsis: Incremental applicative JSON parser description: Easy to use JSON parser fully supporting incremental parsing. Parsing grammar in applicative form.
test/ParserSpec.hs view
@@ -11,6 +11,8 @@ import Data.Text.Encoding (encodeUtf8) import Control.Monad (forM_) import Data.Text.Encoding (encodeUtf8)+import qualified Data.Vector as Vec+import qualified Data.HashMap.Strict as HMap import Data.JsonStream.Parser import Data.JsonStream.TokenParser@@ -103,14 +105,25 @@ msg = parseLazyByteString parser (BL.fromChunks test) :: [Int] msg `shouldBe` [1] + it "arrayFound generates events" $ do+ let test = ["[[1,2,3],true,[],false,{\"key\":1}]"]+ parser = arrayOf (arrayFound 10 20 (1 .! integer))+ msg = parseLazyByteString parser (BL.fromChunks test) :: [Int]+ msg `shouldBe` [10,2,20,10,20] + it "objectFound generates events" $ do+ let test = ["[[1,2,3],true,[],false,{\"key\":1}]"]+ parser = arrayOf (objectFound 10 20 ("key" .: integer))+ msg = parseLazyByteString parser (BL.fromChunks test) :: [Int]+ msg `shouldBe` [10,1,20]+ specEdge :: Spec specEdge = describe "Edge cases" $ do it "Correct incremental parsing 1" $ do let msg1 = "[ {\"test1\" :[1,true,false,null,-3.591e+1,[12,13]], \"test2\":\"123\\r\\n\\\"\\u0041\"}]" pmsg = BL.fromChunks $ map BS.singleton msg1 res = parseLazyByteString value pmsg :: [AE.Value]- show res `shouldBe` "[Array (fromList [Object fromList [(\"test2\",String \"123\\r\\n\\\"A\"),(\"test1\",Array (fromList [Number 1.0,Bool True,Bool False,Null,Number -35.91,Array (fromList [Number 12.0,Number 13.0])]))]])]"+ res `shouldBe` [Array (Vec.fromList [Object $ HMap.fromList [("test2",String "123\r\n\"A"),("test1",Array (Vec.fromList [Number 1.0,Bool True,Bool False,Null,Number (-35.91),Array (Vec.fromList [Number 12.0,Number 13.0])]))]])] it "Correct incremental parsing 2" $ do let msg1 = "{\"test1\" :[1,true,false,null,-3.591e+1,[12,13]], \"test2\":\"test2string\"}"@@ -266,8 +279,15 @@ let test1 = "[\"\", \"\", true]" onechar = BL.fromChunks $ map BS.singleton $ BS.unpack test1 parser = arrayOf bool- res = parseByteString parser test1 :: [Bool]+ res = parseLazyByteString parser onechar :: [Bool] res `shouldBe` [True]++ it "Correctly parses safeString when sliced" $ do+ let test1 = "[\"looooooooooong\", \"short\"]"+ onechar = BL.fromChunks $ map BS.singleton $ BS.unpack test1+ parser = arrayOf (safeString 6)+ res = parseLazyByteString parser onechar :: [T.Text]+ res `shouldBe` ["short"] -- testLexer (start:rest) = iter rest (tokenParser start)