json-stream 0.4.3.0 → 0.4.4.0
raw patch · 7 files changed
+481/−19 lines, 7 filesdep +conduitdep +primitivePVP ok
version bump matches the API change (PVP)
Dependencies added: conduit, primitive
API changes (from Hackage documentation)
+ Data.JsonStream.Parser: manyReverse :: Parser a -> Parser [a]
+ Data.JsonStream.Parser: valueWith :: (Value -> Parser a) -> Parser a
Files
- Data/JsonStream/Conduit.hs +25/−0
- Data/JsonStream/Parser.hs +18/−9
- Data/JsonStream/Unescape.hs +407/−7
- README.md +1/−1
- changelog.md +7/−0
- json-stream.cabal +11/−1
- test/ParserSpec.hs +12/−1
+ Data/JsonStream/Conduit.hs view
@@ -0,0 +1,25 @@+-- |+-- Module : Data.JsonStream.Conduit+-- License : BSD-style+--+-- Stability : experimental+-- Portability : portable+--+-- Use "Data.JsonStream.Parser" parsers in "Data.Conduit".++module Data.JsonStream.Conduit (+ parserConduit+) where++import Data.ByteString (ByteString)+import qualified Data.Conduit.Internal as C++import Data.JsonStream.Parser++-- |Use a 'Parser' as a conduit from 'ByteString' input chunks to results, finally returning any parse error or 'Nothing' on success.+parserConduit :: Parser a -> C.ConduitT ByteString a m (Maybe String)+parserConduit ps = C.ConduitT (parsePipe $ runParser ps) where+ parsePipe (ParseYield a p) r = C.HaveOutput (parsePipe p r) a+ parsePipe (ParseNeedData f) r = C.NeedInput (\i -> parsePipe (f i) r) (\() -> r (Just "Incomplete JSON"))+ parsePipe (ParseFailed e) r = r (Just e)+ parsePipe (ParseDone l) r = C.Leftover (r Nothing) l
Data/JsonStream/Parser.hs view
@@ -47,6 +47,7 @@ , eitherDecodeStrict -- * FromJSON parser , value+ , valueWith , string , byteString -- * Constant space parsers@@ -74,6 +75,7 @@ , filterI , takeI , mapWithFailure+ , manyReverse -- * SAX-like parsers , arrayFound , objectFound@@ -89,6 +91,7 @@ import Control.Applicative import qualified Data.Aeson as AE+import qualified Data.Aeson.Types as AE import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.ByteString.Lazy.Internal as BL@@ -398,7 +401,9 @@ ObjectBegin -> AE.Object . tomap <$> callParse (manyReverse (objectItems aeValue)) tok _ -> Failed ("aeValue - unexpected token: " ++ show el) --- | Optimized function for aeson objects - evades reversing the objects+-- | Identical to @fmap 'reverse' . 'many'@ but more efficient.+-- If you don't care about the order of the results but plan to fully evaluate the list,+-- this can be slightly more efficient than 'many' as it avoids the accumulating thunks. manyReverse :: Parser a -> Parser [a] manyReverse f = Parser $ \ntok -> loop [] (callParse f ntok) where@@ -544,21 +549,25 @@ value' _ (JValue AE.Null) ntok = Yield Nothing (Done "" ntok) value' tok _ _ = callParse (Just <$> valparse) tok --- | Match 'FromJSON' value. Calls parseJSON on the parsed value.------ >>> let json = "[{\"key1\": [1,2], \"key2\": [5,6]}]"--- >>> parseByteString (arrayOf value) json :: [AE.Value]--- [Object (fromList [("key2",Array [Number 5.0,Number 6.0]),("key1",Array [Number 1.0,Number 2.0])])]-value :: AE.FromJSON a => Parser a-value = Parser $ \ntok -> loop (callParse aeValue ntok)+-- | Match values with a 'AE.Parser'. Returns values for which the given parser succeeds.+valueWith :: (AE.Value -> AE.Parser a) -> Parser a+valueWith jparser = Parser $ \ntok -> loop (callParse aeValue ntok) where loop (Done ctx ntp) = Done ctx ntp loop (Failed err) = Failed err loop (MoreData (Parser np, ntok)) = MoreData (Parser (loop . np), ntok) loop (Yield v np) =- case AE.fromJSON v of+ case AE.parse jparser v of AE.Error _ -> loop np AE.Success res -> Yield res (loop np)++-- | Match 'AE.FromJSON' value. Equivalent to @'valueWith' 'AE.parseJSON'@.+--+-- >>> let json = "[{\"key1\": [1,2], \"key2\": [5,6]}]"+-- >>> parseByteString (arrayOf value) json :: [AE.Value]+-- [Object (fromList [("key2",Array [Number 5.0,Number 6.0]),("key1",Array [Number 1.0,Number 2.0])])]+value :: AE.FromJSON a => Parser a+value = valueWith AE.parseJSON -- | Take maximum n matching items. --
Data/JsonStream/Unescape.hs view
@@ -1,28 +1,48 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE ForeignFunctionInterface #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE UnliftedFFITypes #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MultiWayIf #-} module Data.JsonStream.Unescape ( unescapeText ) where -import Control.Exception (evaluate, throw, try)-import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO) import Data.ByteString as B import Data.ByteString.Internal as B hiding (c2w)-import qualified Data.Text.Array as A import Data.Text.Encoding.Error (UnicodeException (..)) import Data.Text.Internal (Text (..))-import Data.Text.Internal.Private (runText) import Data.Text.Unsafe (unsafeDupablePerformIO)-import Data.Word (Word8)-import Foreign.C.Types (CInt (..), CSize (..))+import Data.Word (Word8, Word32) import Foreign.ForeignPtr (withForeignPtr)-import Foreign.Marshal.Utils (with) import Foreign.Ptr (Ptr, plusPtr) import Foreign.Storable (peek)++#if MIN_VERSION_text(2,0,0)++import qualified Data.Primitive as P+import qualified Data.Text.Array as T+import qualified Data.Text.Internal as T+import Data.Bits (shiftL, shiftR, (.&.), (.|.))+import Control.Exception (try, throwIO)+import Foreign.ForeignPtr (ForeignPtr)+import GHC.ForeignPtr (plusForeignPtr)++#else++import Control.Exception (evaluate, throw, try)+import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)+import Data.Text.Internal.Private (runText)+import Foreign.Marshal.Utils (with)+import qualified Data.Text.Array as A import GHC.Base (MutableByteArray#)+import Foreign.C.Types (CInt (..), CSize (..)) +#endif++#if !MIN_VERSION_text(2,0,0)+ foreign import ccall unsafe "_jstream_decode_string" c_js_decode :: MutableByteArray# s -> Ptr CSize -> Ptr Word8 -> Ptr Word8 -> IO CInt@@ -49,3 +69,383 @@ unescapeText :: ByteString -> Either UnicodeException Text unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText' {-# INLINE unescapeText #-}++#else++withBS :: ByteString -> (ForeignPtr Word8 -> Int -> r) -> r+#if MIN_VERSION_bytestring(0,11,0)+withBS (BS !sfp !slen) kont = kont sfp slen+#else+withBS (PS !sfp !soff !slen) kont = kont (plusForeignPtr sfp soff) slen+#endif+{-# INLINE withBS #-}++unescapeText :: ByteString -> Either UnicodeException Text+unescapeText = unsafeDupablePerformIO . try . unescapeTextIO++throwDecodeError :: IO a+throwDecodeError =+ let desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream"+ in throwIO (DecodeError desc Nothing)++-- The following is copied from aeson-2.0 ++-------------------------------------------------------------------------------+-- unescapeTextIO+-------------------------------------------------------------------------------++-- This function is generated using staged-streams+-- See: https://github.com/phadej/staged/blob/master/staged-streams-unicode/src/Unicode/JSON.hs+--+-- Because @aeson@ better to not use template-haskell itself,+-- we dump the splice and prettify it by hand a bit.+--+unescapeTextIO :: ByteString -> IO Text+unescapeTextIO bs = withBS bs $ \fptr len ->+ withForeignPtr fptr $ \begin -> do+ let end :: Ptr Word8+ end = plusPtr begin len++ arr <- P.newPrimArray len++ let write3bytes :: Int -> Word8 -> Word8 -> Word8 -> Ptr Word8 -> IO Text+ write3bytes !out !b1 !b2 !b3 !inp = do+ P.writePrimArray arr out b1+ write2bytes (out + 1) b2 b3 inp++ write2bytes :: Int -> Word8 -> Word8 -> Ptr Word8 -> IO Text+ write2bytes !out !b1 !b2 !inp = do+ P.writePrimArray arr out b1+ write1byte (out + 1) b2 inp++ write1byte :: Int -> Word8 -> Ptr Word8 -> IO Text+ write1byte !out !b1 !inp = do+ P.writePrimArray arr out b1+ state_start (out + 1) inp++ writeCodePoint :: Int -> Ptr Word8 -> Word32 -> IO Text+ writeCodePoint !out !inp !acc+ | acc <= 127 = do+ P.writePrimArray arr out (fromIntegral acc :: Word8)+ state_start (out + 1) (plusPtr inp 1)++ | acc <= 2047 = do+ let b1 = fromIntegral (shiftR acc 6 .|. 192) :: Word8+ let b2 = fromIntegral ((acc .&. 63) .|. 128) :: Word8+ P.writePrimArray arr out b1+ write1byte (out + 1) b2 (plusPtr inp 1)++ | acc <= 65535 = do+ let b1 = fromIntegral (shiftR acc 12 .|. 224) :: Word8+ let b2 = fromIntegral ((shiftR acc 6 .&. 63) .|. 128) :: Word8+ let b3 = fromIntegral ((acc .&. 63) .|. 128) :: Word8+ P.writePrimArray arr out b1+ write2bytes (out + 1) b2 b3 (plusPtr inp 1)++ | otherwise = do+ let b1 = fromIntegral (shiftR acc 18 .|. 240) :: Word8+ let b2 = fromIntegral ((shiftR acc 12 .&. 63) .|. 128) :: Word8+ let b3 = fromIntegral ((shiftR acc 6 .&. 63) .|. 128) :: Word8+ let b4 = fromIntegral ((acc .&. 63) .|. 128) :: Word8+ P.writePrimArray arr out b1+ write3bytes (out + 1) b2 b3 b4 (plusPtr inp 1)++ state_sudone :: Int -> Ptr Word8 -> Word32 -> Word32 -> IO Text+ state_sudone !out !inp !hi !lo+ | 56320 <= lo, lo <= 57343+ = writeCodePoint out inp (65536 + (shiftL (hi - 55296) 10 .|. (lo - 56320)))+ + | otherwise+ = throwDecodeError++ state_su4 :: Int -> Ptr Word8 -> Word32 -> Word32 -> IO Text+ state_su4 !out !inp !hi !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_sudone out inp hi (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_sudone out inp hi (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_sudone out inp hi (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_su3 :: Int -> Ptr Word8 -> Word32 -> Word32 -> IO Text+ state_su3 !out !inp !hi !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_su4 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_su4 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_su4 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_su2 :: Int -> Ptr Word8 -> Word32 -> Word32 -> IO Text+ state_su2 !out !inp !hi !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 -> + state_su3 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_su3 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_su3 out (plusPtr inp 1) hi (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_su1 :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_su1 !out !inp !hi+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_su2 out (plusPtr inp 1) hi (fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_su2 out (plusPtr inp 1) hi (fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_su2 out (plusPtr inp 1) hi (fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_su :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_su !out !inp !hi+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ case w8 of+ 117 -> state_su1 out (plusPtr inp 1) hi+ _ -> throwDecodeError++ state_ss :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_ss !out !inp !hi+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ case w8 of+ 92 -> state_su out (plusPtr inp 1) hi+ _ -> throwDecodeError++ state_udone :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_udone !out !inp !acc+ | acc < 55296 || acc > 57343 =+ writeCodePoint out inp acc++ | acc < 56320 =+ state_ss out (plusPtr inp 1) acc++ | otherwise =+ throwDecodeError++ state_u4 :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_u4 !out !inp !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_udone out inp (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_udone out inp (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_udone out inp (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_u3 :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_u3 !out !inp !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_u4 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_u4 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_u4 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_u2 :: Int -> Ptr Word8 -> Word32 -> IO Text+ state_u2 !out !inp !acc+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_u3 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_u3 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_u3 out (plusPtr inp 1) (shiftL acc 4 .|. fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_u1 :: Int -> Ptr Word8 -> IO Text+ state_u1 !out !inp+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | 48 <= w8, w8 <= 57 ->+ state_u2 out (plusPtr inp 1) (fromIntegral (w8 - 48))+ | 65 <= w8, w8 <= 70 ->+ state_u2 out (plusPtr inp 1) (fromIntegral (w8 - 55))+ | 97 <= w8, w8 <= 102 ->+ state_u2 out (plusPtr inp 1) (fromIntegral (w8 - 87))+ | otherwise ->+ throwDecodeError++ state_escape :: Int -> Ptr Word8 -> IO Text+ state_escape !out !inp+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ case w8 of+ 34 -> do+ P.writePrimArray arr out 34+ state_start (out + 1) (plusPtr inp 1)++ 92 -> do+ P.writePrimArray arr out 92+ state_start (out + 1) (plusPtr inp 1)++ 47 -> do+ P.writePrimArray arr out 47+ state_start (out + 1) (plusPtr inp 1)++ 98 -> do+ P.writePrimArray arr out 8+ state_start (out + 1) (plusPtr inp 1)++ 102 -> do+ P.writePrimArray arr out 12+ state_start (out + 1) (plusPtr inp 1)++ 110 -> do+ P.writePrimArray arr out 10+ state_start (out + 1) (plusPtr inp 1)++ 114 -> do+ P.writePrimArray arr out 13+ state_start (out + 1) (plusPtr inp 1)++ 116 -> do+ P.writePrimArray arr out 9+ state_start (out + 1) (plusPtr inp 1)++ 117 ->+ state_u1 out (plusPtr inp 1)++ _ -> throwDecodeError++ state_input4c :: Int -> Ptr Word8 -> Word8 -> Word8 -> Word8 -> IO Text+ state_input4c !out !inp !b1 !b2 !b3+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128+ , let acc = shiftL (fromIntegral (b1 .&. 7)) 18+ , let acc' = acc .|. shiftL (fromIntegral (b2 .&. 63)) 12+ , let acc'' = acc' .|. shiftL (fromIntegral (b3 .&. 63)) 6+ , let acc''' = acc'' .|. fromIntegral (w8 .&. 63) :: Word32+ , acc''' >= 65536 && acc''' < 1114112 -> do+ P.writePrimArray arr out b1+ write3bytes (out + 1) b2 b3 w8 (plusPtr inp 1)++ | otherwise ->+ throwDecodeError++ state_input4b :: Int -> Ptr Word8 -> Word8 -> Word8 -> IO Text+ state_input4b !out !inp !b1 !b2+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128 ->+ state_input4c out (plusPtr inp 1) b1 b2 w8++ | otherwise ->+ throwDecodeError++ state_input4 :: Int -> Ptr Word8 -> Word8 -> IO Text+ state_input4 !out !inp !b1+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128 ->+ state_input4b out (plusPtr inp 1) b1 w8++ | otherwise ->+ throwDecodeError++ state_input3b :: Int -> Ptr Word8 -> Word8 -> Word8 -> IO Text+ state_input3b !out !inp !b1 !b2+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128+ , let acc = shiftL (fromIntegral (b1 .&. 15)) 12+ , let acc' = acc .|. shiftL (fromIntegral (b2 .&. 63)) 6+ , let acc'' = acc' .|. fromIntegral (w8 .&. 63) :: Word32+ , (acc'' >= 2048 && acc'' < 55296) || acc'' > 57343 -> do+ P.writePrimArray arr out b1+ write2bytes (out + 1) b2 w8 (plusPtr inp 1)++ | otherwise ->+ throwDecodeError++ state_input3 :: Int -> Ptr Word8 -> Word8 -> IO Text+ state_input3 !out !inp !b1+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128 ->+ state_input3b out (plusPtr inp 1) b1 w8++ | otherwise ->+ throwDecodeError++ state_input2 :: Int -> Ptr Word8 -> Word8 -> IO Text+ state_input2 !out !inp !b1+ | inp == end = throwDecodeError+ | otherwise = do+ w8 <- peek inp+ if | (w8 .&. 192) == 128,+ let acc = shiftL (fromIntegral (b1 .&. 63)) 6 :: Word32+ acc' = acc .|. fromIntegral (w8 .&. 63) :: Word32+ , acc' >= 128 -> do+ P.writePrimArray arr out b1+ write1byte (out + 1) w8 (plusPtr inp 1)++ | otherwise ->+ throwDecodeError++ state_start :: Int -> Ptr Word8 -> IO Text+ state_start !out !inp+ | inp == end = do+ P.shrinkMutablePrimArray arr out+ frozenArr <- P.unsafeFreezePrimArray arr+ return $ case frozenArr of+ P.PrimArray ba -> T.Text (T.ByteArray ba) 0 out++ | otherwise = do+ w8 <- peek inp+ if | w8 == 92 -> state_escape out (plusPtr inp 1)+ | w8 < 128 -> do+ P.writePrimArray arr out w8+ state_start (out + 1) (plusPtr inp 1)++ | w8 < 192 -> throwDecodeError+ | w8 < 224 -> state_input2 out (plusPtr inp 1) w8+ | w8 < 240 -> state_input3 out (plusPtr inp 1) w8+ | w8 < 248 -> state_input4 out (plusPtr inp 1) w8++ | otherwise -> throwDecodeError++ -- start the state machine+ state_start (0 :: Int) begin++#endif
README.md view
@@ -1,6 +1,6 @@ # json-stream - Applicative incremental JSON parser for Haskell -[](https://travis-ci.org/ondrap/json-stream) [](https://hackage.haskell.org/package/json-stream)+[](https://hackage.haskell.org/package/json-stream) # When to use this library
changelog.md view
@@ -1,3 +1,10 @@+# 0.4.4.0++- added text 2.0 compatibility+- added conduit interface behind a flag (Dylan Simon)+- added manyReverse (Dylan Simon)+- added valueWith (Dylan Simon)+ # 0.4.3.0 - Aeson 2.0 compatibility
json-stream.cabal view
@@ -1,5 +1,5 @@ name: json-stream-version: 0.4.3.0+version: 0.4.4.0 synopsis: Incremental applicative JSON parser description: Easy to use JSON parser fully supporting incremental parsing. Parsing grammar in applicative form.@@ -28,6 +28,10 @@ type: git location: https://github.com/ondrap/json-stream.git +flag conduit+ description: Support the conduit package+ manual: True+ default: False library exposed-modules: Data.JsonStream.Parser@@ -45,6 +49,10 @@ , vector , unordered-containers , scientific+ , primitive+ if flag(conduit)+ exposed-modules: Data.JsonStream.Conduit+ build-depends: conduit default-language: Haskell2010 Ghc-Options: -Wall -fwarn-incomplete-uni-patterns@@ -104,6 +112,8 @@ , directory , QuickCheck , quickcheck-unicode+ , primitive+ -- executable spdtest -- main-is: spdtest.hs
test/ParserSpec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-} module ParserSpec where import Control.Applicative@@ -12,10 +13,14 @@ import Control.Monad (forM_) import Data.Text.Encoding (encodeUtf8) import qualified Data.Vector as Vec-import qualified Data.HashMap.Strict as HMap import System.Directory (getDirectoryContents) import Data.Int import Data.Word+#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.KeyMap as AEK+#else+import qualified Data.HashMap.Strict as HMap+#endif import Data.JsonStream.Parser @@ -124,13 +129,19 @@ msg = parseLazyByteString parser (BL.fromChunks test) :: [BS.ByteString] msg `shouldBe` ["abcd\\n\\rxyz"] ++ 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]+#if MIN_VERSION_aeson(2,0,0)+ res `shouldBe` [Array (Vec.fromList [Object $ AEK.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])]))]])]+#else 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])]))]])]+#endif it "Correct incremental parsing 2" $ do let msg1 = "{\"test1\" :[1,true,false,null,-3.591e+1,[12,13]], \"test2\":\"test2string\"}"