on master help sign in
23 files changed+147−674
since splitsnapshots
unifiedsplit
.travis.yml+19−32 Data/Aeson.hs+4−4 Data/Aeson/Encoding/Builder.hs+1 Data/Aeson/Encoding/Internal.hs+1 Data/Aeson/Parser/Internal.hs+46−46 Data/Aeson/Scanner/Internal.hs−408 Data/Aeson/TH.hs+14−41 Data/Aeson/Text.hs+1 Data/Aeson/Types.hs−1 Data/Aeson/Types/FromJSON.hs+1 Data/Aeson/Types/Internal.hs+14−33 Data/Aeson/Types/ToJSON.hs+1 aeson.cabal+7−10 attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+18−3 attoparsec-iso8601/attoparsec-iso8601.cabal+4−5 benchmarks/AesonEncode.hs+2−2 benchmarks/AesonParse.hs+2−2 benchmarks/aeson-benchmarks.cabal+2−5 changelog.md+2−61 tests/ErrorMessages.hs+1−1 tests/UnitTests.hs+1−9 travis/install.sh+3−8 travis/script.sh+3−3
.travis.yml+19−32
@@ -25,59 +25,46 @@ before_cache:
2525matrix:
2626 include:
2727
28− # Do the special build steps and handle the newest and oldest
29− # GHCs first, since these are the most likely to fail.
30−
31− # HLint
3228 - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
3329 compiler: ": #GHC 8.4.4"
3430 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
3531
36− # Latest GHC
37− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.10.1
38− compiler: ": #GHC 8.10.1"
39− addons: {apt: {packages: [cabal-install-3.0,ghc-8.10.1], sources: [hvr-ghc]}}
40−
41− # Oldest GHC
42− - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
43− compiler: ": #GHC 7.8.4"
44− addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
45−
46− # Benchmarks
47− - env: BUILD=stack CABALVER=3.0 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
32+ - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
4833 compiler: ": #GHC 8.6.5"
49− addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
34+ addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
5035
51− # C unescape implementation
52− - env: BUILD=stack CABALVER=3.0 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
36+ - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
5337 compiler: ": #GHC 8.6.5"
54− addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
38+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
5539
40+ - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.1
41+ compiler: ": #GHC 8.8.1"
42+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
5643
57− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.3
58− compiler: ": #GHC 8.8.3"
59− addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.3], sources: [hvr-ghc]}}
60−
61− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.6.5
44+ - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
6245 compiler: ": #GHC 8.6.5"
63− addons: {apt: {packages: [cabal-install-3.0,ghc-8.6.5], sources: [hvr-ghc]}}
46+ addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
6447
65− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.4.4
48+ - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
6649 compiler: ": #GHC 8.4.4"
67− addons: {apt: {packages: [cabal-install-3.0,ghc-8.4.4], sources: [hvr-ghc]}}
50+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
6851
69− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.2.2
52+ - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
7053 compiler: ": #GHC 8.2.2"
71− addons: {apt: {packages: [cabal-install-3.0,ghc-8.2.2], sources: [hvr-ghc]}}
54+ addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
7255
73− - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.0.2
56+ - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
7457 compiler: ": #GHC 8.0.2"
75− addons: {apt: {packages: [cabal-install-3.0,ghc-8.0.2], sources: [hvr-ghc]}}
58+ addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
7659
7760 - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
7861 compiler: ": #GHC 7.10.3"
7962 addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
8063
64+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
65+ compiler: ": #GHC 7.8.4"
66+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
67+
8168before_install:
8269 - unset CC
8370 - export PATH=$HOME/.local/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
Data/Aeson.hs+4−4
@@ -147,9 +147,9 @@ import Prelude.Compat
147147
148148import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
149149import Data.Aeson.Encoding (encodingToLazyByteString)
150−import Data.Aeson.Scanner.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
150+import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
151151import Data.Aeson.Types
152−import Data.Aeson.Types.Internal (formatError)
152+import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
153153import qualified Data.ByteString as B
154154import qualified Data.ByteString.Lazy as L
155155
@@ -257,7 +257,7 @@ eitherDecodeStrict =
257257-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
258258eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
259259eitherDecodeFileStrict =
260− fmap eitherDecodeStrict . B.readFile
260+ fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON) . B.readFile
261261{-# INLINE eitherDecodeFileStrict #-}
262262
263263-- | Like 'decode'' but returns an error message when decoding fails.
@@ -274,7 +274,7 @@ eitherDecodeStrict' =
274274-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
275275eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
276276eitherDecodeFileStrict' =
277− fmap eitherDecodeStrict' . B.readFile
277+ fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON) . B.readFile
278278{-# INLINE eitherDecodeFileStrict' #-}
279279
280280-- $use
Data/Aeson/Encoding/Builder.hs+1
@@ -44,6 +44,7 @@ import Data.ByteString.Builder as B
4444import Data.ByteString.Builder.Prim as BP
4545import Data.ByteString.Builder.Scientific (scientificBuilder)
4646import Data.Char (chr, ord)
47+import Data.Semigroup ((<>))
4748import Data.Scientific (Scientific, base10Exponent, coefficient)
4849import Data.Text.Encoding (encodeUtf8BuilderEscaped)
4950import Data.Time (UTCTime(..))
Data/Aeson/Encoding/Internal.hs+1
@@ -63,6 +63,7 @@ import Data.Aeson.Types.Internal (Value)
6363import Data.ByteString.Builder (Builder, char7, toLazyByteString)
6464import Data.Int
6565import Data.Scientific (Scientific)
66+import Data.Semigroup (Semigroup ((<>)))
6667import Data.Text (Text)
6768import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
6869import Data.Typeable (Typeable)
Data/Aeson/Parser/Internal.hs+46−46
@@ -2,9 +2,12 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE NoImplicitPrelude #-}
44{-# LANGUAGE OverloadedStrings #-}
5+#if MIN_VERSION_ghc_prim(0,3,1)
6+{-# LANGUAGE MagicHash #-}
7+#endif
58#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
69-- Work around a compiler bug
7−{-# OPTIONS_GHC -fsimpl-tick-factor=300 #-}
10+{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
811#endif
912-- |
1013-- Module: Data.Aeson.Parser.Internal
@@ -29,7 +32,6 @@ module Data.Aeson.Parser.Internal
2932 , value
3033 , jstring
3134 , jstring_
32− , jstringSlow
3335 , scientific
3436 -- * Strict parsers
3537 , json', jsonEOF'
@@ -56,10 +58,8 @@ import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Val
5658import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
5759import Data.Function (fix)
5860import Data.Functor.Compat (($>))
59−import Data.Bits (testBit)
6061import Data.Scientific (Scientific)
6162import Data.Text (Text)
62−import qualified Data.Text.Encoding as TE
6363import Data.Vector (Vector)
6464import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
6565import qualified Data.Attoparsec.ByteString as A
@@ -67,13 +67,16 @@ import qualified Data.Attoparsec.Lazy as L
6767import qualified Data.ByteString as B
6868import qualified Data.ByteString.Unsafe as B
6969import qualified Data.ByteString.Lazy as L
70−import qualified Data.ByteString.Lazy as BSL
71−import qualified Data.ByteString.Lazy.Char8 as C
72−import qualified Data.ByteString.Builder as B
7370import qualified Data.HashMap.Strict as H
7471import qualified Data.Scientific as Sci
7572import Data.Aeson.Parser.Unescape (unescapeText)
7673
74+#if MIN_VERSION_ghc_prim(0,3,1)
75+import GHC.Base (Int#, (==#), isTrue#, word2Int#, orI#, andI#)
76+import GHC.Word (Word8(W8#))
77+import qualified Data.Text.Encoding as TE
78+#endif
79+
7780#define BACKSLASH 92
7881#define CLOSE_CURLY 125
7982#define CLOSE_SQUARE 93
@@ -320,20 +323,37 @@ jstring = A.word8 DOUBLE_QUOTE *> jstring_
320323-- | Parse a string without a leading quote.
321324jstring_ :: Parser Text
322325{-# INLINE jstring_ #-}
323−jstring_ = do
324− s <- A.takeWhile (\w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && not (testBit w 7))
325− let txt = TE.decodeUtf8 s
326− w <- A.peekWord8
327− case w of
328− Nothing -> fail "string without end"
329− Just DOUBLE_QUOTE -> A.anyWord8 $> txt
330− _ -> jstringSlow s
331−
332−jstringSlow :: B.ByteString -> Parser Text
333−{-# INLINE jstringSlow #-}
334−jstringSlow s' = {-# SCC "jstringSlow" #-} do
326+jstring_ = {-# SCC "jstring_" #-} do
327+#if MIN_VERSION_ghc_prim(0,3,1)
328+ (s, S _ escaped) <- A.runScanner startState go <* A.anyWord8
329+ -- We escape only if there are
330+ -- non-ascii (over 7bit) characters or backslash present.
331+ --
332+ -- Note: if/when text will have fast ascii -> text conversion
333+ -- (e.g. uses utf8 encoding) we can have further speedup.
334+ if isTrue# escaped
335+ then case unescapeText s of
336+ Right r -> return r
337+ Left err -> fail $ show err
338+ else return (TE.decodeUtf8 s)
339+ where
340+ startState = S 0# 0#
341+ go (S skip escaped) (W8# c)
342+ | isTrue# skip = Just (S 0# escaped')
343+ | isTrue# (w ==# 34#) = Nothing -- double quote
344+ | otherwise = Just (S skip' escaped')
345+ where
346+ w = word2Int# c
347+ skip' = w ==# 92# -- backslash
348+ escaped' = escaped
349+ `orI#` (w `andI#` 0x80# ==# 0x80#) -- c >= 0x80
350+ `orI#` skip'
351+ `orI#` (w `andI#` 0x1f# ==# w) -- c < 0x20
352+
353+data S = S Int# Int#
354+#else
335355 s <- A.scan startState go <* A.anyWord8
336− case unescapeText (B.append s' s) of
356+ case unescapeText s of
337357 Right r -> return r
338358 Left err -> fail $ show err
339359 where
@@ -344,6 +364,7 @@ jstringSlow s' = {-# SCC "jstringSlow" #-} do
344364 | otherwise = let a' = c == backslash
345365 in Just a'
346366 where backslash = BACKSLASH
367+#endif
347368
348369decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
349370decodeWith p to s =
@@ -369,35 +390,14 @@ eitherDecodeWith p to s =
369390 L.Done _ v -> case to v of
370391 ISuccess a -> Right a
371392 IError path msg -> Left (path, msg)
372− L.Fail notparsed ctx msg -> Left ([], buildMsg notparsed ctx msg)
393+ L.Fail _ ctx msg -> Left ([], buildMsg ctx msg)
373394 where
374− buildMsg :: L.ByteString -> [String] -> String -> String
375− buildMsg notYetParsed [] msg = msg ++ formatErrorLine notYetParsed
376− buildMsg notYetParsed (expectation:_) msg =
377− msg ++ ". Expecting " ++ expectation ++ formatErrorLine notYetParsed
395+ buildMsg :: [String] -> String -> String
396+ buildMsg [] msg = msg
397+ buildMsg (expectation:_) msg =
398+ msg ++ ". Expecting " ++ expectation
378399{-# INLINE eitherDecodeWith #-}
379400
380−-- | Grab the first 100 bytes from the non parsed portion and
381−-- format to get nicer error messages
382−formatErrorLine :: L.ByteString -> String
383−formatErrorLine bs =
384− C.unpack .
385− -- if formatting results in empty ByteString just return that
386− -- otherwise construct the error message with the bytestring builder
387− (\bs' ->
388− if BSL.null bs'
389− then BSL.empty
390− else
391− B.toLazyByteString $
392− B.stringUtf8 " at '" <> B.lazyByteString bs' <> B.stringUtf8 "'"
393− ) .
394− -- if newline is present cut at that position
395− BSL.takeWhile (10 /=) .
396− -- remove spaces, CR's, tabs, backslashes and quotes characters
397− BSL.filter (`notElem` [9, 13, 32, 34, 47, 92]) .
398− -- take 100 bytes
399− BSL.take 100 $ bs
400−
401401eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
402402 -> Either (JSONPath, String) a
403403eitherDecodeStrictWith p to s =
Data/Aeson/Scanner/Internal.hsdeleted−408
@@ -1,408 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
4−{-# LANGUAGE OverloadedStrings #-}
5−-- |
6−-- Module: Data.Aeson.Scanner.Internal
7−-- Copyright: (c) 2011-2016 Bryan O'Sullivan
8−-- (c) 2011 MailRank, Inc.
9−-- (c) 2020 Yuras Shumovich
10−-- License: BSD3
11−-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
12−-- Stability: experimental
13−-- Portability: portable
14−--
15−-- Efficiently and correctly parse a JSON string. The string must be
16−-- encoded as UTF-8.
17−
18−module Data.Aeson.Scanner.Internal
19− (
20− -- * Lazy scanners
21− json, jsonEOF
22− , jsonWith
23− , jsonLast
24− , jsonAccum
25− , jsonNoDup
26− , value
27− , jstring
28− , jstring_
29− , scientific
30− -- * Strict scanners
31− , json', jsonEOF'
32− , jsonWith'
33− , jsonLast'
34− , jsonAccum'
35− , jsonNoDup'
36− , value'
37− -- * Helpers
38− , decodeWith
39− , decodeStrictWith
40− , eitherDecodeWith
41− , eitherDecodeStrictWith
42− -- ** Handling objects with duplicate keys
43− , fromListAccum
44− , parseListNoDup
45− ) where
46−
47−import Prelude.Compat
48−
49−import Data.Aeson.Types.Internal
50−import qualified Data.Aeson.Parser.Internal as Parser
51−import Data.Bits (testBit)
52−import Data.ByteString (ByteString)
53−import qualified Data.ByteString.Lazy as Lazy (ByteString)
54−import Data.Function (fix)
55−import Data.Functor.Compat (($>))
56−import Data.HashMap.Strict (HashMap)
57−import qualified Data.HashMap.Strict as HashMap
58−import Data.Scientific (Scientific)
59−import Data.Text (Text)
60−import qualified Data.Text.Encoding as Text
61−import Data.Vector (Vector)
62−import qualified Data.Vector as Vector
63−import Data.Word
64−import Scanner (Scanner)
65−import qualified Scanner
66−import qualified Scanner.Attoparsec as Scanner
67−
68−#define BACKSLASH 92
69−#define CLOSE_CURLY 125
70−#define CLOSE_SQUARE 93
71−#define COMMA 44
72−#define DOUBLE_QUOTE 34
73−#define OPEN_CURLY 123
74−#define OPEN_SQUARE 91
75−#define C_0 48
76−#define C_9 57
77−#define C_A 65
78−#define C_F 70
79−#define C_a 97
80−#define C_f 102
81−#define C_n 110
82−#define C_t 116
83−
84−-- | Parse any JSON value.
85−--
86−-- The conversion of a parsed value to a Haskell value is deferred
87−-- until the Haskell value is needed. This may improve performance if
88−-- only a subset of the results of conversions are needed, but at a
89−-- cost in thunk allocation.
90−--
91−-- This function is an alias for 'value'.
92−--
93−-- ==== Warning
94−--
95−-- If an object contains duplicate keys, only the first one will be kept.
96−-- For a more flexible alternative, see 'jsonWith'.
97−json :: Scanner Value
98−json = value
99−
100−-- | Parse any JSON value.
101−--
102−-- This is a strict version of 'json' which avoids building up thunks
103−-- during parsing; it performs all conversions immediately. Prefer
104−-- this version if most of the JSON data needs to be accessed.
105−--
106−-- This function is an alias for 'value''.
107−--
108−-- ==== Warning
109−--
110−-- If an object contains duplicate keys, only the first one will be kept.
111−-- For a more flexible alternative, see 'jsonWith''.
112−json' :: Scanner Value
113−json' = value'
114−
115−-- Open recursion: object_, object_', array_, array_' are parameterized by the
116−-- toplevel Value parser to be called recursively, to keep the parameter
117−-- mkObject outside of the recursive loop for proper inlining.
118−
119−object_ :: ([(Text, Value)] -> Either String Object)
120− -> Scanner Value -> Scanner Value
121−object_ mkObject val = {-# SCC "object_" #-} Object
122− <$> objectValues mkObject jstring val
123−{-# INLINE object_ #-}
124−
125−object_' :: ([(Text, Value)] -> Either String Object)
126− -> Scanner Value -> Scanner Value
127−object_' mkObject val' = {-# SCC "object_'" #-} do
128− !vals <- objectValues mkObject jstring' val'
129− return (Object vals)
130− where
131− jstring' = do
132− !s <- jstring
133− return s
134−{-# INLINE object_' #-}
135−
136−objectValues :: ([(Text, Value)] -> Either String Object)
137− -> Scanner Text -> Scanner Value -> Scanner (HashMap Text Value)
138−objectValues mkObject str val = do
139− skipSpace
140− w <- lookAhead'
141− if w == CLOSE_CURLY
142− then Scanner.anyWord8 $> HashMap.empty
143− else loop []
144− where
145− -- Why use acc pattern here, you may ask? because 'HashMap.fromList' use
146− -- 'unsafeInsert' and it's much faster because it's doing in place update
147− -- to the 'HashMap'!
148− loop acc = do
149− k <- str <* skipSpace <* Scanner.char8 ':'
150− v <- val <* skipSpace
151− ch <- Scanner.satisfy (\w -> w == COMMA || w == CLOSE_CURLY)
152− let acc' = (k, v) : acc
153− if ch == COMMA
154− then skipSpace >> loop acc'
155− else case mkObject acc' of
156− Left err -> fail err
157− Right obj -> pure obj
158−{-# INLINE objectValues #-}
159−
160−array_ :: Scanner Value -> Scanner Value
161−array_ val = {-# SCC "array_" #-} Array <$> arrayValues val
162−{-# INLINE array_ #-}
163−
164−array_' :: Scanner Value -> Scanner Value
165−array_' val = {-# SCC "array_'" #-} do
166− !vals <- arrayValues val
167− return (Array vals)
168−{-# INLINE array_' #-}
169−
170−arrayValues :: Scanner Value -> Scanner (Vector Value)
171−arrayValues val = do
172− skipSpace
173− w <- lookAhead'
174− if w == CLOSE_SQUARE
175− then Scanner.anyWord8 $> Vector.empty
176− else loop [] 1
177− where
178− loop acc !len = do
179− v <- val <* skipSpace
180− ch <- Scanner.satisfy (\w -> w == COMMA || w == CLOSE_SQUARE)
181− if ch == COMMA
182− then skipSpace >> loop (v:acc) (len+1)
183− else return (Vector.reverse (Vector.fromListN len (v:acc)))
184−{-# INLINE arrayValues #-}
185−
186−-- | Parse any JSON value. Synonym of 'json'.
187−value :: Scanner Value
188−value = jsonWith (pure . HashMap.fromList)
189−
190−-- | Parse any JSON value.
191−--
192−-- This parser is parameterized by a function to construct an 'Object'
193−-- from a raw list of key-value pairs, where duplicates are preserved.
194−-- The pairs appear in __reverse order__ from the source.
195−--
196−-- ==== __Examples__
197−--
198−-- 'json' keeps only the first occurence of each key, using 'HashMap.Lazy.fromList'.
199−--
200−-- @
201−-- 'json' = 'jsonWith' ('Right' '.' 'HashMap.fromList')
202−-- @
203−--
204−-- 'jsonLast' keeps the last occurence of each key, using
205−-- @'HashMap.Lazy.fromListWith' ('const' 'id')@.
206−--
207−-- @
208−-- 'jsonLast' = 'jsonWith' ('Right' '.' 'HashMap.Lazy.fromListWith' ('const' 'id'))
209−-- @
210−--
211−-- 'jsonAccum' keeps wraps all values in arrays to keep duplicates, using
212−-- 'fromListAccum'.
213−--
214−-- @
215−-- 'jsonAccum' = 'jsonWith' ('Right' . 'fromListAccum')
216−-- @
217−--
218−-- 'jsonNoDup' fails if any object contains duplicate keys, using 'parseListNoDup'.
219−--
220−-- @
221−-- 'jsonNoDup' = 'jsonWith' 'parseListNoDup'
222−-- @
223−jsonWith :: ([(Text, Value)] -> Either String Object) -> Scanner Value
224−jsonWith mkObject = fix $ \value_ -> do
225− skipSpace
226− w <- lookAhead'
227− case w of
228− DOUBLE_QUOTE -> Scanner.anyWord8 *> (String <$> jstring_)
229− OPEN_CURLY -> Scanner.anyWord8 *> object_ mkObject value_
230− OPEN_SQUARE -> Scanner.anyWord8 *> array_ value_
231− C_f -> Scanner.string "false" $> Bool False
232− C_t -> Scanner.string "true" $> Bool True
233− C_n -> Scanner.string "null" $> Null
234− _ | w >= 48 && w <= 57 || w == 45
235− -> Number <$> scientific
236− | otherwise -> fail "not a valid json value"
237−{-# INLINE jsonWith #-}
238−
239−-- | Variant of 'json' which keeps only the last occurence of every key.
240−jsonLast :: Scanner Value
241−jsonLast = jsonWith (Right . HashMap.fromListWith (const id))
242−
243−-- | Variant of 'json' wrapping all object mappings in 'Array' to preserve
244−-- key-value pairs with the same keys.
245−jsonAccum :: Scanner Value
246−jsonAccum = jsonWith (Right . fromListAccum)
247−
248−-- | Variant of 'json' which fails if any object contains duplicate keys.
249−jsonNoDup :: Scanner Value
250−jsonNoDup = jsonWith parseListNoDup
251−
252−-- | @'fromListAccum' kvs@ is an object mapping keys to arrays containing all
253−-- associated values from the original list @kvs@.
254−--
255−-- >>> fromListAccum [("apple", Bool True), ("apple", Bool False), ("orange", Bool False)]
256−-- fromList [("apple", [Bool False, Bool True]), ("orange", [Bool False])]
257−fromListAccum :: [(Text, Value)] -> Object
258−fromListAccum =
259− fmap (Array . Vector.fromList . ($ []))
260− . HashMap.fromListWith (.) . (fmap . fmap) (:)
261−
262−-- | @'fromListNoDup' kvs@ fails if @kvs@ contains duplicate keys.
263−parseListNoDup :: [(Text, Value)] -> Either String Object
264−parseListNoDup =
265− HashMap.traverseWithKey unwrap . HashMap.fromListWith (\_ _ -> Nothing)
266− . (fmap . fmap) Just
267− where
268− unwrap k Nothing = Left $ "found duplicate key: " ++ show k
269− unwrap _ (Just v) = Right v
270−
271−-- | Strict version of 'value'. Synonym of 'json''.
272−value' :: Scanner Value
273−value' = jsonWith' (pure . HashMap.fromList)
274−
275−-- | Strict version of 'jsonWith'.
276−jsonWith' :: ([(Text, Value)] -> Either String Object) -> Scanner Value
277−jsonWith' mkObject = fix $ \value_ -> do
278− skipSpace
279− w <- lookAhead'
280− case w of
281− DOUBLE_QUOTE -> do
282− !s <- Scanner.anyWord8 *> jstring_
283− return (String s)
284− OPEN_CURLY -> Scanner.anyWord8 *> object_' mkObject value_
285− OPEN_SQUARE -> Scanner.anyWord8 *> array_' value_
286− C_f -> Scanner.string "false" $> Bool False
287− C_t -> Scanner.string "true" $> Bool True
288− C_n -> Scanner.string "null" $> Null
289− _ | w >= 48 && w <= 57 || w == 45
290− -> do
291− !n <- scientific
292− return (Number n)
293− | otherwise -> fail "not a valid json value"
294−{-# INLINE jsonWith' #-}
295−
296−-- | Variant of 'json'' which keeps only the last occurence of every key.
297−jsonLast' :: Scanner Value
298−jsonLast' = jsonWith' (pure . HashMap.fromListWith (const id))
299−
300−-- | Variant of 'json'' wrapping all object mappings in 'Array' to preserve
301−-- key-value pairs with the same keys.
302−jsonAccum' :: Scanner Value
303−jsonAccum' = jsonWith' (pure . fromListAccum)
304−
305−-- | Variant of 'json'' which fails if any object contains duplicate keys.
306−jsonNoDup' :: Scanner Value
307−jsonNoDup' = jsonWith' parseListNoDup
308−
309−-- | Parse a quoted JSON string.
310−jstring :: Scanner Text
311−jstring = Scanner.word8 DOUBLE_QUOTE *> jstring_
312−
313−-- | Parse a string without a leading quote.
314−jstring_ :: Scanner Text
315−{-# INLINE jstring_ #-}
316−jstring_ = do
317− s <- Scanner.takeWhile $
318− \w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && not (testBit w 7)
319− w <- lookAhead'
320− case w of
321− DOUBLE_QUOTE -> Scanner.anyWord8 $> Text.decodeUtf8 s
322− _ -> Scanner.atto $ Parser.jstringSlow s
323−
324−decodeWith :: Scanner Value -> (Value -> Result a) -> Lazy.ByteString -> Maybe a
325−decodeWith p to s =
326− case Scanner.scanLazy p s of
327− Right v -> case to v of
328− Success a -> Just a
329− _ -> Nothing
330− _ -> Nothing
331−{-# INLINE decodeWith #-}
332−
333−decodeStrictWith :: Scanner Value -> (Value -> Result a) -> ByteString
334− -> Maybe a
335−decodeStrictWith p to s =
336− case either Error to (Scanner.scanOnly p s) of
337− Success a -> Just a
338− _ -> Nothing
339−{-# INLINE decodeStrictWith #-}
340−
341−eitherDecodeWith :: Scanner Value -> (Value -> IResult a) -> Lazy.ByteString
342− -> Either (JSONPath, String) a
343−eitherDecodeWith p to s =
344− case Scanner.scanLazy p s of
345− Right v -> case to v of
346− ISuccess a -> Right a
347− IError path msg -> Left (path, msg)
348− Left msg -> Left ([], msg)
349−{-# INLINE eitherDecodeWith #-}
350−
351−eitherDecodeStrictWith :: Scanner Value -> (Value -> IResult a) -> ByteString
352− -> Either (JSONPath, String) a
353−eitherDecodeStrictWith p to s =
354− case either (IError []) to (Scanner.scanOnly p s) of
355− ISuccess a -> Right a
356− IError path msg -> Left (path, msg)
357−{-# INLINE eitherDecodeStrictWith #-}
358−
359−-- $lazy
360−--
361−-- The 'json' and 'value' parsers decouple identification from
362−-- conversion. Identification occurs immediately (so that an invalid
363−-- JSON document can be rejected as early as possible), but conversion
364−-- to a Haskell value is deferred until that value is needed.
365−--
366−-- This decoupling can be time-efficient if only a smallish subset of
367−-- elements in a JSON value need to be inspected, since the cost of
368−-- conversion is zero for uninspected elements. The trade off is an
369−-- increase in memory usage, due to allocation of thunks for values
370−-- that have not yet been converted.
371−
372−-- $strict
373−--
374−-- The 'json'' and 'value'' parsers combine identification with
375−-- conversion. They consume more CPU cycles up front, but have a
376−-- smaller memory footprint.
377−
378−-- | Parse a top-level JSON value followed by optional whitespace and
379−-- end-of-input. See also: 'json'.
380−jsonEOF :: Scanner Value
381−jsonEOF = json <* skipSpace <* endOfInput
382−
383−-- | Parse a top-level JSON value followed by optional whitespace and
384−-- end-of-input. See also: 'json''.
385−jsonEOF' :: Scanner Value
386−jsonEOF' = json' <* skipSpace <* endOfInput
387−
388−-- | The only valid whitespace in a JSON document is space, newline,
389−-- carriage return, and tab.
390−skipSpace :: Scanner ()
391−skipSpace = Scanner.skipWhile
392− $ \w -> w == 0x20 || w == 0x0a || w == 0x0d || w == 0x09
393−{-# INLINE skipSpace #-}
394−
395−endOfInput :: Scanner ()
396−endOfInput = do
397− w <- Scanner.lookAhead
398− case w of
399− Nothing -> return ()
400− Just _ -> fail "input after data"
401−
402−-- | Parse a JSON number.
403−scientific :: Scanner Scientific
404−scientific = Scanner.atto Parser.scientific
405−
406−lookAhead' :: Scanner Word8
407−lookAhead' = Scanner.lookAhead
408− >>= maybe (fail "unexpected end of input") return
Data/Aeson/TH.hs+14−41
@@ -116,10 +116,7 @@ module Data.Aeson.TH
116116 , mkLiftParseJSON2
117117 ) where
118118
119−import Prelude.Compat hiding (fail)
120−
121−-- We don't have MonadFail Q, so we should use `fail` from real `Prelude`
122−import Prelude (fail)
119+import Prelude.Compat
123120
124121import Control.Applicative ((<|>))
125122import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
@@ -1307,8 +1304,9 @@ dispatchFunByType jc jf conName tvMap list ty = do
13071304 tyVarNames :: [Name]
13081305 tyVarNames = M.keys tvMap
13091306
1310− itf <- isInTypeFamilyApp tyVarNames tyCon tyArgs
1311− if any (`mentionsName` tyVarNames) lhsArgs || itf
1307+ itf <- isTyFamily tyCon
1308+ if any (`mentionsName` tyVarNames) lhsArgs
1309+ || itf && any (`mentionsName` tyVarNames) tyArgs
13121310 then outOfPlaceTyVarError jc conName
13131311 else if any (`mentionsName` tyVarNames) rhsArgs
13141312 then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
@@ -1659,44 +1657,19 @@ isTyVar (VarT _) = True
16591657isTyVar (SigT t _) = isTyVar t
16601658isTyVar _ = False
16611659
1662−-- | Detect if a Name in a list of provided Names occurs as an argument to some
1663−-- type family. This makes an effort to exclude /oversaturated/ arguments to
1664−-- type families. For instance, if one declared the following type family:
1665−--
1666−-- @
1667−-- type family F a :: Type -> Type
1668−-- @
1669−--
1670−-- Then in the type @F a b@, we would consider @a@ to be an argument to @F@,
1671−-- but not @b@.
1672−isInTypeFamilyApp :: [Name] -> Type -> [Type] -> Q Bool
1673−isInTypeFamilyApp names tyFun tyArgs =
1674− case tyFun of
1675− ConT tcName -> go tcName
1676− _ -> return False
1677− where
1678− go :: Name -> Q Bool
1679− go tcName = do
1680− info <- reify tcName
1681− case info of
1660+-- | Is the given type a type family constructor (and not a data family constructor)?
1661+isTyFamily :: Type -> Q Bool
1662+isTyFamily (ConT n) = do
1663+ info <- reify n
1664+ return $ case info of
16821665#if MIN_VERSION_template_haskell(2,11,0)
1683− FamilyI (OpenTypeFamilyD (TypeFamilyHead _ bndrs _ _)) _
1684− -> withinFirstArgs bndrs
1685− FamilyI (ClosedTypeFamilyD (TypeFamilyHead _ bndrs _ _) _) _
1686− -> withinFirstArgs bndrs
1666+ FamilyI OpenTypeFamilyD{} _ -> True
16871667#else
1688− FamilyI (FamilyD TypeFam _ bndrs _) _
1689− -> withinFirstArgs bndrs
1690− FamilyI (ClosedTypeFamilyD _ bndrs _ _) _
1691− -> withinFirstArgs bndrs
1668+ FamilyI (FamilyD TypeFam _ _ _) _ -> True
16921669#endif
1693− _ -> return False
1694− where
1695− withinFirstArgs :: [a] -> Q Bool
1696− withinFirstArgs bndrs =
1697− let firstArgs = take (length bndrs) tyArgs
1698− argFVs = freeVariables firstArgs
1699− in return $ any (`elem` argFVs) names
1670+ FamilyI ClosedTypeFamilyD{} _ -> True
1671+ _ -> False
1672+isTyFamily _ = return False
17001673
17011674-- | Peel off a kind signature from a Type (if it has one).
17021675unSigT :: Type -> Type
Data/Aeson/Text.hs+1
@@ -26,6 +26,7 @@ import Prelude.Compat
2626
2727import Data.Aeson.Types (Value(..), ToJSON(..))
2828import Data.Aeson.Encoding (encodingToLazyByteString)
29+import Data.Semigroup ((<>))
2930import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
3031import Data.Text.Lazy.Builder
3132import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
Data/Aeson/Types.hs−1
@@ -35,7 +35,6 @@ module Data.Aeson.Types
3535 , parse
3636 , parseEither
3737 , parseMaybe
38− , parseFail
3938 , ToJSON(..)
4039 , KeyValue(..)
4140 , modifyFailure
Data/Aeson/Types/FromJSON.hs+1
@@ -96,6 +96,7 @@ import Data.Hashable (Hashable(..))
9696import Data.Int (Int16, Int32, Int64, Int8)
9797import Data.List.NonEmpty (NonEmpty(..))
9898import Data.Maybe (fromMaybe)
99+import Data.Semigroup ((<>))
99100import Data.Proxy (Proxy(..))
100101import Data.Ratio ((%), Ratio)
101102import Data.Scientific (Scientific, base10Exponent)
Data/Aeson/Types/Internal.hs+14−33
@@ -44,7 +44,6 @@ module Data.Aeson.Types.Internal
4444 , parse
4545 , parseEither
4646 , parseMaybe
47− , parseFail
4847 , modifyFailure
4948 , prependFailure
5049 , parserThrowError
@@ -95,6 +94,7 @@ import Data.HashMap.Strict (HashMap)
9594import Data.Hashable (Hashable(..))
9695import Data.List (intercalate)
9796import Data.Scientific (Scientific)
97+import Data.Semigroup (Semigroup((<>)))
9898import Data.String (IsString(..))
9999import Data.Text (Text, pack, unpack)
100100import Data.Time (UTCTime)
@@ -342,10 +342,6 @@ instance Monoid (Parser a) where
342342 mappend = (<>)
343343 {-# INLINE mappend #-}
344344
345−-- | Raise a parsing failure with some custom message.
346−parseFail :: String -> Parser a
347−parseFail = fail
348−
349345apP :: Parser (a -> b) -> Parser a -> Parser b
350346apP d e = do
351347 b <- d
@@ -425,9 +421,6 @@ instance TH.Lift Value where
425421 where a' = V.toList a
426422 lift (Object o) = [| Object (H.fromList . map (first pack) $ o') |]
427423 where o' = map (first unpack) . H.toList $ o
428−#if MIN_VERSION_template_haskell(2,16,0)
429− liftTyped = TH.unsafeTExpCoerce . TH.lift
430−#endif
431424
432425-- | The empty array.
433426emptyArray :: Value
@@ -588,53 +581,41 @@ data Options = Options
588581 -- the constructor tag. If 'False' the encoding will always
589582 -- follow the `sumEncoding`.
590583 , omitNothingFields :: Bool
591− -- ^ If 'True', record fields with a 'Nothing' value will be
592− -- omitted from the resulting object. If 'False', the resulting
584+ -- ^ If 'True' record fields with a 'Nothing' value will be
585+ -- omitted from the resulting object. If 'False' the resulting
593586 -- object will include those fields mapping to @null@.
594587 --
595− -- Note that this /does not/ affect parsing: 'Maybe' fields are
596− -- optional regardless of the value of 'omitNothingFields', subject
597− -- to the note below.
598− --
599588 -- === Note
600589 --
601590 -- Setting 'omitNothingFields' to 'True' only affects fields which are of
602− -- type 'Maybe' /uniformly/ in the 'ToJSON' instance.
603− -- In particular, if the type of a field is declared as a type variable, it
591+ -- type 'Maybe' /uniformly/ in the 'ToJSON' or 'FromJSON' instance. In
592+ -- particular, if the type of a field is declared as a type variable, it
604593 -- will not be omitted from the JSON object, unless the field is
605594 -- specialized upfront in the instance.
606595 --
607− -- The same holds for 'Maybe' fields being optional in the 'FromJSON' instance.
608− --
609596 -- ==== __Example__
610597 --
611598 -- The generic instance for the following type @Fruit@ depends on whether
612599 -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
613600 --
614601 -- @
615− -- data Fruit a = Fruit
602+ -- data Fruit a =
616603 -- { apples :: a -- A field whose type is a type variable.
617604 -- , oranges :: 'Maybe' Int
618− -- } deriving 'Generic'
605+ -- }
606+ --
607+ -- options :: 'Options'
608+ -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
619609 --
620610 -- -- apples required, oranges optional
621611 -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)).
622− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a)
612+ -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) where
613+ -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
623614 --
624615 -- -- apples optional, oranges optional
625616 -- -- In this instance, the field apples is uniformly of type ('Maybe' a).
626− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a))
627− --
628− -- options :: 'Options'
629− -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
630− --
631− -- -- apples always present in the output, oranges is omitted if 'Nothing'
632− -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit a) where
633− -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options
634− --
635− -- -- both apples and oranges are omitted if 'Nothing'
636− -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit ('Maybe' a)) where
637− -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options
617+ -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) where
618+ -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
638619 -- @
639620 , sumEncoding :: SumEncoding
640621 -- ^ Specifies how to encode constructors of a sum datatype.
Data/Aeson/Types/ToJSON.hs+1
@@ -79,6 +79,7 @@ import Data.Functor.Sum (Sum(..))
7979import Data.Int (Int16, Int32, Int64, Int8)
8080import Data.List (intersperse)
8181import Data.List.NonEmpty (NonEmpty(..))
82+import Data.Semigroup ((<>))
8283import Data.Proxy (Proxy(..))
8384import Data.Ratio (Ratio, denominator, numerator)
8485import Data.Scientific (Scientific)
aeson.cabal+7−10
@@ -1,5 +1,5 @@
11name: aeson
2−version: 1.4.7.1
2+version: 1.4.6.0
33license: BSD3
44license-file: LICENSE
55category: Text, Web, JSON
@@ -82,7 +82,6 @@ library
8282 Data.Aeson.Internal
8383 Data.Aeson.Internal.Time
8484 Data.Aeson.Parser.Internal
85− Data.Aeson.Scanner.Internal
8685
8786 -- Deprecated modules
8887 exposed-modules:
@@ -107,18 +106,18 @@ library
107106 bytestring >= 0.10.4.0 && < 0.11,
108107 containers >= 0.5.5.1 && < 0.7,
109108 deepseq >= 1.3.0.0 && < 1.5,
110− ghc-prim >= 0.2 && < 0.7,
111− template-haskell >= 2.9.0.0 && < 2.17,
109+ ghc-prim >= 0.2 && < 0.6,
110+ template-haskell >= 2.9.0.0 && < 2.16,
112111 text >= 1.2.3.0 && < 1.3,
113− time >= 1.4 && < 1.11
112+ time >= 1.4 && < 1.10
114113
115114 if impl(ghc >= 8.0)
116115 build-depends: bytestring >= 0.10.8.1
117116
118117 -- Compat
119118 build-depends:
120− base-compat-batteries >= 0.10.0 && < 0.12,
121− time-compat >= 1.9.2.2 && < 1.10
119+ base-compat >= 0.9.1 && < 0.12,
120+ time-compat >= 1.9.2.2 && < 1.10
122121
123122 if !impl(ghc >= 8.6)
124123 build-depends:
@@ -148,8 +147,6 @@ library
148147 -- Other dependencies
149148 build-depends:
150149 attoparsec >= 0.13.2.2 && < 0.14,
151− scanner >= 0.3 && < 0.4,
152− scanner-attoparsec >= 0.1 && < 0.2,
153150 dlist >= 0.8.0.4 && < 0.9,
154151 hashable >= 1.2.7.0 && < 1.4,
155152 scientific >= 0.3.6.2 && < 0.4,
@@ -211,7 +208,7 @@ test-suite tests
211208 UnitTests.NullaryConstructors
212209
213210 build-depends:
214− QuickCheck >= 2.10.0.1 && < 2.15,
211+ QuickCheck >= 2.10.0.1 && < 2.14,
215212 aeson,
216213 integer-logarithms >= 1 && <1.1,
217214 attoparsec,
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+18−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE NoImplicitPrelude #-}
23-- |
34-- Module: Data.Aeson.Internal.Time
@@ -18,10 +19,24 @@ module Data.Attoparsec.Time.Internal
1819
1920import Prelude.Compat
2021
21−import Data.Fixed (Fixed(MkFixed), Pico)
2222import Data.Int (Int64)
23−import Data.Time (TimeOfDay(..))
24−import Data.Time.Clock.Compat
23+import Data.Time
24+import Unsafe.Coerce (unsafeCoerce)
25+
26+#if MIN_VERSION_time(1,6,0)
27+
28+import Data.Time.Clock (diffTimeToPicoseconds)
29+
30+#endif
31+
32+import Data.Fixed (Pico, Fixed(MkFixed))
33+
34+#if !MIN_VERSION_time(1,6,0)
35+
36+diffTimeToPicoseconds :: DiffTime -> Integer
37+diffTimeToPicoseconds = unsafeCoerce
38+
39+#endif
2540
2641toPico :: Integer -> Pico
2742toPico = MkFixed
attoparsec-iso8601/attoparsec-iso8601.cabal+4−5
@@ -35,12 +35,11 @@ library
3535 Data.Attoparsec.Time.Internal
3636 Data.Attoparsec.Time
3737 build-depends:
38− attoparsec >= 0.13.0.1 && < 0.14.0.0,
38+ attoparsec >= 0.13.0.1,
3939 base >= 4.7 && < 5,
40− base-compat-batteries >= 0.10.0 && < 0.12,
41− time-compat >= 1.9.2.2 && < 1.10,
42− text >= 1.1.1.0 && < 1.3.0.0,
43− time >= 1.1.1.4 && < 1.11
40+ base-compat >= 0.9.1 && < 0.12,
41+ text >= 1.1.1.0,
42+ time >= 1.1.1.4
4443
4544 if flag(fast)
4645 ghc-options: -O0
benchmarks/AesonEncode.hs+2−2
@@ -9,7 +9,7 @@ import Prelude.Compat
99import Control.DeepSeq
1010import Control.Monad (forM_)
1111import Data.Aeson
12−import Scanner (Result(..), scanWith)
12+import Data.Attoparsec.ByteString (IResult(..), parseWith)
1313import Data.Char (isDigit)
1414import Data.Time.Clock
1515import System.Environment (getArgs)
@@ -27,7 +27,7 @@ main = do
2727 forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
2828 putStrLn $ arg ++ ":"
2929 let refill = B.hGet h 16384
30− result0 <- scanWith refill json =<< refill
30+ result0 <- parseWith refill json =<< refill
3131 r0 <- case result0 of
3232 Done _ r -> return r
3333 _ -> fail $ "failed to read " ++ show arg
benchmarks/AesonParse.hs+2−2
@@ -8,7 +8,7 @@ import Prelude.Compat
88
99import Data.Aeson
1010import Control.Monad
11−import Scanner (Result(..), scanWith)
11+import Data.Attoparsec.ByteString (IResult(..), parseWith)
1212import Data.Time.Clock
1313import System.Environment (getArgs)
1414import System.IO
@@ -27,7 +27,7 @@ main = do
2727 | otherwise = do
2828 hSeek h AbsoluteSeek 0
2929 let refill = B.hGet h blkSize
30− result <- scanWith refill json =<< refill
30+ result <- parseWith refill json =<< refill
3131 case result of
3232 Done _ _ -> loop (good+1) bad
3333 _ -> loop good (bad+1)
benchmarks/aeson-benchmarks.cabal+2−5
@@ -29,8 +29,6 @@ library
2929 c-sources: ../cbits/unescape_string.c
3030 build-depends:
3131 attoparsec,
32− scanner,
33− scanner-attoparsec,
3432 base,
3533 base-compat,
3634 containers,
@@ -86,7 +84,6 @@ library
8684 Data.Aeson.Parser.Unescape
8785 Data.Aeson.Parser.UnescapeFFI
8886 Data.Aeson.Parser.UnescapePure
89− Data.Aeson.Scanner.Internal
9087 Data.Aeson.TH
9188 Data.Aeson.Text
9289 Data.Aeson.Types
@@ -251,7 +248,7 @@ executable aeson-benchmark-aeson-encode
251248 ghc-options: -Wall -O2 -rtsopts
252249 build-depends:
253250 aeson-benchmarks,
254− scanner,
251+ attoparsec,
255252 base,
256253 base-compat,
257254 bytestring,
@@ -264,7 +261,7 @@ executable aeson-benchmark-aeson-parse
264261 ghc-options: -Wall -O2 -rtsopts
265262 build-depends:
266263 aeson-benchmarks,
267− scanner,
264+ attoparsec,
268265 base,
269266 base-compat,
270267 bytestring,
changelog.md+2−61
@@ -1,68 +1,9 @@
11For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
22
3−#### 1.4.7.1
4−
5−* GHC 8.10 compatibility, thanks to Ryan Scott.
6−
7−### 1.4.7.0
8−
9−Long overdue release (once again), so there's quite a bit of stuff
10−included even though it's a "minor" release. Big thanks to all the
11−contributors, the project would not exist without you!
12−
13−Special thanks to Oleg Grenrus and Xia Li-Yao for reviewing tons
14−of stuff.
15−
16−New stuff:
17−
18−* Add `rejectUnknownFields` to Options which rejects unknown fields on
19− deserialization. Useful to find errors during development, but
20− enabling this should be considered a breaking change as previously
21− accepted inputs may now be rejected. Thanks to rmanne.
22−
23−```
24−instance FromJSON Foo where
25− parseJSON = gParseJSON defaultOptions { rejectUnknownFields = True }
26−```
3+### upcoming
274
285* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
29− standard `{numerator=..., denumerator=...}` encoding. Thanks to
30− Aleksey Khudyakov.
31−
32−* Add more information to parse errors, including a sample of the
33− surrounding text. Hopefully this will lead to less "Failed to read:
34− satisfy" confusion! Thanks to Sasha Bogicevic. We expect some
35− downstream test suites to break because of this, apologies in
36− advance. Hopefully you will like the improvement anyway :-)
37−
38−* Add `parseFail` to `Data.Aeson.Types`. `parseFail = fail` but
39− doesn't require users to know about `MonadFail`. Thanks to Colin
40− Woodbury.
41−
42−* Make Template Haskell type family detection smarter when deriving
43− `ToJSON1` instances, thanks to Ryan Scott.
44−
45−* Optimize string parsing for the common case of strings without
46− escapes, thanks to Yuras.
47−
48−
49−Misc:
50−
51−* Clean up compiler warnings and switch from base-compat to
52− base-compat-batteries. Thanks to Colin Woodbury & Oleg Grenrus.
53−
54−* Clarification & fixes to documentation regarding treatment of Maybe fields, thanks to Roman Cheplyaka.
55−
56−* Add documentation for internal development workflows. Thanks to Guru
57− Devanla.
58−
59−* Drop support for GHC < 7.8. We've chosen to support older GHCs as
60− long as it doesn't prevent us from adding new features, but now it
61− does! Thanks to Oleg Grenrus for the patch.
62−
63−* Allow generic-deriving 1.13 in test suite.
64−
65−* Some DRY fixes thanks to Mark Fajkus.
6+ standard `{numerator=..., denumerator=...}` encoding.
667
678### 1.4.6.0
689
tests/ErrorMessages.hs+1−1
@@ -11,7 +11,7 @@ import Prelude.Compat
1111
1212import Data.Aeson (FromJSON(..), Value, json)
1313import Data.Aeson.Types (Parser)
14−import Data.Aeson.Scanner.Internal (eitherDecodeWith)
14+import Data.Aeson.Parser (eitherDecodeWith)
1515import Data.Aeson.Internal (formatError, iparse)
1616import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff)
1717import Data.Proxy (Proxy(..))
tests/UnitTests.hs+1−9
@@ -5,7 +5,6 @@
55{-# LANGUAGE OverloadedStrings #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TemplateHaskell #-}
8−{-# LANGUAGE TypeFamilies #-}
98{-# LANGUAGE QuasiQuotes #-}
109
1110-- For Data.Aeson.Types.camelTo
@@ -267,7 +266,7 @@ formatPathExample =
267266
268267formatRelativePathExample :: Assertion
269268formatRelativePathExample =
270− let rhs = formatRelativePath [Key "x", Index 0]
269+ let rhs = formatPath [Key "x", Index 0]
271270 lhs = ".x[0]"
272271 in assertEqual "formatRelativePath example" lhs rhs
273272
@@ -748,11 +747,6 @@ bigNaturalKeyDecoding =
748747 (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
749748 ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
750749
751−-- A regression test for: https://github.com/bos/aeson/issues/757
752−type family Fam757 :: * -> *
753−type instance Fam757 = Maybe
754−newtype Newtype757 a = MkNewtype757 (Fam757 a)
755−
756750deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
757751
758752deriveToJSON defaultOptions ''Foo
@@ -764,5 +758,3 @@ deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
764758deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
765759deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
766760deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
767−
768−deriveToJSON1 defaultOptions ''Newtype757
travis/install.sh+3−8
@@ -50,14 +50,9 @@ case $BUILD in
5050 fi
5151 ;;
5252 cabal2)
53− cabal v2-update -v
53+ cabal v2-update -v
5454 sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
5555
56− cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
57− ;;
58− attoparsec-iso8601)
59− cd attoparsec-iso8601
60− cabal v2-update -v
61− sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
62− cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
56+ cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
57+ ;;
6358esac
travis/script.sh+3−3
@@ -19,9 +19,9 @@ case $BUILD in
1919 SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
2020 (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
2121 ;;
22− cabal2)
23− cabal v2-build all --enable-tests --enable-benchmarks
24− cabal v2-test all --enable-tests --enable-benchmarks
22+ cabal2)
23+ cabal v2-build --enable-tests --enable-benchmarks
24+ cabal v2-test --enable-tests --enable-benchmarks
2525 ;;
2626 hlint)
2727 make lint