packages feed

aeson 1.4.1.0 → 1.4.2.0

raw patch · 19 files changed

+376/−116 lines, 19 filesdep +contravariantdep ~attoparsecdep ~basedep ~base-orphansPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: contravariant

Dependency ranges changed: attoparsec, base, base-orphans, containers, deepseq, dlist, ghc-prim, hashable, nats, primitive, scientific, semigroups, tagged, template-haskell, text, th-abstraction, time, time-locale-compat, transformers, transformers-compat, unordered-containers, vector

API changes (from Hackage documentation)

- Data.Aeson.TH: instance Data.Aeson.TH.LookupField (GHC.Base.Maybe a)
+ Data.Aeson: infixr 8 .=
+ Data.Aeson.QQ.Simple: aesonQQ :: QuasiQuoter
+ Data.Aeson.TH: instance Data.Aeson.TH.LookupField (GHC.Maybe.Maybe a)
+ Data.Aeson.Types: infixr 8 .=
- Data.Aeson: (.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson: (.:!) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson: (.:) :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson: (.:) :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson: (.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson: (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson: FromJSONKeyCoerce :: !(CoerceText a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyText :: !(Text -> a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyTextParser :: !(Text -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyValue :: !(Value -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: ToJSONKeyText :: !(a -> Text) -> !(a -> Encoding' Text) -> ToJSONKeyFunction a
+ Data.Aeson: ToJSONKeyText :: !a -> Text -> !a -> Encoding' Text -> ToJSONKeyFunction a
- Data.Aeson: ToJSONKeyValue :: !(a -> Value) -> !(a -> Encoding) -> ToJSONKeyFunction a
+ Data.Aeson: ToJSONKeyValue :: !a -> Value -> !a -> Encoding -> ToJSONKeyFunction a
- Data.Aeson: decode :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decode :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decode' :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decode' :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a)
+ Data.Aeson: decodeFileStrict :: FromJSON a => FilePath -> IO (Maybe a)
- Data.Aeson: decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a)
+ Data.Aeson: decodeFileStrict' :: FromJSON a => FilePath -> IO (Maybe a)
- Data.Aeson: decodeStrict :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decodeStrict :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: decodeStrict' :: (FromJSON a) => ByteString -> Maybe a
+ Data.Aeson: decodeStrict' :: FromJSON a => ByteString -> Maybe a
- Data.Aeson: eitherDecode :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecode :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecode' :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecode' :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
+ Data.Aeson: eitherDecodeFileStrict :: FromJSON a => FilePath -> IO (Either String a)
- Data.Aeson: eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
+ Data.Aeson: eitherDecodeFileStrict' :: FromJSON a => FilePath -> IO (Either String a)
- Data.Aeson: eitherDecodeStrict :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecodeStrict :: FromJSON a => ByteString -> Either String a
- Data.Aeson: eitherDecodeStrict' :: (FromJSON a) => ByteString -> Either String a
+ Data.Aeson: eitherDecodeStrict' :: FromJSON a => ByteString -> Either String a
- Data.Aeson: encode :: (ToJSON a) => a -> ByteString
+ Data.Aeson: encode :: ToJSON a => a -> ByteString
- Data.Aeson: encodeFile :: (ToJSON a) => FilePath -> a -> IO ()
+ Data.Aeson: encodeFile :: ToJSON a => FilePath -> a -> IO ()
- Data.Aeson: fromJSON :: (FromJSON a) => Value -> Result a
+ Data.Aeson: fromJSON :: FromJSON a => Value -> Result a
- Data.Aeson.Encoding.Internal: Value :: (Encoding' Series) -> Series
+ Data.Aeson.Encoding.Internal: Value :: Encoding' Series -> Series
- Data.Aeson.Internal: ifromJSON :: (FromJSON a) => Value -> IResult a
+ Data.Aeson.Internal: ifromJSON :: FromJSON a => Value -> IResult a
- Data.Aeson.Types: (.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: (.:!) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: (.:) :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson.Types: (.:) :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson.Types: (.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: (.:?) :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: FromJSONKeyCoerce :: !(CoerceText a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyText :: !(Text -> a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyTextParser :: !(Text -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyValue :: !(Value -> Parser a) -> FromJSONKeyFunction a
+ Data.Aeson.Types: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: ToJSONKeyText :: !(a -> Text) -> !(a -> Encoding' Text) -> ToJSONKeyFunction a
+ Data.Aeson.Types: ToJSONKeyText :: !a -> Text -> !a -> Encoding' Text -> ToJSONKeyFunction a
- Data.Aeson.Types: ToJSONKeyValue :: !(a -> Value) -> !(a -> Encoding) -> ToJSONKeyFunction a
+ Data.Aeson.Types: ToJSONKeyValue :: !a -> Value -> !a -> Encoding -> ToJSONKeyFunction a
- Data.Aeson.Types: fromJSON :: (FromJSON a) => Value -> Result a
+ Data.Aeson.Types: fromJSON :: FromJSON a => Value -> Result a
- Data.Aeson.Types: parseField :: (FromJSON a) => Object -> Text -> Parser a
+ Data.Aeson.Types: parseField :: FromJSON a => Object -> Text -> Parser a
- Data.Aeson.Types: parseFieldMaybe :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: parseFieldMaybe :: FromJSON a => Object -> Text -> Parser (Maybe a)
- Data.Aeson.Types: parseFieldMaybe' :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
+ Data.Aeson.Types: parseFieldMaybe' :: FromJSON a => Object -> Text -> Parser (Maybe a)

Files

Data/Aeson/Parser/Internal.hs view
@@ -328,12 +328,11 @@  decimal0 :: Parser Integer decimal0 = do-  let step a w = a * 10 + fromIntegral (w - zero)-      zero = 48+  let zero = 48   digits <- A.takeWhile1 isDigit_w8   if B.length digits > 1 && B.unsafeHead digits == zero     then fail "leading zero"-    else return (B.foldl' step 0 digits)+    else return (bsToInteger digits)  -- | Parse a JSON number. scientific :: Parser Scientific@@ -366,3 +365,53 @@       fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>     return (Sci.scientific signedCoeff    e) {-# INLINE scientific #-}++------------------ Copy-pasted and adapted from base ------------------------++bsToInteger :: B.ByteString -> Integer+bsToInteger bs+    | l > 40    = valInteger 10 l [ fromIntegral (w - 48) | w <- B.unpack bs ]+    | otherwise = bsToIntegerSimple bs+  where+    l = B.length bs++bsToIntegerSimple :: B.ByteString -> Integer+bsToIntegerSimple = B.foldl' step 0 where+  step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'++-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b+-- digits are combined into a single radix b^2 digit. This process is+-- repeated until we are left with a single digit. This algorithm+-- performs well only on large inputs, so we use the simple algorithm+-- for smaller inputs.+valInteger :: Integer -> Int -> [Integer] -> Integer+valInteger = go+  where+    go :: Integer -> Int -> [Integer] -> Integer+    go _ _ []  = 0+    go _ _ [d] = d+    go b l ds+        | l > 40 = b' `seq` go b' l' (combine b ds')+        | otherwise = valSimple b ds+      where+        -- ensure that we have an even number of digits+        -- before we call combine:+        ds' = if even l then ds else 0 : ds+        b' = b * b+        l' = (l + 1) `quot` 2++    combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)+      where+        d = d1 * b + d2+    combine _ []  = []+    combine _ [_] = errorWithoutStackTrace "this should not happen"++-- The following algorithm is only linear for types whose Num operations+-- are in constant time.+valSimple :: Integer -> [Integer] -> Integer+valSimple base = go 0+  where+    go r [] = r+    go r (d : ds) = r' `seq` go r' ds+      where+        r' = r * base + fromIntegral d
+ Data/Aeson/QQ/Simple.hs view
@@ -0,0 +1,25 @@+-- | Like "Data.Aeson.QQ" but without interpolation.+module Data.Aeson.QQ.Simple (aesonQQ) where++import           Data.Aeson+import qualified Data.Text                  as T+import qualified Data.Text.Encoding         as TE+import           Language.Haskell.TH+import           Language.Haskell.TH.Quote+import           Language.Haskell.TH.Syntax (Lift (..))+import           Prelude                    ()+import           Prelude.Compat++aesonQQ :: QuasiQuoter+aesonQQ = QuasiQuoter+    { quoteExp  = aesonExp+    , quotePat  = const $ error "No quotePat defined for jsonQQ"+    , quoteType = const $ error "No quoteType defined for jsonQQ"+    , quoteDec  = const $ error "No quoteDec defined for jsonQQ"+    }++aesonExp :: String -> ExpQ+aesonExp txt =+  case eitherDecodeStrict $ TE.encodeUtf8 $ T.pack txt of+    Left err  -> error $ "Error in aesonExp: " ++ show err+    Right val -> lift (val :: Value)
Data/Aeson/Types/ToJSON.hs view
@@ -74,6 +74,7 @@ import Data.Fixed (Fixed, HasResolution) import Data.Foldable (toList) import Data.Functor.Compose (Compose(..))+import Data.Functor.Contravariant (Contravariant (..)) import Data.Functor.Identity (Identity(..)) import Data.Functor.Product (Product(..)) import Data.Functor.Sum (Sum(..))@@ -346,6 +347,13 @@     name .= value = (name, toJSON value)     {-# INLINE (.=) #-} +-- | Constructs a singleton 'H.HashMap'. For calling functions that+--   demand an 'Object' for constructing objects. To be used in+--   conjunction with 'mconcat'. Prefer to use 'object' where possible.+instance KeyValue Object where+    name .= value = H.singleton name (toJSON value)+    {-# INLINE (.=) #-}+ ------------------------------------------------------------------------------- --  Classes and types for map keys -------------------------------------------------------------------------------@@ -497,6 +505,9 @@         . lazyToStrictByteString         . E.encodingToLazyByteString         . e++instance Contravariant ToJSONKeyFunction where+    contramap = contramapToJSONKeyFunction  -- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor. contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
aeson.cabal view
@@ -1,5 +1,5 @@ name:            aeson-version:         1.4.1.0+version:         1.4.2.0 license:         BSD3 license-file:    LICENSE category:        Text, Web, JSON@@ -75,6 +75,7 @@     Data.Aeson.Text     Data.Aeson.Types     Data.Aeson.TH+    Data.Aeson.QQ.Simple      Data.Aeson.Encoding.Internal     Data.Aeson.Internal@@ -99,45 +100,61 @@     Data.Attoparsec.Time     Data.Attoparsec.Time.Internal +  -- GHC bundled libs   build-depends:-    attoparsec >= 0.13.0.1,-    base >= 4.5 && < 5,-    base-compat >= 0.9.1 && < 0.11,-    containers >= 0.4.2,-    deepseq >= 1.3 && < 1.5,-    dlist >= 0.6,-    ghc-prim >= 0.2,-    hashable >= 1.1.2.0,-    primitive >= 0.6.1,-    scientific >= 0.3.4.7 && < 0.4,-    tagged >=0.8.3 && <0.9,-    template-haskell >= 2.7,-    text >= 1.2.3,-    th-abstraction >= 0.2.2 && < 0.3,-    time >= 1.1.1.4,-    time-locale-compat >= 0.1.1 && < 0.2,-    unordered-containers >= 0.2.5.0 && < 0.3,-    uuid-types >= 1.0.3 && <1.1,-    vector >= 0.8+    base             >= 4.5.0.0 && < 5,+    containers       >= 0.4.2.1 && < 0.7,+    deepseq          >= 1.3.0.0 && < 1.5,+    ghc-prim         >= 0.2     && < 0.6,+    template-haskell >= 2.7.0.0 && < 2.15,+    text             >= 1.2.3.0 && < 1.3,+    time             >= 1.4     && < 1.9 +  -- Compat+  build-depends:+    base-compat     >= 0.9.1    && < 0.11+   if flag(bytestring-builder)     build-depends: bytestring >= 0.9.2 && < 0.10.4,                    bytestring-builder >= 0.10.4 && < 1   else     build-depends: bytestring >= 0.10.4 && < 0.11 +  if !impl(ghc >= 8.6)+    build-depends:+      contravariant >=1.4.1    && <1.6+   if !impl(ghc >= 8.0)     -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9     build-depends:-      semigroups >= 0.18.2 && < 0.19,-      transformers >= 0.2.2.0,-      transformers-compat >= 0.3,+      semigroups          >= 0.18.5  && < 0.19,+      transformers        >= 0.3.0.0 && < 0.6,+      transformers-compat >= 0.6.2   && < 0.7,       fail == 4.9.*    if !impl(ghc >= 7.10)     -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8-    build-depends: nats >= 1 && < 1.2,+    build-depends: nats >= 1.1.1 && < 1.2,                    void >= 0.7.2 && < 0.8++  -- cannot use latest version+  build-depends:+    unordered-containers >= 0.2.8.0  && < 0.3,++    -- not in LTS-12.10+    tagged               >= 0.8.5    && < 0.9,+    primitive            >= 0.6.3.0  && < 0.7++  -- Other dependencies+  build-depends:+    attoparsec           >= 0.13.2.2 && < 0.14,+    dlist                >= 0.8.0.4  && < 0.9,+    hashable             >= 1.2.7.0  && < 1.3,+    scientific           >= 0.3.6.2  && < 0.4,+    th-abstraction       >= 0.2.8.0  && < 0.3,+    time-locale-compat   >= 0.1.1.5  && < 0.2,+    uuid-types           >= 1.0.3    && < 1.1,+    vector               >= 0.12.0.1 && < 0.13    ghc-options: -Wall 
benchmarks/Compare.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -6,16 +7,19 @@ import Prelude.Compat  import Compare.BufferBuilder ()-import Compare.JsonBuilder () import Criterion.Main import Data.BufferBuilder.Json-import Data.Json.Builder import Twitter import Twitter.Manual () import Typed.Common import qualified Data.Aeson as Aeson import qualified Compare.JsonBench as JsonBench +#ifdef MIN_VERSION_json_builder+import Data.Json.Builder+import Compare.JsonBuilder ()+#endif+ main :: IO () main =   defaultMain [@@ -23,7 +27,9 @@      bgroup "twitter" [          bench "aeson" $ nf Aeson.encode twtr        , bench "buffer-builder" $ nf encodeJson twtr+#ifdef MIN_VERSION_json_builder        , bench "json-builder" $ nf toJsonLBS twtr+#endif        ]    , JsonBench.benchmarks    ]
benchmarks/Compare/JsonBench.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE NoImplicitPrelude #-}@@ -21,7 +22,10 @@ import Typed.Common (load) import qualified Data.Aeson as Aeson import qualified Data.BufferBuilder.Json as Json++#ifdef MIN_VERSION_json_builder import qualified Data.Json.Builder as JB+#endif  data EyeColor = Green | Blue | Brown     deriving (Eq, Show)@@ -268,6 +272,7 @@             <> "greeting"# Json..=# uGreeting             <> "favoriteFruit"# Json..=# uFavouriteFruit +#ifdef MIN_VERSION_json_builder ---- json-builder instances ----  instance JB.Value EyeColor where@@ -319,6 +324,7 @@             <> t "friends" `JB.row` uFriends             <> t "greeting" `JB.row` uGreeting             <> t "favoriteFruit" `JB.row` uFavouriteFruit+#endif  benchmarks :: Benchmark benchmarks = env (load "json-data/buffer-builder.json") $@@ -326,5 +332,7 @@     bgroup "json-bench" [       bench "aeson" $ nf Aeson.encode parsedUserList     , bench "buffer-builder" $ nf Json.encodeJson parsedUserList+#ifdef MIN_VERSION_json_builder     , bench "json-builder" $ nf JB.toJsonLBS parsedUserList+#endif     ]
+ benchmarks/Issue673.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (+    main,+    input17,+    input32,+    input64,+    input128,+    input256,+    input2048,+    input4096,+    input8192,+    input16384,+  ) where++import Criterion.Main+import Prelude.Compat+import Data.Int (Int64)+import Data.Scientific (Scientific)+import Data.Aeson.Parser (scientific)++import qualified Data.Attoparsec.ByteString.Lazy as AttoL+import qualified Data.Attoparsec.ByteString.Char8 as Atto8+import qualified Data.Aeson as A+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString.Lazy.Char8 as LBS8++decodeInt :: LBS.ByteString -> Maybe Int+decodeInt = A.decode++decodeString :: LBS.ByteString -> Maybe String+decodeString = A.decode++decodeScientific :: LBS.ByteString -> Maybe Scientific+decodeScientific = A.decode++decodeViaRead :: LBS.ByteString -> Integer+decodeViaRead = read . LBS8.unpack++decodeAtto :: LBS.ByteString -> Maybe Scientific+decodeAtto+    = parseOnly (scientific <* AttoL.endOfInput)+  where+    parseOnly p lbs = case AttoL.parse p lbs of+        AttoL.Done _ r -> Just r+        AttoL.Fail {}  -> Nothing++decodeAtto8 :: LBS.ByteString -> Maybe Scientific+decodeAtto8+    = parseOnly (Atto8.scientific <* AttoL.endOfInput)+  where+    parseOnly p lbs = case AttoL.parse p lbs of+        AttoL.Done _ r -> Just r+        AttoL.Fail {}  -> Nothing++generate :: Int64 -> LBS.ByteString+generate n = LBS8.replicate n '1'++input17 :: LBS.ByteString+input17 = generate 17++input32 :: LBS.ByteString+input32 = generate 32++input64 :: LBS.ByteString+input64 = generate 64++input128 :: LBS.ByteString+input128 = generate 128++input256 :: LBS.ByteString+input256 = generate 256++input2048 :: LBS.ByteString+input2048 = generate 2048++input4096 :: LBS.ByteString+input4096 = generate 4096++input8192 :: LBS.ByteString+input8192 = generate 8192++input16384 :: LBS.ByteString+input16384 = generate 16384+++main :: IO ()+main =  defaultMain+    -- works on 64bit+    [ benchPair "17" input17+    -- , benchPair "32" input32+    -- , benchPair "64" input64+    -- , benchPair "128" input128+    -- , benchPair "256" input256+    , benchPair "2048" input2048+    , benchPair "4096" input4096+    , benchPair "8192" input8192+    , benchPair "16384" input16384+    ]+  where+    benchPair name input = bgroup name+        [ bench "Int"        $ whnf decodeInt input+        , bench "Simple"     $ whnf bsToIntegerSimple (LBS.toStrict input)+        , bench "Optim"      $ whnf bsToInteger (LBS.toStrict input)+        , bench "Read"       $ whnf decodeViaRead input+        , bench "Scientific" $ whnf decodeScientific input+        , bench "parserA"    $ whnf decodeAtto  input+        , bench "parserS"    $ whnf decodeAtto8  input+        , bench "String"     $ whnf decodeString $ "\"" <> input <> "\""+        ]++-------------------------------------------------------------------------------+-- better fromInteger+-------------------------------------------------------------------------------++bsToInteger :: BS.ByteString -> Integer+bsToInteger bs+    | l > 40    = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]+    | otherwise = bsToIntegerSimple bs+  where+    l = BS.length bs++bsToIntegerSimple :: BS.ByteString -> Integer+bsToIntegerSimple = BS.foldl' step 0 where+  step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'++-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b+-- digits are combined into a single radix b^2 digit. This process is+-- repeated until we are left with a single digit. This algorithm+-- performs well only on large inputs, so we use the simple algorithm+-- for smaller inputs.+valInteger :: Integer -> Int -> [Integer] -> Integer+valInteger = go+  where+    go :: Integer -> Int -> [Integer] -> Integer+    go _ _ []  = 0+    go _ _ [d] = d+    go b l ds+        | l > 40 = b' `seq` go b' l' (combine b ds')+        | otherwise = valSimple b ds+      where+        -- ensure that we have an even number of digits+        -- before we call combine:+        ds' = if even l then ds else 0 : ds+        b' = b * b+        l' = (l + 1) `quot` 2++    combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)+      where+        d = d1 * b + d2+    combine _ []  = []+    combine _ [_] = errorWithoutStackTrace "this should not happen"++-- The following algorithm is only linear for types whose Num operations+-- are in constant time.+valSimple :: Integer -> [Integer] -> Integer+valSimple base = go 0+  where+    go r [] = r+    go r (d : ds) = r' `seq` go r' ds+      where+        r' = r * base + fromIntegral d
benchmarks/aeson-benchmarks.cabal view
@@ -63,6 +63,10 @@       -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9       build-depends: semigroups >= 0.18.2 && < 0.19 +    if !impl(ghc >= 8.6)+      build-depends:+       contravariant >=1.4.1    && <1.6+     include-dirs: ../include      ghc-options: -O2 -Wall@@ -155,7 +159,6 @@   other-modules:     Compare.BufferBuilder     Compare.JsonBench-    Compare.JsonBuilder     Twitter     Twitter.Manual     Typed.Common@@ -168,9 +171,14 @@     criterion >= 1.0,     deepseq,     ghc-prim,-    json-builder,     text + if impl(ghc < 8.4)+   other-modules:+     Compare.JsonBuilder+   build-depends:+     json-builder+ executable aeson-benchmark-micro   default-language: Haskell2010   main-is: Micro.hs@@ -318,3 +326,16 @@     text,     unordered-containers,     vector++executable aeson-issue-673+  default-language: Haskell2010+  main-is: Issue673.hs+  ghc-options: -Wall -O1 -rtsopts+  build-depends:+    aeson-benchmarks,+    attoparsec,+    base,+    bytestring,+    scientific,+    base-compat,+    criterion >= 1.0
changelog.md view
@@ -1,7 +1,14 @@ For 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). -#### 1.4.1.0+### 1.4.2.0 +* Add `Data.Aeson.QQ.Simple` which is a simpler version of the `aeson-qq` package, it does not support interpolation, thanks to Oleg Grenrus.+* Add `Contravariant ToJSONKeyFunction` instance, thanks to Oleg Grenrus.+* Add `KeyValue Object` instance, thanks to Robert Hensing+* Improved performance when parsing large numbers, thanks to Oleg Grenrus.++### 1.4.1.0+ * Optimizations of generics, thanks to Rémy Oudompheng, here are some numbers for GHC 8.4:   * Compilation time: G/BigProduct.hs is 25% faster, G/BigRecord.hs is 2x faster.   * Runtime performance: BigRecord/toJSON/generic and BigProduct/encode/generic are more than 2x faster.@@ -10,7 +17,7 @@ * Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann * Warning and documentation fixes thanks to tom-bop, Gabor Greif, Ian Jeffries, and Mateusz Curyło. -### 1.4.0.0+## 1.4.0.0  This release introduces bounds on the size of `Scientific` numbers when they are converted to other arbitrary precision types that do not represent them efficiently in memory. @@ -29,7 +36,7 @@  Finally, encoding integral values with large exponents now uses scientific notation, this saves space for large numbers. -### 1.3.1.1+#### 1.3.1.1  * Catch 0 denominators when parsing Ratio 
stack-bench.yaml view
@@ -1,4 +1,4 @@-resolver: nightly-2018-03-12+resolver: lts-12.10 # We use aeson in the snapshot to # - avoid recompilation of criterion # - compare against it@@ -9,5 +9,3 @@ work-dir: .stack-work-bench packages: - benchmarks-extra-deps:-- aeson-1.3.1.0
stack-ffi-unescape.yaml view
@@ -1,4 +1,4 @@-resolver: lts-10.0+resolver: lts-12.10 packages: - '.' flags:@@ -6,4 +6,4 @@     fast: true     cffi: true extra-deps:-- text-1.2.3.0+- hashable-time-0.2.0.1
− stack-lts10.yaml
@@ -1,9 +0,0 @@-resolver: lts-10.0-packages:-- '.'-flags:-  aeson:-    fast: true-    cffi: false-  attoparsec-iso8601:-    fast: true
+ stack-lts12.yaml view
@@ -0,0 +1,10 @@+resolver: lts-12.10+packages:+- '.'+- attoparsec-iso8601+flags:+  aeson:+    fast: true+    cffi: true+extra-deps:+- hashable-time-0.2.0.1
− stack-lts6.yaml
@@ -1,23 +0,0 @@-resolver: lts-6.35-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- semigroups-0.18.2-- tagged-0.8.5-- transformers-compat-0.5.1.4-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- hashable-1.2.6.1-- integer-logarithms-1-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-- text-1.2.3.0-flags:-flags:-  aeson:-    fast: true-  attoparsec-iso8601:-    fast: true-  semigroups:-    bytestring-builder: false
− stack-lts7.yaml
@@ -1,17 +0,0 @@-resolver: lts-7.24-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- hashable-1.2.6.1-- integer-logarithms-1-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-- text-1.2.3.0-flags:-  aeson:-    fast: true-  attoparsec-iso8601:-    fast: true
− stack-lts8.yaml
@@ -1,14 +0,0 @@-resolver: lts-8.24-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- QuickCheck-2.10.0.1-- base-compat-0.9.3-- quickcheck-instances-0.3.16-- th-abstraction-0.2.2.0-flags:-  aeson:-    fast: true-  attoparsec-iso8601:-    fast: true
− stack-lts9.yaml
@@ -1,12 +0,0 @@-resolver: lts-9.9-packages:-- '.'-- attoparsec-iso8601-extra-deps:-- quickcheck-instances-0.3.16-- QuickCheck-2.10.0.1-flags:-  aeson:-    fast: true-  attoparsec-iso8601:-    fast: true
stack-nightly.yaml view
@@ -1,4 +1,4 @@-resolver: nightly-2018-03-07+resolver: nightly-2018-09-26 packages: - '.' - attoparsec-iso8601
tests/UnitTests.hs view
@@ -5,6 +5,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE QuasiQuotes #-}  -- For Data.Aeson.Types.camelTo {-# OPTIONS_GHC -fno-warn-deprecations #-}@@ -25,9 +26,10 @@ import Control.Monad (forM, forM_) import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject, withEmbeddedJSON) import Data.Aeson.Internal (JSONPathElement(..), formatError)+import Data.Aeson.QQ.Simple (aesonQQ) import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1) import Data.Aeson.Text (encodeToTextBuilder)-import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null), camelTo, camelTo2, defaultOptions, omitNothingFields, parse)+import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null, Object), camelTo, camelTo2, defaultOptions, omitNothingFields, parse) import Data.Char (toUpper) import Data.Either.Compat (isLeft, isRight) import Data.Hashable (hash)@@ -93,6 +95,7 @@   , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark   , testGroup "JSONPath" $ fmap (testCase "-") jsonPath   , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws+  , testGroup "Object construction" $ fmap (testCase "-") objectConstruction   , testGroup "Issue #351" $ fmap (testCase "-") issue351   , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors   , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions@@ -107,6 +110,10 @@   , testCase "Big integer decoding" bigIntegerDecoding   , testCase "Big natural decading" bigNaturalDecoding   , testCase "Big integer key decoding" bigIntegerKeyDecoding+  , testGroup "QQ.Simple"+    [ testCase "example" $+      assertEqual "" (object ["foo" .= True]) [aesonQQ| {"foo": true } |]+    ]   ]  roundTripCamel :: String -> Assertion@@ -301,6 +308,18 @@   where   a = object ["223" .= False, "807882556" .= True]   b = object ["807882556" .= True, "223" .= False]++------------------------------------------------------------------------------+-- Check that an alternative way to construct objects works+------------------------------------------------------------------------------++objectConstruction :: [Assertion]+objectConstruction = [+    assertEqual "Equal objects constructed differently" recommended notRecommended+  ]+  where+    recommended = object ["foo" .= True, "bar" .= (-1 :: Int)]+    notRecommended = Object (mconcat ["foo" .= True, "bar" .= (-1 :: Int)])  ------------------------------------------------------------------------------- -- ToJSONKey