on e0dcca7 help sign in
17 files changed+242−122
since splitsnapshots
unifiedsplit
.travis.yml+29−11 Data/Aeson.hs+3−3 Data/Aeson/Encoding/Builder.hs−1 Data/Aeson/Encoding/Internal.hs−1 Data/Aeson/Parser/Internal.hs+45−46 Data/Aeson/TH.hs+41−14 Data/Aeson/Text.hs−1 Data/Aeson/Types.hs+1 Data/Aeson/Types/FromJSON.hs−1 Data/Aeson/Types/Internal.hs+33−14 Data/Aeson/Types/ToJSON.hs−1 aeson.cabal+5−5 attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+3−18 changelog.md+61−2 tests/UnitTests.hs+8 travis/install.sh+8−3 travis/script.sh+5−1
.travis.yml+29−11
@@ -25,35 +25,57 @@ 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
2832 - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
2933 compiler: ": #GHC 8.4.4"
3034 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
3135
36+ # attoparsec-iso8601
37+ - env: BUILD=attoparsec-iso8601 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+ # Latest GHC
42+ - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.10.1
43+ compiler: ": #GHC 8.10.1"
44+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.10.1], sources: [hvr-ghc]}}
45+
46+ # Oldest GHC
47+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
48+ compiler: ": #GHC 7.8.4"
49+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
50+
51+ # Benchmarks
3252 - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
3353 compiler: ": #GHC 8.6.5"
3454 addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
3555
56+ # C unescape implementation
3657 - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
3758 compiler: ": #GHC 8.6.5"
3859 addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
3960
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]}}
4361
44− - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
62+ - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.3
63+ compiler: ": #GHC 8.8.3"
64+ addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.3], sources: [hvr-ghc]}}
65+
66+ - env: BUILD=cabal2 CABALVER=2.4 GHCVER=8.6.5
4567 compiler: ": #GHC 8.6.5"
4668 addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
4769
48− - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
70+ - env: BUILD=cabal2 CABALVER=2.2 GHCVER=8.4.4
4971 compiler: ": #GHC 8.4.4"
5072 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
5173
52− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
74+ - env: BUILD=cabal2 CABALVER=2.0 GHCVER=8.2.2
5375 compiler: ": #GHC 8.2.2"
5476 addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
5577
56− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
78+ - env: BUILD=cabal2 CABALVER=2.0 GHCVER=8.0.2
5779 compiler: ": #GHC 8.0.2"
5880 addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
5981
@@ -61,10 +83,6 @@ matrix:
6183 compiler: ": #GHC 7.10.3"
6284 addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
6385
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−
6886before_install:
6987 - unset CC
7088 - export PATH=$HOME/.local/bin:/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
Data/Aeson.hs+3−3
@@ -149,7 +149,7 @@ import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
149149import Data.Aeson.Encoding (encodingToLazyByteString)
150150import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
151151import Data.Aeson.Types
152−import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
152+import Data.Aeson.Types.Internal (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 (eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON) . B.readFile
260+ fmap eitherDecodeStrict . 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 (eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON) . B.readFile
277+ fmap eitherDecodeStrict' . B.readFile
278278{-# INLINE eitherDecodeFileStrict' #-}
279279
280280-- $use
Data/Aeson/Encoding/Builder.hs−1
@@ -44,7 +44,6 @@ 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 ((<>))
4847import Data.Scientific (Scientific, base10Exponent, coefficient)
4948import Data.Text.Encoding (encodeUtf8BuilderEscaped)
5049import Data.Time (UTCTime(..))
Data/Aeson/Encoding/Internal.hs−1
@@ -63,7 +63,6 @@ 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 ((<>)))
6766import Data.Text (Text)
6867import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
6968import Data.Typeable (Typeable)
Data/Aeson/Parser/Internal.hs+45−46
@@ -2,12 +2,9 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE NoImplicitPrelude #-}
44{-# LANGUAGE OverloadedStrings #-}
5−#if MIN_VERSION_ghc_prim(0,3,1)
6−{-# LANGUAGE MagicHash #-}
7−#endif
85#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
96-- Work around a compiler bug
10−{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
7+{-# OPTIONS_GHC -fsimpl-tick-factor=300 #-}
118#endif
129-- |
1310-- Module: Data.Aeson.Parser.Internal
@@ -58,8 +55,10 @@ import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Val
5855import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
5956import Data.Function (fix)
6057import Data.Functor.Compat (($>))
58+import Data.Bits (testBit)
6159import Data.Scientific (Scientific)
6260import Data.Text (Text)
61+import qualified Data.Text.Encoding as TE
6362import Data.Vector (Vector)
6463import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
6564import qualified Data.Attoparsec.ByteString as A
@@ -67,16 +66,13 @@ import qualified Data.Attoparsec.Lazy as L
6766import qualified Data.ByteString as B
6867import qualified Data.ByteString.Unsafe as B
6968import qualified Data.ByteString.Lazy as L
69+import qualified Data.ByteString.Lazy as BSL
70+import qualified Data.ByteString.Lazy.Char8 as C
71+import qualified Data.ByteString.Builder as B
7072import qualified Data.HashMap.Strict as H
7173import qualified Data.Scientific as Sci
7274import Data.Aeson.Parser.Unescape (unescapeText)
7375
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−
8076#define BACKSLASH 92
8177#define CLOSE_CURLY 125
8278#define CLOSE_SQUARE 93
@@ -323,37 +319,20 @@ jstring = A.word8 DOUBLE_QUOTE *> jstring_
323319-- | Parse a string without a leading quote.
324320jstring_ :: Parser Text
325321{-# INLINE jstring_ #-}
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
322+jstring_ = do
323+ s <- A.takeWhile (\w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && not (testBit w 7))
324+ let txt = TE.decodeUtf8 s
325+ w <- A.peekWord8
326+ case w of
327+ Nothing -> fail "string without end"
328+ Just DOUBLE_QUOTE -> A.anyWord8 $> txt
329+ _ -> jstringSlow s
330+
331+jstringSlow :: B.ByteString -> Parser Text
332+{-# INLINE jstringSlow #-}
333+jstringSlow s' = {-# SCC "jstringSlow" #-} do
355334 s <- A.scan startState go <* A.anyWord8
356− case unescapeText s of
335+ case unescapeText (B.append s' s) of
357336 Right r -> return r
358337 Left err -> fail $ show err
359338 where
@@ -364,7 +343,6 @@ data S = S Int# Int#
364343 | otherwise = let a' = c == backslash
365344 in Just a'
366345 where backslash = BACKSLASH
367−#endif
368346
369347decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
370348decodeWith p to s =
@@ -390,14 +368,35 @@ eitherDecodeWith p to s =
390368 L.Done _ v -> case to v of
391369 ISuccess a -> Right a
392370 IError path msg -> Left (path, msg)
393− L.Fail _ ctx msg -> Left ([], buildMsg ctx msg)
371+ L.Fail notparsed ctx msg -> Left ([], buildMsg notparsed ctx msg)
394372 where
395− buildMsg :: [String] -> String -> String
396− buildMsg [] msg = msg
397− buildMsg (expectation:_) msg =
398− msg ++ ". Expecting " ++ expectation
373+ buildMsg :: L.ByteString -> [String] -> String -> String
374+ buildMsg notYetParsed [] msg = msg ++ formatErrorLine notYetParsed
375+ buildMsg notYetParsed (expectation:_) msg =
376+ msg ++ ". Expecting " ++ expectation ++ formatErrorLine notYetParsed
399377{-# INLINE eitherDecodeWith #-}
400378
379+-- | Grab the first 100 bytes from the non parsed portion and
380+-- format to get nicer error messages
381+formatErrorLine :: L.ByteString -> String
382+formatErrorLine bs =
383+ C.unpack .
384+ -- if formatting results in empty ByteString just return that
385+ -- otherwise construct the error message with the bytestring builder
386+ (\bs' ->
387+ if BSL.null bs'
388+ then BSL.empty
389+ else
390+ B.toLazyByteString $
391+ B.stringUtf8 " at '" <> B.lazyByteString bs' <> B.stringUtf8 "'"
392+ ) .
393+ -- if newline is present cut at that position
394+ BSL.takeWhile (10 /=) .
395+ -- remove spaces, CR's, tabs, backslashes and quotes characters
396+ BSL.filter (`notElem` [9, 13, 32, 34, 47, 92]) .
397+ -- take 100 bytes
398+ BSL.take 100 $ bs
399+
401400eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
402401 -> Either (JSONPath, String) a
403402eitherDecodeStrictWith p to s =
Data/Aeson/TH.hs+41−14
@@ -116,7 +116,10 @@ module Data.Aeson.TH
116116 , mkLiftParseJSON2
117117 ) where
118118
119−import Prelude.Compat
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)
120123
121124import Control.Applicative ((<|>))
122125import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
@@ -1304,9 +1307,8 @@ dispatchFunByType jc jf conName tvMap list ty = do
13041307 tyVarNames :: [Name]
13051308 tyVarNames = M.keys tvMap
13061309
1307− itf <- isTyFamily tyCon
1308− if any (`mentionsName` tyVarNames) lhsArgs
1309− || itf && any (`mentionsName` tyVarNames) tyArgs
1310+ itf <- isInTypeFamilyApp tyVarNames tyCon tyArgs
1311+ if any (`mentionsName` tyVarNames) lhsArgs || itf
13101312 then outOfPlaceTyVarError jc conName
13111313 else if any (`mentionsName` tyVarNames) rhsArgs
13121314 then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
@@ -1657,19 +1659,44 @@ isTyVar (VarT _) = True
16571659isTyVar (SigT t _) = isTyVar t
16581660isTyVar _ = False
16591661
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
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
16651682#if MIN_VERSION_template_haskell(2,11,0)
1666− FamilyI OpenTypeFamilyD{} _ -> True
1683+ FamilyI (OpenTypeFamilyD (TypeFamilyHead _ bndrs _ _)) _
1684+ -> withinFirstArgs bndrs
1685+ FamilyI (ClosedTypeFamilyD (TypeFamilyHead _ bndrs _ _) _) _
1686+ -> withinFirstArgs bndrs
16671687#else
1668− FamilyI (FamilyD TypeFam _ _ _) _ -> True
1688+ FamilyI (FamilyD TypeFam _ bndrs _) _
1689+ -> withinFirstArgs bndrs
1690+ FamilyI (ClosedTypeFamilyD _ bndrs _ _) _
1691+ -> withinFirstArgs bndrs
16691692#endif
1670− FamilyI ClosedTypeFamilyD{} _ -> True
1671− _ -> False
1672−isTyFamily _ = return False
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
16731700
16741701-- | Peel off a kind signature from a Type (if it has one).
16751702unSigT :: Type -> Type
Data/Aeson/Text.hs−1
@@ -26,7 +26,6 @@ import Prelude.Compat
2626
2727import Data.Aeson.Types (Value(..), ToJSON(..))
2828import Data.Aeson.Encoding (encodingToLazyByteString)
29−import Data.Semigroup ((<>))
3029import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
3130import Data.Text.Lazy.Builder
3231import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
Data/Aeson/Types.hs+1
@@ -35,6 +35,7 @@ module Data.Aeson.Types
3535 , parse
3636 , parseEither
3737 , parseMaybe
38+ , parseFail
3839 , ToJSON(..)
3940 , KeyValue(..)
4041 , modifyFailure
Data/Aeson/Types/FromJSON.hs−1
@@ -96,7 +96,6 @@ import Data.Hashable (Hashable(..))
9696import Data.Int (Int16, Int32, Int64, Int8)
9797import Data.List.NonEmpty (NonEmpty(..))
9898import Data.Maybe (fromMaybe)
99−import Data.Semigroup ((<>))
10099import Data.Proxy (Proxy(..))
101100import Data.Ratio ((%), Ratio)
102101import Data.Scientific (Scientific, base10Exponent)
Data/Aeson/Types/Internal.hs+33−14
@@ -44,6 +44,7 @@ module Data.Aeson.Types.Internal
4444 , parse
4545 , parseEither
4646 , parseMaybe
47+ , parseFail
4748 , modifyFailure
4849 , prependFailure
4950 , parserThrowError
@@ -94,7 +95,6 @@ import Data.HashMap.Strict (HashMap)
9495import Data.Hashable (Hashable(..))
9596import Data.List (intercalate)
9697import Data.Scientific (Scientific)
97−import Data.Semigroup (Semigroup((<>)))
9898import Data.String (IsString(..))
9999import Data.Text (Text, pack, unpack)
100100import Data.Time (UTCTime)
@@ -342,6 +342,10 @@ 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+
345349apP :: Parser (a -> b) -> Parser a -> Parser b
346350apP d e = do
347351 b <- d
@@ -421,6 +425,9 @@ instance TH.Lift Value where
421425 where a' = V.toList a
422426 lift (Object o) = [| Object (H.fromList . map (first pack) $ o') |]
423427 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
424431
425432-- | The empty array.
426433emptyArray :: Value
@@ -581,41 +588,53 @@ data Options = Options
581588 -- the constructor tag. If 'False' the encoding will always
582589 -- follow the `sumEncoding`.
583590 , omitNothingFields :: Bool
584− -- ^ If 'True' record fields with a 'Nothing' value will be
585− -- omitted from the resulting object. If 'False' the resulting
591+ -- ^ If 'True', record fields with a 'Nothing' value will be
592+ -- omitted from the resulting object. If 'False', the resulting
586593 -- object will include those fields mapping to @null@.
587594 --
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+ --
588599 -- === Note
589600 --
590601 -- Setting 'omitNothingFields' to 'True' only affects fields which are of
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
602+ -- type 'Maybe' /uniformly/ in the 'ToJSON' instance.
603+ -- In particular, if the type of a field is declared as a type variable, it
593604 -- will not be omitted from the JSON object, unless the field is
594605 -- specialized upfront in the instance.
595606 --
607+ -- The same holds for 'Maybe' fields being optional in the 'FromJSON' instance.
608+ --
596609 -- ==== __Example__
597610 --
598611 -- The generic instance for the following type @Fruit@ depends on whether
599612 -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
600613 --
601614 -- @
602− -- data Fruit a =
615+ -- data Fruit a = Fruit
603616 -- { apples :: a -- A field whose type is a type variable.
604617 -- , oranges :: 'Maybe' Int
605− -- }
606− --
607− -- options :: 'Options'
608− -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
618+ -- } deriving 'Generic'
609619 --
610620 -- -- apples required, oranges optional
611621 -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)).
612− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) where
613− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
622+ -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a)
614623 --
615624 -- -- apples optional, oranges optional
616625 -- -- In this instance, the field apples is uniformly of type ('Maybe' a).
617− -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) where
618− -- 'Data.Aeson.fromJSON' = 'Data.Aeson.genericFromJSON' options
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
619638 -- @
620639 , sumEncoding :: SumEncoding
621640 -- ^ Specifies how to encode constructors of a sum datatype.
Data/Aeson/Types/ToJSON.hs−1
@@ -79,7 +79,6 @@ 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 ((<>))
8382import Data.Proxy (Proxy(..))
8483import Data.Ratio (Ratio, denominator, numerator)
8584import Data.Scientific (Scientific)
aeson.cabal+5−5
@@ -1,5 +1,5 @@
11name: aeson
2−version: 1.4.6.0
2+version: 1.4.7.1
33license: BSD3
44license-file: LICENSE
55category: Text, Web, JSON
@@ -106,8 +106,8 @@ library
106106 bytestring >= 0.10.4.0 && < 0.11,
107107 containers >= 0.5.5.1 && < 0.7,
108108 deepseq >= 1.3.0.0 && < 1.5,
109− ghc-prim >= 0.2 && < 0.6,
110− template-haskell >= 2.9.0.0 && < 2.16,
109+ ghc-prim >= 0.2 && < 0.7,
110+ template-haskell >= 2.9.0.0 && < 2.17,
111111 text >= 1.2.3.0 && < 1.3,
112112 time >= 1.4 && < 1.10
113113
@@ -116,8 +116,8 @@ library
116116
117117 -- Compat
118118 build-depends:
119− base-compat >= 0.9.1 && < 0.12,
120− time-compat >= 1.9.2.2 && < 1.10
119+ base-compat-batteries >= 0.10.0 && < 0.12,
120+ time-compat >= 1.9.2.2 && < 1.10
121121
122122 if !impl(ghc >= 8.6)
123123 build-depends:
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+3−18
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE NoImplicitPrelude #-}
32-- |
43-- Module: Data.Aeson.Internal.Time
@@ -19,24 +18,10 @@ module Data.Attoparsec.Time.Internal
1918
2019import Prelude.Compat
2120
21+import Data.Fixed (Fixed(MkFixed), Pico)
2222import Data.Int (Int64)
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
23+import Data.Time (TimeOfDay(..))
24+import Data.Time.Clock.Compat
4025
4126toPico :: Integer -> Pico
4227toPico = MkFixed
changelog.md+61−2
@@ -1,9 +1,68 @@
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−### upcoming
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+```
427
528* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− standard `{numerator=..., denumerator=...}` encoding.
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.
766
867### 1.4.6.0
968
tests/UnitTests.hs+8
@@ -5,6 +5,7 @@
55{-# LANGUAGE OverloadedStrings #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
89{-# LANGUAGE QuasiQuotes #-}
910
1011-- For Data.Aeson.Types.camelTo
@@ -747,6 +748,11 @@ bigNaturalKeyDecoding =
747748 (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
748749 ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
749750
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+
750756deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
751757
752758deriveToJSON defaultOptions ''Foo
@@ -758,3 +764,5 @@ deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
758764deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
759765deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
760766deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
767+
768+deriveToJSON1 defaultOptions ''Newtype757
travis/install.sh+8−3
@@ -50,9 +50,14 @@ 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− ;;
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
5863esac
travis/script.sh+5−1
@@ -19,7 +19,11 @@ 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)
22+ cabal2)
23+ cabal v2-build --enable-tests --enable-benchmarks
24+ cabal v2-test --enable-tests --enable-benchmarks
25+ ;;
26+ attoparsec-iso8601)
2327 cabal v2-build --enable-tests --enable-benchmarks
2428 cabal v2-test --enable-tests --enable-benchmarks
2529 ;;