on 61b889a help sign in
112 files changed+2809−4969
since splitsnapshots
unifiedsplit
.gitignore−4 .hlint.yaml−11 .travis.yml+21−42 CONTRIBUTING.md+1−39 Data/Aeson.hs+5−77 Data/Aeson/Encode.hs+1−2 Data/Aeson/Encoding.hs+1−2 Data/Aeson/Encoding/Builder.hs+79−6 Data/Aeson/Encoding/Internal.hs+1−2 Data/Aeson/Internal.hs+1−1 Data/Aeson/Internal/Functions.hs+1−1 Data/Aeson/Parser.hs+2−12 Data/Aeson/Parser/Internal.hs+51−219 Data/Aeson/Parser/Time.hs+1−2 Data/Aeson/QQ/Simple.hs−25 Data/Aeson/TH.hs+363−248 Data/Aeson/Text.hs+4−5 Data/Aeson/Types.hs+2−23 Data/Aeson/Types/Class.hs+3−8 Data/Aeson/Types/FromJSON.hs+412−831 Data/Aeson/Types/Generic.hs+8−12 Data/Aeson/Types/Internal.hs+10−121 Data/Aeson/Types/ToJSON.hs+83−248 HLint.hs+10 Makefile+1−1 README.markdown+2−1 aeson.cabal+53−65 attoparsec-iso8601/Data/Attoparsec/Time.hs+5−4 attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+18−21 attoparsec-iso8601/attoparsec-iso8601.cabal+3−3 attoparsec-iso8601/changelog.md−5 benchmarks/AesonCompareAutoInstances.hs+282 benchmarks/AesonEncode.hs+2−2 benchmarks/AesonFoldable.hs+42−14 benchmarks/AesonMap.hs+56−39 benchmarks/AesonParse.hs+3−2 benchmarks/AesonTuples.hs+1−2 benchmarks/Auto/G/BigProduct.hs−33 benchmarks/Auto/G/BigRecord.hs−33 benchmarks/Auto/G/BigSum.hs−28 benchmarks/Auto/G/D.hs−44 benchmarks/Auto/T/BigProduct.hs−27 benchmarks/Auto/T/BigRecord.hs−27 benchmarks/Auto/T/BigSum.hs−22 benchmarks/Auto/T/D.hs−38 benchmarks/AutoCompare.hs−73 benchmarks/Compare.hs+5−10 benchmarks/Compare/BufferBuilder.hs+3−2 benchmarks/Compare/JsonBench.hs+9−18 benchmarks/Compare/JsonBuilder.hs+3−2 benchmarks/CompareWithJSON.hs+33−23 benchmarks/Dates.hs+2−2 benchmarks/Escape.hs−32 benchmarks/Issue673.hs−165 benchmarks/JsonParse.hs+2−3 benchmarks/Micro.hs+6−8 benchmarks/Options.hs+3 benchmarks/ReadFile.hs+1−1 benchmarks/Typed.hs+1−2 benchmarks/Typed/Common.hs+10−1 benchmarks/Typed/Generic.hs+31−15 benchmarks/Typed/Manual.hs+31−15 benchmarks/Typed/TH.hs+31−15 benchmarks/aeson-benchmarks.cabal+70−167 cabal.project−3 changelog.md+1−154 examples/Generic.hs+1−1 examples/Simplest.hs+1−1 examples/TemplateHaskell.hs+1−1 examples/Twitter.hs+2−1 examples/Twitter/Generic.hs+37−2 examples/Twitter/Manual.hs+139−3 examples/Twitter/Options.hs+23 examples/Twitter/TH.hs+18−2 ffi/Data/Aeson/Parser/UnescapeFFI.hs+1−1 pure/Data/Aeson/Parser/UnescapePure.hs+36−13 run-hlint.sh−7 stack-bench.yaml+5−2 stack-ffi-unescape.yaml−9 stack-lts12.yaml−15 stack-lts6.yaml+20 stack-lts7.yaml+13 stack-lts8.yaml+12 stack-nightly.yaml+4−3 stack-pure-unescape.yaml+10 tests/DataFamilies/Encoders.hs+1−1 tests/DataFamilies/Instances.hs+1−2 tests/DataFamilies/Properties.hs+5−6 tests/DataFamilies/Types.hs+1−1 tests/Encoders.hs+7−76 tests/ErrorMessages.hs+41−208 tests/Functions.hs+1−2 tests/Instances.hs+62−7 tests/JSONTestSuite/parsers/test_haskell-aeson/testaeson.cabal+3−5 tests/Options.hs+1−19 tests/PropUtils.hs−208 tests/Properties.hs+511−14 tests/PropertyGeneric.hs−116 tests/PropertyKeys.hs−40 tests/PropertyRTFunctors.hs−51 tests/PropertyRoundTrip.hs−74 tests/PropertyTH.hs−130 tests/SerializationFormatSpec.hs+30−53 tests/Tests.hs+3−5 tests/Types.hs+5−10 tests/UnitTests.hs+39−276 tests/UnitTests/NullaryConstructors.hs+3−8 tests/golden/generic.expected−48 tests/golden/simple.expected−22 travis/cabal_macros.h−448 travis/install.sh−11 travis/script.sh+2−4
.gitignore−4
@@ -5,7 +5,6 @@ dist-newstyle
55cabal.sandbox.config
66.stack-work/
77.stack-work-bench/
8−*.yaml.lock
98
109*.o
1110*.hi
@@ -14,9 +13,6 @@ cabal.sandbox.config
1413.\#*
1514\#*
1615
17−# Emacs backup files
18−*~
19−
2016benchmarks/AesonEncode
2117
2218tests/qc
.hlint.yamldeleted−11
@@ -1,11 +0,0 @@
1−# Ignore some builtin hints
2−- ignore:
3− name: "Reduce duplication"
4− within:
5− - Data.Aeson.Types.ToJSON
6−- ignore:
7− name: "Eta reduce"
8−- ignore:
9− name: "Use newtype instead of data"
10− within:
11− - Data.Aeson.Types.Internal
.travis.yml+21−42
@@ -12,58 +12,37 @@ cache:
1212 - $HOME/.stack
1313 - $HOME/.cabsnap
1414 - $HOME/.cabal/packages
15− - $HOME/.cabal/store
1615
1716before_cache:
1817 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
19− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
20− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
18+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
2419
2520matrix:
2621 include:
27−
28− - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
29− compiler: ": #GHC 8.4.4"
30− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
31−
32− - env: BUILD=stack CABALVER=2.4 GHCVER=8.6.5 STACK_YAML=stack-bench.yaml
33− compiler: ": #GHC 8.6.5"
34− addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
35−
36− - env: BUILD=stack CABALVER=2.2 GHCVER=8.6.5 STACK_YAML=stack-ffi-unescape.yaml
37− compiler: ": #GHC 8.6.5"
38− addons: {apt: {packages: [cabal-install-2.2,ghc-8.6.5], sources: [hvr-ghc]}}
39−
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]}}
43−
44− - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
45− compiler: ": #GHC 8.6.5"
46− addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
47−
48− - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
49− compiler: ": #GHC 8.4.4"
50− addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
51−
52− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
53− compiler: ": #GHC 8.2.2"
54− addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
55−
56− - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
22+ - env: BUILD=hlint CABALVER=1.24 GHCVER=8.0.2
5723 compiler: ": #GHC 8.0.2"
58− addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
59−
60− - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
61− compiler: ": #GHC 7.10.3"
62− addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
63−
24+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
25+ - env: BUILD=stack CABALVER=1.24 GHCVER=8.0.2 STACK_YAML=stack-bench.yaml
26+ compiler: ": #GHC 8.0.2"
27+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
28+ - env: BUILD=stack CABALVER=1.24 GHCVER=8.0.2 STACK_YAML=stack-pure-unescape.yaml
29+ compiler: ": #GHC 8.0.2"
30+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
31+ - env: BUILD=cabal CABALVER=1.24 GHCVER=8.0.2
32+ compiler: ": #GHC 8.0.2"
33+ addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.2], sources: [hvr-ghc]}}
34+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.4.2
35+ compiler: ": #GHC 7.4.2"
36+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.4.2], sources: [hvr-ghc]}}
37+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.6.3
38+ compiler: ": #GHC 7.6.3"
39+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
6440 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
6541 compiler: ": #GHC 7.8.4"
6642 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
43+ - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
44+ compiler: ": #GHC 7.10.3"
45+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
6746
6847before_install:
6948 - unset CC
CONTRIBUTING.md+1−39
@@ -8,7 +8,7 @@ Changes that affect users of aeson should be mentioned in `changelog.md`. It sho
88 * When it should be used
99 * What it supersedes
1010
11−Add this entry under an `## Upcoming` header above existing releases.
11+Add this entry under an `# Upcoming` header above existing releases.
1212
1313The exact format of entries is not important as we'll go through everything before a release is made.
1414
@@ -58,41 +58,3 @@ import Foo as F
5858import Qux (Q(..), q)
5959import qualified A
6060```
61−
62−## Development Workflow (If you plan on using Stack)
63−
64−Here we outline a general workflow new contributors could adopt for a nice incremental development experience.
65−
66−The root folder consists of two stack-*.yaml config files.
67−
68−1. stack-nightly.yaml - Config file defines all the settings to build and test any changes.
69−2. stack-bench.yaml - Used to run benchmark tests.
70−3. stack-ffi-unescape.yaml - This is for replacing certain parts with a C-implementation.
71−
72−Therefore, a ghci development experience would be:
73−
74−1. `stack ghci --stack-yaml stack-nightly.yaml --test`
75−2. :r to recompile
76−3. `main` to run all tests, or `:main --pattern Foo` to run specific tests matched by pattern.
77−
78−Some discussion on this topic can also be found here [here](https://github.com/bos/aeson/issues/733)
79−
80−Of course before submitting a PR, the following steps are recommended:
81−
82−1. 'stack test --stack-yaml stack-nightly.yaml` - Run the entire suite of tests
83−2. `make hlint` - Run hlint on the source folders.
84−3. 'stack test --stack-yaml stack-bench.yaml` - Run the benchmark tests if you believe your changes could affect the benchmarks.
85−
86−## Development Workflow (If you plan on using Cabal)
87−
88−A ghci development experience would be:
89−
90−1. `cabal new-repl test:tests`
91−2. `:r` to recompile
92−3. `Main.main` to run all tests, or `:m Main; :main --pattern Foo` to run specific tests matched by pattern.
93−
94−Of course before submitting a PR, the following steps are recommended:
95−
96−1. `cabal new-test` - Run the entire suite of tests
97−2. `make hlint` - Run hlint on the source folders.
98−3. Uncomment the `benchmarks` line in `cabal.project` - Run the benchmark tests if you believe your changes could affect the benchmarks.
Data/Aeson.hs+5−77
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson
43-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -39,16 +38,11 @@ module Data.Aeson
3938 , eitherDecode
4039 , eitherDecode'
4140 , encode
42− , encodeFile
4341 -- ** Variants for strict bytestrings
4442 , decodeStrict
45− , decodeFileStrict
4643 , decodeStrict'
47− , decodeFileStrict'
4844 , eitherDecodeStrict
49− , eitherDecodeFileStrict
5045 , eitherDecodeStrict'
51− , eitherDecodeFileStrict'
5246 -- * Core JSON types
5347 , Value(..)
5448 , Encoding
@@ -63,18 +57,11 @@ module Data.Aeson
6357 , fromJSON
6458 , ToJSON(..)
6559 , KeyValue(..)
66− , (<?>)
67− , JSONPath
6860 -- ** Keys for maps
6961 , ToJSONKey(..)
7062 , ToJSONKeyFunction(..)
7163 , FromJSONKey(..)
7264 , FromJSONKeyFunction(..)
73− -- *** Generic keys
74− , GToJSONKey()
75− , genericToJSONKey
76− , GFromJSONKey()
77− , genericFromJSONKey
7865 -- ** Liftings to unary and binary type constructors
7966 , FromJSON1(..)
8067 , parseJSON1
@@ -100,34 +87,15 @@ module Data.Aeson
10087 , genericLiftToEncoding
10188 , genericParseJSON
10289 , genericLiftParseJSON
103− -- ** Generic and TH encoding configuration
104− , Options
10590 , defaultOptions
106− -- *** Options fields
107− -- $optionsFields
108− , fieldLabelModifier
109− , constructorTagModifier
110− , allNullaryToStringTag
111− , omitNothingFields
112− , sumEncoding
113− , unwrapUnaryRecords
114− , tagSingleConstructors
115− -- *** Options utilities
116− , SumEncoding(..)
117− , camelTo2
118− , defaultTaggedObject
119− -- ** Options for object keys
120− , JSONKeyOptions
121− , keyModifier
122− , defaultJSONKeyOptions
12391
12492 -- * Inspecting @'Value's@
12593 , withObject
12694 , withText
12795 , withArray
96+ , withNumber
12897 , withScientific
12998 , withBool
130− , withEmbeddedJSON
13199 -- * Constructors and accessors
132100 , Series
133101 , pairs
@@ -140,16 +108,16 @@ module Data.Aeson
140108 -- * Parsing
141109 , json
142110 , json'
143− , parseIndexedJSON
144111 ) where
145112
113+import Prelude ()
146114import Prelude.Compat
147115
148−import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
116+import Data.Aeson.Types.FromJSON (ifromJSON)
149117import Data.Aeson.Encoding (encodingToLazyByteString)
150118import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
151119import Data.Aeson.Types
152−import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
120+import Data.Aeson.Types.Internal (JSONPath, formatError)
153121import qualified Data.ByteString as B
154122import qualified Data.ByteString.Lazy as L
155123
@@ -159,10 +127,6 @@ import qualified Data.ByteString.Lazy as L
159127encode :: (ToJSON a) => a -> L.ByteString
160128encode = encodingToLazyByteString . toEncoding
161129
162−-- | Efficiently serialize a JSON value as a lazy 'L.ByteString' and write it to a file.
163−encodeFile :: (ToJSON a) => FilePath -> a -> IO ()
164−encodeFile fp = L.writeFile fp . encode
165−
166130-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
167131-- If this fails due to incomplete or invalid input, 'Nothing' is
168132-- returned.
@@ -189,18 +153,6 @@ decodeStrict :: (FromJSON a) => B.ByteString -> Maybe a
189153decodeStrict = decodeStrictWith jsonEOF fromJSON
190154{-# INLINE decodeStrict #-}
191155
192−-- | Efficiently deserialize a JSON value from a file.
193−-- If this fails due to incomplete or invalid input, 'Nothing' is
194−-- returned.
195−--
196−-- The input file's content must consist solely of a JSON document,
197−-- with no trailing data except for whitespace.
198−--
199−-- This function parses immediately, but defers conversion. See
200−-- 'json' for details.
201−decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a)
202−decodeFileStrict = fmap decodeStrict . B.readFile
203−
204156-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
205157-- If this fails due to incomplete or invalid input, 'Nothing' is
206158-- returned.
@@ -227,18 +179,6 @@ decodeStrict' :: (FromJSON a) => B.ByteString -> Maybe a
227179decodeStrict' = decodeStrictWith jsonEOF' fromJSON
228180{-# INLINE decodeStrict' #-}
229181
230−-- | Efficiently deserialize a JSON value from a file.
231−-- If this fails due to incomplete or invalid input, 'Nothing' is
232−-- returned.
233−--
234−-- The input file's content must consist solely of a JSON document,
235−-- with no trailing data except for whitespace.
236−--
237−-- This function parses and performs conversion immediately. See
238−-- 'json'' for details.
239−decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a)
240−decodeFileStrict' = fmap decodeStrict' . B.readFile
241−
242182eitherFormatError :: Either (JSONPath, String) a -> Either String a
243183eitherFormatError = either (Left . uncurry formatError) Right
244184{-# INLINE eitherFormatError #-}
@@ -254,12 +194,6 @@ eitherDecodeStrict =
254194 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
255195{-# INLINE eitherDecodeStrict #-}
256196
257−-- | Like 'decodeFileStrict' but returns an error message when decoding fails.
258−eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a)
259−eitherDecodeFileStrict =
260− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON) . B.readFile
261−{-# INLINE eitherDecodeFileStrict #-}
262−
263197-- | Like 'decode'' but returns an error message when decoding fails.
264198eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
265199eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
@@ -271,12 +205,6 @@ eitherDecodeStrict' =
271205 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
272206{-# INLINE eitherDecodeStrict' #-}
273207
274−-- | Like 'decodeFileStrict'' but returns an error message when decoding fails.
275−eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
276−eitherDecodeFileStrict' =
277− fmap (eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON) . B.readFile
278−{-# INLINE eitherDecodeFileStrict' #-}
279−
280208-- $use
281209--
282210-- This section contains basic information on the different ways to
@@ -438,7 +366,7 @@ eitherDecodeFileStrict' =
438366-- The 'Object' type contains JSON objects:
439367--
440368-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
441−-- > Just (fromList [("name",String "Dave"),("age",Number 2)])
369+-- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
442370--
443371-- You can extract values from it with a parser using 'parse',
444372-- 'parseEither' or, in this example, 'parseMaybe':
Data/Aeson/Encode.hs+1−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson.Encode
43-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -9,13 +8,13 @@
98-- Portability: portable
109--
1110-- This module is left to supply limited backwards-compatibility.
12−
1311module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
1412 (
1513 encode
1614 , encodeToTextBuilder
1715 ) where
1816
17+import Prelude ()
1918
2019import Data.ByteString.Lazy (ByteString)
2120import Data.Text.Lazy.Builder (Builder)
Data/Aeson/Encoding.hs+1−2
@@ -1,10 +1,8 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32--
43-- Functions in this module return well-formed 'Encoding''.
54-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
65-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
7−
86module Data.Aeson.Encoding
97 (
108 -- * Encoding
@@ -51,5 +49,6 @@ module Data.Aeson.Encoding
5149 , value
5250 ) where
5351
52+import Prelude ()
5453
5554import Data.Aeson.Encoding.Internal
Data/Aeson/Encoding/Builder.hs+79−6
@@ -1,6 +1,6 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3−{-# LANGUAGE TupleSections #-}
2+{-# LANGUAGE CPP #-}
3+
44-- |
55-- Module: Data.Aeson.Encoding.Builder
66-- Copyright: (c) 2011 MailRank, Inc.
@@ -36,6 +36,7 @@ module Data.Aeson.Encoding.Builder
3636 , ascii5
3737 ) where
3838
39+import Prelude ()
3940import Prelude.Compat
4041
4142import Data.Aeson.Internal.Time
@@ -44,9 +45,8 @@ import Data.ByteString.Builder as B
4445import Data.ByteString.Builder.Prim as BP
4546import Data.ByteString.Builder.Scientific (scientificBuilder)
4647import Data.Char (chr, ord)
47−import Data.Semigroup ((<>))
48+import Data.Monoid ((<>))
4849import Data.Scientific (Scientific, base10Exponent, coefficient)
49−import Data.Text.Encoding (encodeUtf8BuilderEscaped)
5050import Data.Time (UTCTime(..))
5151import Data.Time.Calendar (Day(..), toGregorian)
5252import Data.Time.LocalTime
@@ -55,6 +55,20 @@ import qualified Data.HashMap.Strict as HMS
5555import qualified Data.Text as T
5656import qualified Data.Vector as V
5757
58+#if MIN_VERSION_bytestring(0,10,4)
59+import Data.Text.Encoding (encodeUtf8BuilderEscaped)
60+#else
61+import Data.Bits ((.&.))
62+import Data.Text.Internal (Text(..))
63+import Data.Text.Internal.Unsafe.Shift (shiftR)
64+import Foreign.Ptr (minusPtr, plusPtr)
65+import Foreign.Storable (poke)
66+import qualified Data.ByteString.Builder.Internal as B
67+import qualified Data.ByteString.Builder.Prim.Internal as BP
68+import qualified Data.Text.Array as A
69+import qualified Data.Text.Internal.Encoding.Utf16 as U16
70+#endif
71+
5872-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
5973--
6074-- Use this function if you are encoding over the wire, or need to
@@ -133,7 +147,7 @@ c2w c = fromIntegral (ord c)
133147-- | Encode a JSON number.
134148scientific :: Scientific -> Builder
135149scientific s
136− | e < 0 || e > 1024 = scientificBuilder s
150+ | e < 0 = scientificBuilder s
137151 | otherwise = B.integerDec (coefficient s * 10 ^ e)
138152 where
139153 e = base10Exponent s
@@ -201,7 +215,7 @@ timeOfDay64 (TOD h m s)
201215 !(T mh ml) = twoDigits m
202216 !(T sh sl) = twoDigits (fromIntegral real)
203217 (real,frac) = s `quotRem` pico
204− showFrac = ('.',) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
218+ showFrac = (\x -> ('.', x)) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
205219 trunc12 = (`quotRem` micro) >$<
206220 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
207221 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
@@ -254,3 +268,62 @@ twoDigits a = T (digit hi) (digit lo)
254268
255269digit :: Int -> Char
256270digit x = chr (x + 48)
271+
272+#if !(MIN_VERSION_bytestring(0,10,4))
273+-- | Encode text using UTF-8 encoding and escape the ASCII characters using
274+-- a 'BP.BoundedPrim'.
275+--
276+-- Use this function is to implement efficient encoders for text-based formats
277+-- like JSON or HTML.
278+{-# INLINE encodeUtf8BuilderEscaped #-}
279+-- TODO: Extend documentation with references to source code in @blaze-html@
280+-- or @aeson@ that uses this function.
281+encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
282+encodeUtf8BuilderEscaped be =
283+ -- manual eta-expansion to ensure inlining works as expected
284+ \txt -> B.builder (mkBuildstep txt)
285+ where
286+ bound = max 4 $ BP.sizeBound be
287+
288+ mkBuildstep (Text arr off len) !k =
289+ outerLoop off
290+ where
291+ iend = off + len
292+
293+ outerLoop !i0 br@(B.BufferRange op0 ope)
294+ | i0 >= iend = k br
295+ | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
296+ -- TODO: Use a loop with an integrated bound's check if outRemaining
297+ -- is smaller than 8, as this will save on divisions.
298+ | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
299+ where
300+ outRemaining = (ope `minusPtr` op0) `div` bound
301+ inpRemaining = iend - i0
302+
303+ goPartial !iendTmp = go i0 op0
304+ where
305+ go !i !op
306+ | i < iendTmp = case A.unsafeIndex arr i of
307+ w | w <= 0x7F ->
308+ BP.runB be (fromIntegral w) op >>= go (i + 1)
309+ | w <= 0x7FF -> do
310+ poke8 0 $ (w `shiftR` 6) + 0xC0
311+ poke8 1 $ (w .&. 0x3f) + 0x80
312+ go (i + 1) (op `plusPtr` 2)
313+ | 0xD800 <= w && w <= 0xDBFF -> do
314+ let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
315+ poke8 0 $ (c `shiftR` 18) + 0xF0
316+ poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
317+ poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
318+ poke8 3 $ (c .&. 0x3F) + 0x80
319+ go (i + 2) (op `plusPtr` 4)
320+ | otherwise -> do
321+ poke8 0 $ (w `shiftR` 12) + 0xE0
322+ poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
323+ poke8 2 $ (w .&. 0x3F) + 0x80
324+ go (i + 1) (op `plusPtr` 3)
325+ | otherwise =
326+ outerLoop i (B.BufferRange op ope)
327+ where
328+ poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
329+#endif
Data/Aeson/Encoding/Internal.hs+1−2
@@ -1,9 +1,7 @@
11{-# LANGUAGE DeriveDataTypeable #-}
22{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
43{-# LANGUAGE OverloadedStrings #-}
54{-# LANGUAGE RankNTypes #-}
6−
75module Data.Aeson.Encoding.Internal
86 (
97 -- * Encoding
@@ -57,6 +55,7 @@ module Data.Aeson.Encoding.Internal
5755 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
5856 ) where
5957
58+import Prelude ()
6059import Prelude.Compat
6160
6261import Data.Aeson.Types.Internal (Value)
Data/Aeson/Internal.hs+1−1
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson.Internal
43-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -23,6 +22,7 @@ module Data.Aeson.Internal
2322 , iparse
2423 ) where
2524
25+import Prelude ()
2626
2727import Data.Aeson.Types.Internal
2828import Data.Aeson.Types.FromJSON (ifromJSON)
Data/Aeson/Internal/Functions.hs+1−1
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson.Functions
43-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -15,6 +14,7 @@ module Data.Aeson.Internal.Functions
1514 , mapKey
1615 ) where
1716
17+import Prelude ()
1818import Prelude.Compat
1919
2020import Data.Hashable (Hashable)
Data/Aeson/Parser.hs+2−12
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson.Parser
43-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -35,20 +34,10 @@ module Data.Aeson.Parser
3534 , value
3635 , jstring
3736 , scientific
38− -- ** Handling objects with duplicate keys
39− , jsonWith
40− , jsonLast
41− , jsonAccum
42− , jsonNoDup
4337 -- * Strict parsers
4438 -- $strict
4539 , json'
4640 , value'
47− -- ** Handling objects with duplicate keys
48− , jsonWith'
49− , jsonLast'
50− , jsonAccum'
51− , jsonNoDup'
5241 -- * Decoding without FromJSON instances
5342 , decodeWith
5443 , decodeStrictWith
@@ -56,8 +45,9 @@ module Data.Aeson.Parser
5645 , eitherDecodeStrictWith
5746 ) where
5847
48+import Prelude ()
5949
60−import Data.Aeson.Parser.Internal
50+import Data.Aeson.Parser.Internal (decodeStrictWith, decodeWith, eitherDecodeStrictWith, eitherDecodeWith, json, json', jstring, scientific, value, value')
6151
6252-- $lazy
6353--
Data/Aeson/Parser/Internal.hs+51−219
@@ -1,14 +1,10 @@
11{-# LANGUAGE BangPatterns #-}
22{-# LANGUAGE CPP #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
43{-# LANGUAGE OverloadedStrings #-}
54#if MIN_VERSION_ghc_prim(0,3,1)
65{-# LANGUAGE MagicHash #-}
76#endif
8−#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
9−-- Work around a compiler bug
10−{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
11−#endif
7+
128-- |
139-- Module: Data.Aeson.Parser.Internal
1410-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -25,43 +21,30 @@ module Data.Aeson.Parser.Internal
2521 (
2622 -- * Lazy parsers
2723 json, jsonEOF
28− , jsonWith
29− , jsonLast
30− , jsonAccum
31− , jsonNoDup
3224 , value
3325 , jstring
3426 , jstring_
3527 , scientific
3628 -- * Strict parsers
3729 , json', jsonEOF'
38− , jsonWith'
39− , jsonLast'
40− , jsonAccum'
41− , jsonNoDup'
4230 , value'
4331 -- * Helpers
4432 , decodeWith
4533 , decodeStrictWith
4634 , eitherDecodeWith
4735 , eitherDecodeStrictWith
48− -- ** Handling objects with duplicate keys
49− , fromListAccum
50− , parseListNoDup
5136 ) where
5237
38+import Prelude ()
5339import Prelude.Compat
5440
5541import Control.Applicative ((<|>))
5642import Control.Monad (void, when)
57−import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Value(..))
43+import Data.Aeson.Types.Internal (IResult(..), JSONPath, Result(..), Value(..))
5844import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
59−import Data.Function (fix)
60−import Data.Functor.Compat (($>))
6145import Data.Scientific (Scientific)
6246import Data.Text (Text)
63−import Data.Vector (Vector)
64−import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
47+import Data.Vector as Vector (Vector, empty, fromListN, reverse)
6548import qualified Data.Attoparsec.ByteString as A
6649import qualified Data.Attoparsec.Lazy as L
6750import qualified Data.ByteString as B
@@ -93,7 +76,7 @@ import qualified Data.Text.Encoding as TE
9376#define C_n 110
9477#define C_t 116
9578
96−-- | Parse any JSON value.
79+-- | Parse a top-level JSON value.
9780--
9881-- The conversion of a parsed value to a Haskell value is deferred
9982-- until the Haskell value is needed. This may improve performance if
@@ -103,15 +86,10 @@ import qualified Data.Text.Encoding as TE
10386-- This function is an alias for 'value'. In aeson 0.8 and earlier, it
10487-- parsed only object or array types, in conformance with the
10588-- now-obsolete RFC 4627.
106−--
107−-- ==== Warning
108−--
109−-- If an object contains duplicate keys, only the first one will be kept.
110−-- For a more flexible alternative, see 'jsonWith'.
11189json :: Parser Value
11290json = value
11391
114−-- | Parse any JSON value.
92+-- | Parse a top-level JSON value.
11593--
11694-- This is a strict version of 'json' which avoids building up thunks
11795-- during parsing; it performs all conversions immediately. Prefer
@@ -120,35 +98,23 @@ json = value
12098-- This function is an alias for 'value''. In aeson 0.8 and earlier, it
12199-- parsed only object or array types, in conformance with the
122100-- now-obsolete RFC 4627.
123−--
124−-- ==== Warning
125−--
126−-- If an object contains duplicate keys, only the first one will be kept.
127−-- For a more flexible alternative, see 'jsonWith''.
128101json' :: Parser Value
129102json' = value'
130103
131−-- Open recursion: object_, object_', array_, array_' are parameterized by the
132−-- toplevel Value parser to be called recursively, to keep the parameter
133−-- mkObject outside of the recursive loop for proper inlining.
134−
135−object_ :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
136−object_ mkObject val = {-# SCC "object_" #-} Object <$> objectValues mkObject jstring val
137−{-# INLINE object_ #-}
104+object_ :: Parser Value
105+object_ = {-# SCC "object_" #-} Object <$> objectValues jstring value
138106
139−object_' :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
140−object_' mkObject val' = {-# SCC "object_'" #-} do
141− !vals <- objectValues mkObject jstring' val'
107+object_' :: Parser Value
108+object_' = {-# SCC "object_'" #-} do
109+ !vals <- objectValues jstring' value'
142110 return (Object vals)
143111 where
144112 jstring' = do
145113 !s <- jstring
146114 return s
147−{-# INLINE object_' #-}
148115
149−objectValues :: ([(Text, Value)] -> Either String Object)
150− -> Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
151−objectValues mkObject str val = do
116+objectValues :: Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
117+objectValues str val = do
152118 skipSpace
153119 w <- A.peekWord8'
154120 if w == CLOSE_CURLY
@@ -158,26 +124,22 @@ objectValues mkObject str val = do
158124 -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
159125 -- and it's much faster because it's doing in place update to the 'HashMap'!
160126 loop acc = do
161− k <- (str A.<?> "object key") <* skipSpace <* (char ':' A.<?> "':'")
162− v <- (val A.<?> "object value") <* skipSpace
163− ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_CURLY) A.<?> "',' or '}'"
127+ k <- str <* skipSpace <* char ':'
128+ v <- val <* skipSpace
129+ ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_CURLY
164130 let acc' = (k, v) : acc
165131 if ch == COMMA
166132 then skipSpace >> loop acc'
167− else case mkObject acc' of
168− Left err -> fail err
169− Right obj -> pure obj
133+ else return (H.fromList acc')
170134{-# INLINE objectValues #-}
171135
172−array_ :: Parser Value -> Parser Value
173−array_ val = {-# SCC "array_" #-} Array <$> arrayValues val
174−{-# INLINE array_ #-}
136+array_ :: Parser Value
137+array_ = {-# SCC "array_" #-} Array <$> arrayValues value
175138
176−array_' :: Parser Value -> Parser Value
177−array_' val = {-# SCC "array_'" #-} do
178− !vals <- arrayValues val
139+array_' :: Parser Value
140+array_' = {-# SCC "array_'" #-} do
141+ !vals <- arrayValues value'
179142 return (Array vals)
180−{-# INLINE array_' #-}
181143
182144arrayValues :: Parser Value -> Parser (Vector Value)
183145arrayValues val = do
@@ -188,133 +150,57 @@ arrayValues val = do
188150 else loop [] 1
189151 where
190152 loop acc !len = do
191− v <- (val A.<?> "json list value") <* skipSpace
192− ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_SQUARE) A.<?> "',' or ']'"
153+ v <- val <* skipSpace
154+ ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_SQUARE
193155 if ch == COMMA
194156 then skipSpace >> loop (v:acc) (len+1)
195157 else return (Vector.reverse (Vector.fromListN len (v:acc)))
196158{-# INLINE arrayValues #-}
197159
198−-- | Parse any JSON value. Synonym of 'json'.
199−value :: Parser Value
200−value = jsonWith (pure . H.fromList)
201−
202−-- | Parse any JSON value.
203−--
204−-- This parser is parameterized by a function to construct an 'Object'
205−-- from a raw list of key-value pairs, where duplicates are preserved.
206−-- The pairs appear in __reverse order__ from the source.
160+-- | Parse any JSON value. You should usually 'json' in preference to
161+-- this function, as this function relaxes the object-or-array
162+-- requirement of RFC 4627.
207163--
208−-- ==== __Examples__
209−--
210−-- 'json' keeps only the first occurence of each key, using 'HashMap.Lazy.fromList'.
211−--
212−-- @
213−-- 'json' = 'jsonWith' ('Right' '.' 'H.fromList')
214−-- @
215−--
216−-- 'jsonLast' keeps the last occurence of each key, using
217−-- @'HashMap.Lazy.fromListWith' ('const' 'id')@.
218−--
219−-- @
220−-- 'jsonLast' = 'jsonWith' ('Right' '.' 'HashMap.Lazy.fromListWith' ('const' 'id'))
221−-- @
222−--
223−-- 'jsonAccum' keeps wraps all values in arrays to keep duplicates, using
224−-- 'fromListAccum'.
225−--
226−-- @
227−-- 'jsonAccum' = 'jsonWith' ('Right' . 'fromListAccum')
228−-- @
229−--
230−-- 'jsonNoDup' fails if any object contains duplicate keys, using 'parseListNoDup'.
231−--
232−-- @
233−-- 'jsonNoDup' = 'jsonWith' 'parseListNoDup'
234−-- @
235−jsonWith :: ([(Text, Value)] -> Either String Object) -> Parser Value
236−jsonWith mkObject = fix $ \value_ -> do
164+-- In particular, be careful in using this function if you think your
165+-- code might interoperate with Javascript. A na&#xef;ve Javascript
166+-- library that parses JSON data using @eval@ is vulnerable to attack
167+-- unless the encoded data represents an object or an array. JSON
168+-- implementations in other languages conform to that same restriction
169+-- to preserve interoperability and security.
170+value :: Parser Value
171+value = do
237172 skipSpace
238173 w <- A.peekWord8'
239174 case w of
240175 DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring_)
241− OPEN_CURLY -> A.anyWord8 *> object_ mkObject value_
242− OPEN_SQUARE -> A.anyWord8 *> array_ value_
243− C_f -> string "false" $> Bool False
244− C_t -> string "true" $> Bool True
245− C_n -> string "null" $> Null
176+ OPEN_CURLY -> A.anyWord8 *> object_
177+ OPEN_SQUARE -> A.anyWord8 *> array_
178+ C_f -> string "false" *> pure (Bool False)
179+ C_t -> string "true" *> pure (Bool True)
180+ C_n -> string "null" *> pure Null
246181 _ | w >= 48 && w <= 57 || w == 45
247182 -> Number <$> scientific
248183 | otherwise -> fail "not a valid json value"
249−{-# INLINE jsonWith #-}
250−
251−-- | Variant of 'json' which keeps only the last occurence of every key.
252−jsonLast :: Parser Value
253−jsonLast = jsonWith (Right . H.fromListWith (const id))
254184
255−-- | Variant of 'json' wrapping all object mappings in 'Array' to preserve
256−-- key-value pairs with the same keys.
257−jsonAccum :: Parser Value
258−jsonAccum = jsonWith (Right . fromListAccum)
259−
260−-- | Variant of 'json' which fails if any object contains duplicate keys.
261−jsonNoDup :: Parser Value
262−jsonNoDup = jsonWith parseListNoDup
263−
264−-- | @'fromListAccum' kvs@ is an object mapping keys to arrays containing all
265−-- associated values from the original list @kvs@.
266−--
267−-- >>> fromListAccum [("apple", Bool True), ("apple", Bool False), ("orange", Bool False)]
268−-- fromList [("apple", [Bool False, Bool True]), ("orange", [Bool False])]
269−fromListAccum :: [(Text, Value)] -> Object
270−fromListAccum =
271− fmap (Array . Vector.fromList . ($ [])) . H.fromListWith (.) . (fmap . fmap) (:)
272−
273−-- | @'fromListNoDup' kvs@ fails if @kvs@ contains duplicate keys.
274−parseListNoDup :: [(Text, Value)] -> Either String Object
275−parseListNoDup =
276− H.traverseWithKey unwrap . H.fromListWith (\_ _ -> Nothing) . (fmap . fmap) Just
277− where
278− unwrap k Nothing = Left $ "found duplicate key: " ++ show k
279− unwrap _ (Just v) = Right v
280−
281−-- | Strict version of 'value'. Synonym of 'json''.
185+-- | Strict version of 'value'. See also 'json''.
282186value' :: Parser Value
283−value' = jsonWith' (pure . H.fromList)
284−
285−-- | Strict version of 'jsonWith'.
286−jsonWith' :: ([(Text, Value)] -> Either String Object) -> Parser Value
287−jsonWith' mkObject = fix $ \value_ -> do
187+value' = do
288188 skipSpace
289189 w <- A.peekWord8'
290190 case w of
291191 DOUBLE_QUOTE -> do
292192 !s <- A.anyWord8 *> jstring_
293193 return (String s)
294− OPEN_CURLY -> A.anyWord8 *> object_' mkObject value_
295− OPEN_SQUARE -> A.anyWord8 *> array_' value_
296− C_f -> string "false" $> Bool False
297− C_t -> string "true" $> Bool True
298− C_n -> string "null" $> Null
194+ OPEN_CURLY -> A.anyWord8 *> object_'
195+ OPEN_SQUARE -> A.anyWord8 *> array_'
196+ C_f -> string "false" *> pure (Bool False)
197+ C_t -> string "true" *> pure (Bool True)
198+ C_n -> string "null" *> pure Null
299199 _ | w >= 48 && w <= 57 || w == 45
300200 -> do
301201 !n <- scientific
302202 return (Number n)
303203 | otherwise -> fail "not a valid json value"
304−{-# INLINE jsonWith' #-}
305−
306−-- | Variant of 'json'' which keeps only the last occurence of every key.
307−jsonLast' :: Parser Value
308−jsonLast' = jsonWith' (pure . H.fromListWith (const id))
309−
310−-- | Variant of 'json'' wrapping all object mappings in 'Array' to preserve
311−-- key-value pairs with the same keys.
312−jsonAccum' :: Parser Value
313−jsonAccum' = jsonWith' (pure . fromListAccum)
314−
315−-- | Variant of 'json'' which fails if any object contains duplicate keys.
316−jsonNoDup' :: Parser Value
317−jsonNoDup' = jsonWith' parseListNoDup
318204
319205-- | Parse a quoted JSON string.
320206jstring :: Parser Text
@@ -390,12 +276,7 @@ eitherDecodeWith p to s =
390276 L.Done _ v -> case to v of
391277 ISuccess a -> Right a
392278 IError path msg -> Left (path, msg)
393− L.Fail _ ctx msg -> Left ([], buildMsg ctx msg)
394− where
395− buildMsg :: [String] -> String -> String
396− buildMsg [] msg = msg
397− buildMsg (expectation:_) msg =
398− msg ++ ". Expecting " ++ expectation
279+ L.Fail _ _ msg -> Left ([], msg)
399280{-# INLINE eitherDecodeWith #-}
400281
401282eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
@@ -448,11 +329,12 @@ data SP = SP !Integer {-# UNPACK #-}!Int
448329
449330decimal0 :: Parser Integer
450331decimal0 = do
451− let zero = 48
332+ let step a w = a * 10 + fromIntegral (w - zero)
333+ zero = 48
452334 digits <- A.takeWhile1 isDigit_w8
453335 if B.length digits > 1 && B.unsafeHead digits == zero
454336 then fail "leading zero"
455− else return (bsToInteger digits)
337+ else return (B.foldl' step 0 digits)
456338
457339-- | Parse a JSON number.
458340scientific :: Parser Scientific
@@ -485,53 +367,3 @@ scientific = do
485367 fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
486368 return (Sci.scientific signedCoeff e)
487369{-# INLINE scientific #-}
488−
489−------------------ Copy-pasted and adapted from base ------------------------
490−
491−bsToInteger :: B.ByteString -> Integer
492−bsToInteger bs
493− | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- B.unpack bs ]
494− | otherwise = bsToIntegerSimple bs
495− where
496− l = B.length bs
497−
498−bsToIntegerSimple :: B.ByteString -> Integer
499−bsToIntegerSimple = B.foldl' step 0 where
500− step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
501−
502−-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
503−-- digits are combined into a single radix b^2 digit. This process is
504−-- repeated until we are left with a single digit. This algorithm
505−-- performs well only on large inputs, so we use the simple algorithm
506−-- for smaller inputs.
507−valInteger :: Integer -> Int -> [Integer] -> Integer
508−valInteger = go
509− where
510− go :: Integer -> Int -> [Integer] -> Integer
511− go _ _ [] = 0
512− go _ _ [d] = d
513− go b l ds
514− | l > 40 = b' `seq` go b' l' (combine b ds')
515− | otherwise = valSimple b ds
516− where
517− -- ensure that we have an even number of digits
518− -- before we call combine:
519− ds' = if even l then ds else 0 : ds
520− b' = b * b
521− l' = (l + 1) `quot` 2
522−
523− combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
524− where
525− d = d1 * b + d2
526− combine _ [] = []
527− combine _ [_] = errorWithoutStackTrace "this should not happen"
528−
529−-- The following algorithm is only linear for types whose Num operations
530−-- are in constant time.
531−valSimple :: Integer -> [Integer] -> Integer
532−valSimple base = go 0
533− where
534− go r [] = r
535− go r (d : ds) = r' `seq` go r' ds
536− where
537− r' = r * base + fromIntegral d
Data/Aeson/Parser/Time.hs+1−2
@@ -1,5 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Data.Aeson.Parser.Time
42 (
53 run
@@ -11,6 +9,7 @@ module Data.Aeson.Parser.Time
119 , zonedTime
1210 ) where
1311
12+import Prelude ()
1413import Prelude.Compat
1514
1615import Data.Attoparsec.Text (Parser)
Data/Aeson/QQ/Simple.hsdeleted−25
@@ -1,25 +0,0 @@
1−-- | Like "Data.Aeson.QQ" but without interpolation.
2−module Data.Aeson.QQ.Simple (aesonQQ) where
3−
4−import Data.Aeson
5−import qualified Data.Text as T
6−import qualified Data.Text.Encoding as TE
7−import Language.Haskell.TH
8−import Language.Haskell.TH.Quote
9−import Language.Haskell.TH.Syntax (Lift (..))
10−import Prelude ()
11−import Prelude.Compat
12−
13−aesonQQ :: QuasiQuoter
14−aesonQQ = QuasiQuoter
15− { quoteExp = aesonExp
16− , quotePat = const $ error "No quotePat defined for jsonQQ"
17− , quoteType = const $ error "No quoteType defined for jsonQQ"
18− , quoteDec = const $ error "No quoteDec defined for jsonQQ"
19− }
20−
21−aesonExp :: String -> ExpQ
22−aesonExp txt =
23− case eitherDecodeStrict $ TE.encodeUtf8 $ T.pack txt of
24− Left err -> error $ "Error in aesonExp: " ++ show err
25− Right val -> lift (val :: Value)
Data/Aeson/TH.hs+363−248
@@ -85,6 +85,7 @@ $('deriveJSON' 'defaultOptions' ''(,,,))
8585@
8686
8787-}
88+
8889module Data.Aeson.TH
8990 (
9091 -- * Encoding configuration
@@ -116,42 +117,49 @@ module Data.Aeson.TH
116117 , mkLiftParseJSON2
117118 ) where
118119
119−import Prelude.Compat
120+import Prelude ()
121+import Prelude.Compat hiding (exp)
120122
121123import Control.Applicative ((<|>))
122−import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
124+import Data.Aeson (Object, (.=), (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
123125import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
124−import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
126+import Data.Aeson.Types.Internal ((<?>), Pair, JSONPathElement(Key))
125127import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
126−import Data.Aeson.Types.ToJSON (fromPairs, pair)
127128import Control.Monad (liftM2, unless, when)
128129import Data.Foldable (foldr')
129130#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
130131import Data.List (nub)
131132#endif
132−import Data.List (foldl', genericLength, intercalate, partition, union)
133+import Data.List (foldl', genericLength , intercalate , intersperse, partition, union)
133134import Data.List.NonEmpty ((<|), NonEmpty((:|)))
134135import Data.Map (Map)
135136import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136−import qualified Data.Monoid as Monoid
137137import Data.Set (Set)
138+#if MIN_VERSION_template_haskell(2,8,0)
138139import Language.Haskell.TH hiding (Arity)
140+#else
141+import Language.Haskell.TH
142+#endif
139143import Language.Haskell.TH.Datatype
144+#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
145+import Language.Haskell.TH.Lib (starK)
146+#endif
140147#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
141148import Language.Haskell.TH.Syntax (mkNameG_tc)
142149#endif
143150import Text.Printf (printf)
151+import qualified Data.Aeson as A
144152import qualified Data.Aeson.Encoding.Internal as E
145153import qualified Data.Foldable as F (all)
146−import qualified Data.HashMap.Strict as H (difference, fromList, keys, lookup, toList)
154+import qualified Data.HashMap.Strict as H (lookup, toList)
147155import qualified Data.List.NonEmpty as NE (length, reverse)
148156import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
149−import qualified Data.Semigroup as Semigroup (Option(..))
150157import qualified Data.Set as Set (empty, insert, member)
151158import qualified Data.Text as T (Text, pack, unpack)
152−import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
159+import qualified Data.Vector as V (unsafeIndex, null, length, create, fromList)
153160import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
154161
162+
155163--------------------------------------------------------------------------------
156164-- Convenience
157165--------------------------------------------------------------------------------
@@ -245,8 +253,8 @@ deriveToJSONCommon :: JSONClass
245253 -> Name
246254 -- ^ Name of the type for which to generate an instance.
247255 -> Q [Dec]
248−deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue Value jc)
249− , (ToEncoding, \jc _ -> consToValue Encoding jc)
256+deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue jc)
257+ , (ToEncoding, \jc _ -> consToEncoding jc)
250258 ]
251259
252260-- | Generates a lambda expression which encodes the given data type or
@@ -276,7 +284,7 @@ mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
276284 -> Options -- ^ Encoding options.
277285 -> Name -- ^ Name of the encoded type.
278286 -> Q Exp
279−mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
287+mkToJSONCommon = mkFunCommon (\jc _ -> consToValue jc)
280288
281289-- | Generates a lambda expression which encodes the given data type or
282290-- data family instance constructor as a JSON string.
@@ -305,14 +313,12 @@ mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
305313 -> Options -- ^ Encoding options.
306314 -> Name -- ^ Name of the encoded type.
307315 -> Q Exp
308−mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
316+mkToEncodingCommon = mkFunCommon (\jc _ -> consToEncoding jc)
309317
310318-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
311−-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
312−-- constructors must be from the same type.
313−consToValue :: ToJSONFun
314− -- ^ The method ('toJSON' or 'toEncoding') being derived.
315− -> JSONClass
319+-- code to generate a 'Value' of a number of constructors. All constructors
320+-- must be from the same type.
321+consToValue :: JSONClass
316322 -- ^ The ToJSON variant being derived.
317323 -> Options
318324 -- ^ Encoding options.
@@ -322,16 +328,16 @@ consToValue :: ToJSONFun
322328 -- ^ Constructors for which to generate JSON generating code.
323329 -> Q Exp
324330
325−consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
331+consToValue _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
326332 ++ "Not a single constructor given!"
327333
328−consToValue target jc opts instTys cons = do
334+consToValue jc opts vars cons = do
329335 value <- newName "value"
330336 tjs <- newNameList "_tj" $ arityInt jc
331337 tjls <- newNameList "_tjl" $ arityInt jc
332338 let zippedTJs = zip tjs tjls
333339 interleavedTJs = interleave tjs tjls
334− lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
340+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
335341 tvMap = M.fromList $ zip lastTyVars zippedTJs
336342 lamE (map varP $ interleavedTJs ++ [value]) $
337343 caseE (varE value) (matches tvMap)
@@ -339,26 +345,66 @@ consToValue target jc opts instTys cons = do
339345 matches tvMap = case cons of
340346 -- A single constructor is directly encoded. The constructor itself may be
341347 -- forgotten.
342− [con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
348+ [con] | not (tagSingleConstructors opts) -> [argsToValue jc tvMap opts False con]
343349 _ | allNullaryToStringTag opts && all isNullary cons ->
344− [ match (conP conName []) (normalB $ conStr target opts conName) []
350+ [ match (conP conName []) (normalB $ conStr opts conName) []
345351 | con <- cons
346352 , let conName = constructorName con
347353 ]
348− | otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
354+ | otherwise -> [argsToValue jc tvMap opts True con | con <- cons]
349355
350−-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
351−conStr :: ToJSONFun -> Options -> Name -> Q Exp
352−conStr Value opts = appE [|String|] . conTxt opts
353−conStr Encoding opts = appE [|E.text|] . conTxt opts
356+conStr :: Options -> Name -> Q Exp
357+conStr opts = appE [|String|] . conTxt opts
354358
355−-- | Name of the constructor as a quoted 'Text'.
356359conTxt :: Options -> Name -> Q Exp
357−conTxt opts = appE [|T.pack|] . stringE . conString opts
360+conTxt opts = appE [|T.pack|] . conStringE opts
361+
362+conStringE :: Options -> Name -> Q Exp
363+conStringE opts = stringE . constructorTagModifier opts . nameBase
364+
365+-- | Helper function used by both 'deriveToJSON' and 'mkToEncoding'. Generates
366+-- code to write out a value for a number of constructors. All constructors
367+-- must be from the same type.
368+consToEncoding :: JSONClass
369+ -- ^ The ToJSON variant being derived.
370+ -> Options
371+ -- ^ Encoding options.
372+ -> [Type]
373+ -- ^ The types from the data type/data family instance declaration
374+ -> [ConstructorInfo]
375+ -- ^ Constructors for which to generate JSON generating code.
376+ -> Q Exp
377+
378+consToEncoding _ _ _ [] = error $ "Data.Aeson.TH.consToEncoding: "
379+ ++ "Not a single constructor given!"
358380
359−-- | Name of the constructor.
360−conString :: Options -> Name -> String
361−conString opts = constructorTagModifier opts . nameBase
381+consToEncoding jc opts vars cons = do
382+ value <- newName "value"
383+ tes <- newNameList "_te" $ arityInt jc
384+ tels <- newNameList "_tel" $ arityInt jc
385+ let zippedTEs = zip tes tels
386+ interleavedTEs = interleave tes tels
387+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
388+ tvMap = M.fromList $ zip lastTyVars zippedTEs
389+ lamE (map varP $ interleavedTEs ++ [value]) $
390+ caseE (varE value) (matches tvMap)
391+ where
392+ matches tvMap = case cons of
393+ -- A single constructor is directly encoded. The constructor itself may be
394+ -- forgotten.
395+ [con] | not (tagSingleConstructors opts) -> [argsToEncoding jc tvMap opts False con]
396+ -- Encode just the name of the constructor of a sum type iff all the
397+ -- constructors are nullary.
398+ _ | allNullaryToStringTag opts && all isNullary cons ->
399+ [ match (conP conName [])
400+ (normalB $ encStr opts conName) []
401+ | con <- cons
402+ , let conName = constructorName con
403+ ]
404+ | otherwise -> [argsToEncoding jc tvMap opts True con | con <- cons]
405+
406+encStr :: Options -> Name -> Q Exp
407+encStr opts = appE [|E.text|] . conTxt opts
362408
363409-- | If constructor is nullary.
364410isNullary :: ConstructorInfo -> Bool
@@ -366,133 +412,127 @@ isNullary ConstructorInfo { constructorVariant = NormalConstructor
366412 , constructorFields = tys } = null tys
367413isNullary _ = False
368414
369−-- | Wrap fields of a non-record constructor. See 'sumToValue'.
370−opaqueSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
371−opaqueSumToValue target opts multiCons nullary conName value =
372− sumToValue target opts multiCons nullary conName
373− value
374− pairs
375− where
376− pairs contentsFieldName = pairE contentsFieldName value
377−
378−-- | Wrap fields of a record constructor. See 'sumToValue'.
379−recordSumToValue :: ToJSONFun -> Options -> Bool -> Bool -> Name -> ExpQ -> ExpQ
380−recordSumToValue target opts multiCons nullary conName pairs =
381− sumToValue target opts multiCons nullary conName
382− (fromPairsE pairs)
383− (const pairs)
384−
385−-- | Wrap fields of a constructor.
386−sumToValue
387− :: ToJSONFun
388− -- ^ The method being derived.
389− -> Options
390− -- ^ Deriving options.
391− -> Bool
392− -- ^ Does this type have multiple constructors.
393− -> Bool
394− -- ^ Is this constructor nullary.
395− -> Name
396− -- ^ Constructor name.
397− -> ExpQ
398− -- ^ Fields of the constructor as a 'Value' or 'Encoding'.
399− -> (String -> ExpQ)
400− -- ^ Representation of an 'Object' fragment used for the 'TaggedObject'
401− -- variant; of type @[(Text,Value)]@ or @[Encoding]@, depending on the method
402− -- being derived.
403− --
404− -- - For non-records, produces a pair @"contentsFieldName":value@,
405− -- given a @contentsFieldName@ as an argument. See 'opaqueSumToValue'.
406− -- - For records, produces the list of pairs corresponding to fields of the
407− -- encoded value (ignores the argument). See 'recordSumToValue'.
408− -> ExpQ
409−sumToValue target opts multiCons nullary conName value pairs
415+sumToValue :: Options -> Bool -> Bool -> Name -> Q Exp -> Q Exp
416+sumToValue opts multiCons nullary conName exp
410417 | multiCons =
411418 case sumEncoding opts of
412419 TwoElemArray ->
413− array target [conStr target opts conName, value]
420+ [|Array|] `appE` ([|V.fromList|] `appE` listE [conStr opts conName, exp])
414421 TaggedObject{tagFieldName, contentsFieldName} ->
415− -- TODO: Maybe throw an error in case
416− -- tagFieldName overwrites a field in pairs.
417− let tag = pairE tagFieldName (conStr target opts conName)
418− content = pairs contentsFieldName
419− in fromPairsE $
420− if nullary then tag else infixApp tag [|(Monoid.<>)|] content
422+ let tag = infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
423+ contents = infixApp [|T.pack contentsFieldName|] [|(.=)|] exp
424+ in
425+ [|A.object|] `appE` listE (if nullary then [tag] else [tag, contents])
421426 ObjectWithSingleField ->
422− objectE [(conString opts conName, value)]
423− UntaggedValue | nullary -> conStr target opts conName
424− UntaggedValue -> value
425− | otherwise = value
427+ [|A.object|] `appE` listE
428+ [ infixApp (conTxt opts conName) [|(.=)|] exp
429+ ]
430+ UntaggedValue | nullary -> conStr opts conName
431+ UntaggedValue -> exp
432+ | otherwise = exp
426433
427434-- | Generates code to generate the JSON encoding of a single constructor.
428−argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
435+argsToValue :: JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
429436
430437-- Polyadic constructors with special case for unary constructors.
431−argsToValue target jc tvMap opts multiCons
438+argsToValue jc tvMap opts multiCons
432439 ConstructorInfo { constructorName = conName
433440 , constructorVariant = NormalConstructor
434441 , constructorFields = argTys } = do
435442 argTys' <- mapM resolveTypeSynonyms argTys
436443 let len = length argTys'
437444 args <- newNameList "arg" len
438− let js = case [ dispatchToJSON target jc conName tvMap argTy
439− `appE` varE arg
440− | (arg, argTy) <- zip args argTys'
441− ] of
442− -- Single argument is directly converted.
443− [e] -> e
444− -- Zero and multiple arguments are converted to a JSON array.
445− es -> array target es
446−
445+ js <- case [ dispatchToJSON jc conName tvMap argTy
446+ `appE` varE arg
447+ | (arg, argTy) <- zip args argTys'
448+ ] of
449+ -- Single argument is directly converted.
450+ [e] -> return e
451+ -- Multiple arguments are converted to a JSON array.
452+ es -> do
453+ mv <- newName "mv"
454+ let newMV = bindS (varP mv)
455+ ([|VM.unsafeNew|] `appE`
456+ litE (integerL $ fromIntegral len))
457+ stmts = [ noBindS $
458+ [|VM.unsafeWrite|] `appE`
459+ varE mv `appE`
460+ litE (integerL ix) `appE`
461+ e
462+ | (ix, e) <- zip [(0::Integer)..] es
463+ ]
464+ ret = noBindS $ [|return|] `appE` varE mv
465+ return $ [|Array|] `appE`
466+ (varE 'V.create `appE`
467+ doE (newMV:stmts++[ret]))
447468 match (conP conName $ map varP args)
448− (normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
469+ (normalB $ sumToValue opts multiCons (null argTys') conName js)
449470 []
450471
451472-- Records.
452−argsToValue target jc tvMap opts multiCons
473+argsToValue jc tvMap opts multiCons
453474 info@ConstructorInfo { constructorName = conName
454475 , constructorVariant = RecordConstructor fields
455476 , constructorFields = argTys } =
456477 case (unwrapUnaryRecords opts, not multiCons, argTys) of
457− (True,True,[_]) -> argsToValue target jc tvMap opts multiCons
478+ (True,True,[_]) -> argsToValue jc tvMap opts multiCons
458479 (info{constructorVariant = NormalConstructor})
459480 _ -> do
460481 argTys' <- mapM resolveTypeSynonyms argTys
461482 args <- newNameList "arg" $ length argTys'
462− let pairs | omitNothingFields opts = infixApp maybeFields
463− [|(Monoid.<>)|]
483+ let exp = [|A.object|] `appE` pairs
484+
485+ pairs | omitNothingFields opts = infixApp maybeFields
486+ [|(++)|]
464487 restFields
465− | otherwise = mconcatE (map pureToPair argCons)
488+ | otherwise = listE $ map toPair argCons
466489
467− argCons = zip3 (map varE args) argTys' fields
490+ argCons = zip3 args argTys' fields
468491
469− maybeFields = mconcatE (map maybeToPair maybes)
492+ maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
470493
471− restFields = mconcatE (map pureToPair rest)
494+ restFields = listE $ map toPair rest
472495
473− (maybes0, rest0) = partition isMaybe argCons
474− (options, rest) = partition isOption rest0
475− maybes = maybes0 ++ map optionToMaybe options
496+ (maybes, rest) = partition isMaybe argCons
476497
477− maybeToPair = toPairLifted True
478− pureToPair = toPairLifted False
498+ maybeToPair (arg, argTy, field) =
499+ infixApp ([|keyValuePairWith|]
500+ `appE` dispatchToJSON jc conName tvMap argTy
501+ `appE` toFieldName field)
502+ [|(<$>)|]
503+ (varE arg)
479504
480− toPairLifted lifted (arg, argTy, field) =
481− let toValue = dispatchToJSON target jc conName tvMap argTy
482− fieldName = fieldLabel opts field
483− e arg' = pairE fieldName (toValue `appE` arg')
484− in if lifted
485− then do
486− x <- newName "x"
487− [|maybe mempty|] `appE` lam1E (varP x) (e (varE x)) `appE` arg
488− else e arg
505+ toPair (arg, argTy, field) =
506+ [|keyValuePairWith|]
507+ `appE` dispatchToJSON jc conName tvMap argTy
508+ `appE` toFieldName field
509+ `appE` varE arg
510+
511+ toFieldName field = [|T.pack|] `appE` fieldLabelExp opts field
489512
490513 match (conP conName $ map varP args)
491− (normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
492− []
514+ ( normalB
515+ $ if multiCons
516+ then case sumEncoding opts of
517+ TwoElemArray -> [|toJSON|] `appE` tupE [conStr opts conName, exp]
518+ TaggedObject{tagFieldName} ->
519+ [|A.object|] `appE`
520+ -- TODO: Maybe throw an error in case
521+ -- tagFieldName overwrites a field in pairs.
522+ infixApp (infixApp [|T.pack tagFieldName|]
523+ [|(.=)|]
524+ (conStr opts conName))
525+ [|(:)|]
526+ pairs
527+ ObjectWithSingleField ->
528+ [|A.object|] `appE` listE
529+ [ infixApp (conTxt opts conName) [|(.=)|] exp ]
530+ UntaggedValue -> exp
531+ else exp
532+ ) []
493533
494534-- Infix constructors.
495−argsToValue target jc tvMap opts multiCons
535+argsToValue jc tvMap opts multiCons
496536 ConstructorInfo { constructorName = conName
497537 , constructorVariant = InfixConstructor
498538 , constructorFields = argTys } = do
@@ -501,12 +541,11 @@ argsToValue target jc tvMap opts multiCons
501541 ar <- newName "argR"
502542 match (infixP (varP al) conName (varP ar))
503543 ( normalB
504− $ opaqueSumToValue target opts multiCons False conName
505− $ array target
506− [ dispatchToJSON target jc conName tvMap aTy
507− `appE` varE a
508− | (a, aTy) <- [(al,alTy), (ar,arTy)]
509− ]
544+ $ sumToValue opts multiCons False conName
545+ $ [|toJSON|] `appE` listE [ dispatchToJSON jc conName tvMap aTy
546+ `appE` varE a
547+ | (a, aTy) <- [(al,alTy), (ar,arTy)]
548+ ]
510549 )
511550 []
512551
@@ -514,63 +553,151 @@ isMaybe :: (a, Type, b) -> Bool
514553isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
515554isMaybe _ = False
516555
517−isOption :: (a, Type, b) -> Bool
518−isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
519−isOption _ = False
520−
521−optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
522−optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
523−
524556(<^>) :: ExpQ -> ExpQ -> ExpQ
525557(<^>) a b = infixApp a [|(E.><)|] b
526558infixr 6 <^>
527559
560+(<:>) :: ExpQ -> ExpQ -> ExpQ
561+(<:>) a b = a <^> [|E.colon|] <^> b
562+infixr 5 <:>
563+
528564(<%>) :: ExpQ -> ExpQ -> ExpQ
529565(<%>) a b = a <^> [|E.comma|] <^> b
530566infixr 4 <%>
531567
532−-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
533−array :: ToJSONFun -> [ExpQ] -> ExpQ
534−array Encoding [] = [|E.emptyArray_|]
535−array Value [] = [|Array V.empty|]
536−array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
537−array Value es = do
538− mv <- newName "mv"
539− let newMV = bindS (varP mv)
540− ([|VM.unsafeNew|] `appE`
541− litE (integerL $ fromIntegral (length es)))
542− stmts = [ noBindS $
543− [|VM.unsafeWrite|] `appE`
544− varE mv `appE`
545− litE (integerL ix) `appE`
546− e
547− | (ix, e) <- zip [(0::Integer)..] es
548− ]
549− ret = noBindS $ [|return|] `appE` varE mv
550− [|Array|] `appE`
551− (varE 'V.create `appE`
552− doE (newMV:stmts++[ret]))
568+array :: ExpQ -> ExpQ
569+array exp = [|E.wrapArray|] `appE` exp
553570
554−-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
555−objectE :: [(String, ExpQ)] -> ExpQ
556−objectE = fromPairsE . mconcatE . fmap (uncurry pairE)
571+object :: ExpQ -> ExpQ
572+object exp = [|E.wrapObject|] `appE` exp
557573
558−-- | 'mconcat' a list of fixed length.
559−--
560−-- > mconcatE [ [|x|], [|y|], [|z|] ] = [| x <> (y <> z) |]
561−mconcatE :: [ExpQ] -> ExpQ
562−mconcatE [] = [|Monoid.mempty|]
563−mconcatE [x] = x
564−mconcatE (x : xs) = infixApp x [|(Monoid.<>)|] (mconcatE xs)
574+sumToEncoding :: Options -> Bool -> Bool -> Name -> Q Exp -> Q Exp
575+sumToEncoding opts multiCons nullary conName exp
576+ | multiCons =
577+ let fexp = exp in
578+ case sumEncoding opts of
579+ TwoElemArray ->
580+ array (encStr opts conName <%> fexp)
581+ TaggedObject{tagFieldName, contentsFieldName} ->
582+ let tag = [|E.text (T.pack tagFieldName)|] <:> encStr opts conName
583+ contents = [|E.text (T.pack contentsFieldName)|] <:> fexp
584+ in
585+ object $
586+ if nullary then tag else tag <%> contents
587+ ObjectWithSingleField ->
588+ object (encStr opts conName <:> fexp)
589+ UntaggedValue | nullary -> encStr opts conName
590+ UntaggedValue -> exp
591+ | otherwise = exp
565592
566−fromPairsE :: ExpQ -> ExpQ
567−fromPairsE = ([|fromPairs|] `appE`)
593+-- | Generates code to generate the JSON encoding of a single constructor.
594+argsToEncoding :: JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
568595
569−-- | Create (an encoding of) a key-value pair.
570−--
571−-- > pairE "k" [|v|] = [|pair "k" v|]
572−pairE :: String -> ExpQ -> ExpQ
573−pairE k v = [|pair k|] `appE` v
596+-- Polyadic constructors with special case for unary constructors.
597+argsToEncoding jc tvMap opts multiCons
598+ ConstructorInfo { constructorName = conName
599+ , constructorVariant = NormalConstructor
600+ , constructorFields = argTys } = do
601+ argTys' <- mapM resolveTypeSynonyms argTys
602+ args <- newNameList "arg" $ length argTys'
603+ js <- case zip args argTys' of
604+ -- Nullary constructors are converted to an empty array.
605+ [] -> return [| E.emptyArray_ |]
606+
607+ -- Single argument is directly converted.
608+ [(e,eTy)] -> return (dispatchToEncoding jc conName tvMap eTy
609+ `appE` varE e)
610+ -- Multiple arguments are converted to a JSON array.
611+ es ->
612+ return (array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap xTy
613+ `appE` varE x
614+ | (x,xTy) <- es
615+ ]))
616+ match (conP conName $ map varP args)
617+ (normalB $ sumToEncoding opts multiCons (null argTys') conName js)
618+ []
619+
620+-- Records.
621+argsToEncoding jc tvMap opts multiCons
622+ info@ConstructorInfo { constructorName = conName
623+ , constructorVariant = RecordConstructor fields
624+ , constructorFields = argTys } =
625+ case (unwrapUnaryRecords opts, not multiCons, argTys) of
626+ (True,True,[_]) -> argsToEncoding jc tvMap opts multiCons
627+ (info{constructorVariant = NormalConstructor})
628+ _ -> do
629+ argTys' <- mapM resolveTypeSynonyms argTys
630+ args <- newNameList "arg" $ length argTys'
631+ let exp = object objBody
632+
633+ objBody = [|E.econcat|] `appE`
634+ ([|intersperse E.comma|] `appE` pairs)
635+ pairs | omitNothingFields opts = infixApp maybeFields
636+ [|(++)|]
637+ restFields
638+ | otherwise = listE (map toPair argCons)
639+
640+ argCons = zip3 args argTys' fields
641+
642+ maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
643+
644+ restFields = listE (map toPair rest)
645+
646+ (maybes, rest) = partition isMaybe argCons
647+
648+ maybeToPair (arg, argTy, field) =
649+ infixApp
650+ (infixApp
651+ (infixE
652+ (Just $ toFieldName field <^> [|E.colon|])
653+ [|(E.><)|]
654+ Nothing)
655+ [|(.)|]
656+ (dispatchToEncoding jc conName tvMap argTy))
657+ [|(<$>)|]
658+ (varE arg)
659+
660+ toPair (arg, argTy, field) =
661+ toFieldName field
662+ <:> dispatchToEncoding jc conName tvMap argTy
663+ `appE` varE arg
664+
665+ toFieldName field = [|E.text|] `appE`
666+ ([|T.pack|] `appE` fieldLabelExp opts field)
667+
668+ match (conP conName $ map varP args)
669+ ( normalB
670+ $ if multiCons
671+ then case sumEncoding opts of
672+ TwoElemArray -> array $
673+ encStr opts conName <%> exp
674+ TaggedObject{tagFieldName} -> object $
675+ ([|E.text (T.pack tagFieldName)|] <:>
676+ encStr opts conName) <%>
677+ objBody
678+ ObjectWithSingleField -> object $
679+ encStr opts conName <:> exp
680+ UntaggedValue -> exp
681+ else exp
682+ ) []
683+
684+-- Infix constructors.
685+argsToEncoding jc tvMap opts multiCons
686+ ConstructorInfo { constructorName = conName
687+ , constructorVariant = InfixConstructor
688+ , constructorFields = argTys } = do
689+ al <- newName "argL"
690+ ar <- newName "argR"
691+ [alTy,arTy] <- mapM resolveTypeSynonyms argTys
692+ match (infixP (varP al) conName (varP ar))
693+ ( normalB
694+ $ sumToEncoding opts multiCons False conName
695+ $ array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap aTy
696+ `appE` varE a
697+ | (a,aTy) <- [(al,alTy), (ar,arTy)]
698+ ])
699+ )
700+ []
574701
575702--------------------------------------------------------------------------------
576703-- FromJSON
@@ -663,13 +790,13 @@ consFromJSON :: JSONClass
663790consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
664791 ++ "Not a single constructor given!"
665792
666−consFromJSON jc tName opts instTys cons = do
793+consFromJSON jc tName opts vars cons = do
667794 value <- newName "value"
668795 pjs <- newNameList "_pj" $ arityInt jc
669796 pjls <- newNameList "_pjl" $ arityInt jc
670797 let zippedPJs = zip pjs pjls
671798 interleavedPJs = interleave pjs pjls
672− lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
799+ lastTyVars = map varTToName $ drop (length vars - arityInt jc) vars
673800 tvMap = M.fromList $ zip lastTyVars zippedPJs
674801 lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
675802
@@ -697,7 +824,8 @@ consFromJSON jc tName opts instTys cons = do
697824 [ liftM2 (,) (normalG $
698825 infixApp (varE txt)
699826 [|(==)|]
700− (conTxt opts conName)
827+ ([|T.pack|] `appE`
828+ conStringE opts conName)
701829 )
702830 ([|pure|] `appE` conE conName)
703831 | con <- cons
@@ -789,7 +917,7 @@ consFromJSON jc tName opts instTys cons = do
789917 caseE (varE conVal)
790918 [ match (conP 'String [varP str])
791919 (guardedB
792− [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
920+ [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] ([|T.pack|] `appE` conStringE opts conName)
793921 )
794922 ([|pure|] `appE` conE conName)
795923 ]
@@ -922,38 +1050,18 @@ parseRecord :: JSONClass
9221050 -> Name
9231051 -> [Name]
9241052 -> Name
925− -> Bool
9261053 -> ExpQ
927−parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject =
928− (if rejectUnknownFields opts
929− then infixApp checkUnknownRecords [|(>>)|]
930− else id) $
1054+parseRecord jc tvMap argTys opts tName conName fields obj =
9311055 foldl' (\a b -> infixApp a [|(<*>)|] b)
9321056 (infixApp (conE conName) [|(<$>)|] x)
9331057 xs
9341058 where
935− tagFieldNameAppender =
936− if inTaggedObject then (tagFieldName (sumEncoding opts) :) else id
937− knownFields = appE [|H.fromList|] $ listE $
938− map (\knownName -> tupE [appE [|T.pack|] $ litE $ stringL knownName, [|()|]]) $
939− tagFieldNameAppender $ map nameBase fields
940− checkUnknownRecords =
941− caseE (appE [|H.keys|] $ infixApp (varE obj) [|H.difference|] knownFields)
942− [ match (listP []) (normalB [|return ()|]) []
943− , newName "unknownFields" >>=
944− \unknownFields -> match (varP unknownFields)
945− (normalB $ appE [|fail|] $ infixApp
946− (litE (stringL "Unknown fields: "))
947− [|(++)|]
948− (appE [|show|] (varE unknownFields)))
949− []
950− ]
9511059 x:xs = [ [|lookupField|]
9521060 `appE` dispatchParseJSON jc conName tvMap argTy
9531061 `appE` litE (stringL $ show tName)
9541062 `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
9551063 `appE` varE obj
956− `appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
1064+ `appE` ( [|T.pack|] `appE` fieldLabelExp opts field
9571065 )
9581066 | (field, argTy) <- zip fields argTys
9591067 ]
@@ -1022,7 +1130,7 @@ parseArgs jc tvMap tName opts
10221130 , constructorFields = argTys }
10231131 (Left (_, obj)) = do
10241132 argTys' <- mapM resolveTypeSynonyms argTys
1025− parseRecord jc tvMap argTys' opts tName conName fields obj True
1133+ parseRecord jc tvMap argTys' opts tName conName fields obj
10261134parseArgs jc tvMap tName opts
10271135 info@ConstructorInfo { constructorName = conName
10281136 , constructorVariant = RecordConstructor fields
@@ -1037,7 +1145,7 @@ parseArgs jc tvMap tName opts
10371145 argTys' <- mapM resolveTypeSynonyms argTys
10381146 caseE (varE valName)
10391147 [ match (conP 'Object [varP obj]) (normalB $
1040− parseRecord jc tvMap argTys' opts tName conName fields obj False) []
1148+ parseRecord jc tvMap argTys' opts tName conName fields obj) []
10411149 , matchFailed tName conName "Object"
10421150 ]
10431151
@@ -1126,11 +1234,6 @@ instance OVERLAPPABLE_ LookupField a where
11261234instance INCOHERENT_ LookupField (Maybe a) where
11271235 lookupField pj _ _ = parseOptionalFieldWith pj
11281236
1129−instance INCOHERENT_ LookupField (Semigroup.Option a) where
1130− lookupField pj tName rec obj key =
1131− fmap Semigroup.Option
1132− (lookupField (fmap Semigroup.getOption . pj) tName rec obj key)
1133−
11341237lookupFieldWith :: (Value -> Parser a) -> String -> String
11351238 -> Object -> T.Text -> Parser a
11361239lookupFieldWith pj tName rec obj key =
@@ -1138,6 +1241,9 @@ lookupFieldWith pj tName rec obj key =
11381241 Nothing -> unknownFieldFail tName rec (T.unpack key)
11391242 Just v -> pj v <?> Key key
11401243
1244+keyValuePairWith :: (v -> Value) -> T.Text -> v -> Pair
1245+keyValuePairWith tj name value = (name, tj value)
1246+
11411247unknownFieldFail :: String -> String -> String -> Parser fail
11421248unknownFieldFail tName rec key =
11431249 fail $ printf "When parsing the record %s of type %s the key %s was not present."
@@ -1220,27 +1326,23 @@ deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
12201326deriveJSONClass consFuns jc opts name = do
12211327 info <- reifyDatatype name
12221328 case info of
1223− DatatypeInfo { datatypeContext = ctxt
1224− , datatypeName = parentName
1225−#if MIN_VERSION_th_abstraction(0,3,0)
1226− , datatypeInstTypes = instTys
1227−#else
1228− , datatypeVars = instTys
1229−#endif
1230− , datatypeVariant = variant
1231− , datatypeCons = cons
1329+ DatatypeInfo { datatypeContext = ctxt
1330+ , datatypeName = parentName
1331+ , datatypeVars = vars
1332+ , datatypeVariant = variant
1333+ , datatypeCons = cons
12321334 } -> do
12331335 (instanceCxt, instanceType)
1234− <- buildTypeInstance parentName jc ctxt instTys variant
1336+ <- buildTypeInstance parentName jc ctxt vars variant
12351337 (:[]) <$> instanceD (return instanceCxt)
12361338 (return instanceType)
1237− (methodDecs parentName instTys cons)
1339+ (methodDecs parentName vars cons)
12381340 where
12391341 methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
1240− methodDecs parentName instTys cons = flip map consFuns $ \(jf, jfMaker) ->
1342+ methodDecs parentName vars cons = flip map consFuns $ \(jf, jfMaker) ->
12411343 funD (jsonFunValName jf (arity jc))
12421344 [ clause []
1243− (normalB $ jfMaker jc parentName opts instTys cons)
1345+ (normalB $ jfMaker jc parentName opts vars cons)
12441346 []
12451347 ]
12461348
@@ -1256,21 +1358,17 @@ mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q
12561358mkFunCommon consFun jc opts name = do
12571359 info <- reifyDatatype name
12581360 case info of
1259− DatatypeInfo { datatypeContext = ctxt
1260− , datatypeName = parentName
1261−#if MIN_VERSION_th_abstraction(0,3,0)
1262− , datatypeInstTypes = instTys
1263−#else
1264− , datatypeVars = instTys
1265−#endif
1266− , datatypeVariant = variant
1267− , datatypeCons = cons
1361+ DatatypeInfo { datatypeContext = ctxt
1362+ , datatypeName = parentName
1363+ , datatypeVars = vars
1364+ , datatypeVariant = variant
1365+ , datatypeCons = cons
12681366 } -> do
12691367 -- We force buildTypeInstance here since it performs some checks for whether
12701368 -- or not the provided datatype's kind matches the derived method's
12711369 -- typeclass, and produces errors if it can't.
1272− !_ <- buildTypeInstance parentName jc ctxt instTys variant
1273− consFun jc parentName opts instTys cons
1370+ !_ <- buildTypeInstance parentName jc ctxt vars variant
1371+ consFun jc parentName opts vars cons
12741372
12751373dispatchFunByType :: JSONClass
12761374 -> JSONFun
@@ -1315,13 +1413,10 @@ dispatchFunByType jc jf conName tvMap list ty = do
13151413 (interleave rhsArgs rhsArgs)
13161414 else varE $ jsonFunValOrListName list jf Arity0
13171415
1318−dispatchToJSON
1319− :: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1320−dispatchToJSON target jc n tvMap =
1321− dispatchFunByType jc (targetToJSONFun target) n tvMap False
1322−
1323−dispatchParseJSON
1416+dispatchToJSON, dispatchToEncoding, dispatchParseJSON
13241417 :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1418+dispatchToJSON jc n tvMap = dispatchFunByType jc ToJSON n tvMap False
1419+dispatchToEncoding jc n tvMap = dispatchFunByType jc ToEncoding n tvMap False
13251420dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
13261421
13271422--------------------------------------------------------------------------------
@@ -1603,13 +1698,21 @@ type TyVarMap = Map Name (Name, Name)
16031698-- | Returns True if a Type has kind *.
16041699hasKindStar :: Type -> Bool
16051700hasKindStar VarT{} = True
1701+#if MIN_VERSION_template_haskell(2,8,0)
16061702hasKindStar (SigT _ StarT) = True
1703+#else
1704+hasKindStar (SigT _ StarK) = True
1705+#endif
16071706hasKindStar _ = False
16081707
16091708-- Returns True is a kind is equal to *, or if it is a kind variable.
16101709isStarOrVar :: Kind -> Bool
1710+#if MIN_VERSION_template_haskell(2,8,0)
16111711isStarOrVar StarT = True
16121712isStarOrVar VarT{} = True
1713+#else
1714+isStarOrVar StarK = True
1715+#endif
16131716isStarOrVar _ = False
16141717
16151718-- Generate a list of fresh names with a common prefix, and numbered suffixes.
@@ -1667,7 +1770,9 @@ isTyFamily (ConT n) = do
16671770#else
16681771 FamilyI (FamilyD TypeFam _ _ _) _ -> True
16691772#endif
1773+#if MIN_VERSION_template_haskell(2,9,0)
16701774 FamilyI ClosedTypeFamilyD{} _ -> True
1775+#endif
16711776 _ -> False
16721777isTyFamily _ = return False
16731778
@@ -1695,7 +1800,9 @@ mentionsName = go
16951800 go :: Type -> [Name] -> Bool
16961801 go (AppT t1 t2) names = go t1 names || go t2 names
16971802 go (SigT t _k) names = go t names
1803+#if MIN_VERSION_template_haskell(2,8,0)
16981804 || go _k names
1805+#endif
16991806 go (VarT n) names = n `elem` names
17001807 go _ _ = False
17011808
@@ -1751,14 +1858,23 @@ uncurryTy t = ([], t :| [])
17511858
17521859-- | Like uncurryType, except on a kind level.
17531860uncurryKind :: Kind -> NonEmpty Kind
1861+#if MIN_VERSION_template_haskell(2,8,0)
17541862uncurryKind = snd . uncurryTy
1863+#else
1864+uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
1865+uncurryKind k = k :| []
1866+#endif
17551867
17561868createKindChain :: Int -> Kind
17571869createKindChain = go starK
17581870 where
17591871 go :: Kind -> Int -> Kind
17601872 go k 0 = k
1873+#if MIN_VERSION_template_haskell(2,8,0)
17611874 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1875+#else
1876+ go k !n = go (ArrowK StarK k) (n - 1)
1877+#endif
17621878
17631879-- | Makes a string literal expression from a constructor's name.
17641880conNameExp :: Options -> ConstructorInfo -> Q Exp
@@ -1768,11 +1884,11 @@ conNameExp opts = litE
17681884 . nameBase
17691885 . constructorName
17701886
1771−-- | Extracts a record field label.
1772−fieldLabel :: Options -- ^ Encoding options
1773− -> Name
1774− -> String
1775−fieldLabel opts = fieldLabelModifier opts . nameBase
1887+-- | Creates a string literal expression from a record field label.
1888+fieldLabelExp :: Options -- ^ Encoding options
1889+ -> Name
1890+ -> Q Exp
1891+fieldLabelExp opts = litE . stringL . fieldLabelModifier opts . nameBase
17761892
17771893-- | The name of the outermost 'Value' constructor.
17781894valueConName :: Value -> String
@@ -1812,7 +1928,11 @@ canEtaReduce remaining dropped =
18121928-------------------------------------------------------------------------------
18131929
18141930applySubstitutionKind :: Map Name Kind -> Type -> Type
1931+#if MIN_VERSION_template_haskell(2,8,0)
18151932applySubstitutionKind = applySubstitution
1933+#else
1934+applySubstitutionKind _ t = t
1935+#endif
18161936
18171937substNameWithKind :: Name -> Kind -> Type -> Type
18181938substNameWithKind n k = applySubstitutionKind (M.singleton n k)
@@ -1901,13 +2021,6 @@ data Direction = To | From
19012021-- | A representation of which typeclass method is being spliced in.
19022022data JSONFun = ToJSON | ToEncoding | ParseJSON
19032023
1904−-- | A refinement of JSONFun to [ToJSON, ToEncoding].
1905−data ToJSONFun = Value | Encoding
1906−
1907−targetToJSONFun :: ToJSONFun -> JSONFun
1908−targetToJSONFun Value = ToJSON
1909−targetToJSONFun Encoding = ToEncoding
1910−
19112024-- | A representation of which typeclass is being derived.
19122025data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
19132026
@@ -1976,7 +2089,9 @@ data StarKindStatus = NotKindStar
19762089canRealizeKindStar :: Type -> StarKindStatus
19772090canRealizeKindStar t = case t of
19782091 _ | hasKindStar t -> KindStar
2092+#if MIN_VERSION_template_haskell(2,8,0)
19792093 SigT _ (VarT k) -> IsKindVar k
2094+#endif
19802095 _ -> NotKindStar
19812096
19822097-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
Data/Aeson/Text.hs+4−5
@@ -1,5 +1,4 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE OverloadedStrings #-}
43-- |
54-- Module: Data.Aeson.Text
@@ -15,18 +14,18 @@
1514--
1615-- You can use the conversions to 'Builder's when embedding JSON messages as
1716-- parts of a protocol.
18−
1917module Data.Aeson.Text
2018 (
2119 encodeToLazyText
2220 , encodeToTextBuilder
2321 ) where
2422
23+import Prelude ()
2524import Prelude.Compat
2625
2726import Data.Aeson.Types (Value(..), ToJSON(..))
2827import Data.Aeson.Encoding (encodingToLazyByteString)
29−import Data.Semigroup ((<>))
28+import Data.Monoid ((<>))
3029import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
3130import Data.Text.Lazy.Builder
3231import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
@@ -47,8 +46,8 @@ encodeToLazyText = LT.decodeUtf8 . encodingToLazyByteString . toEncoding
4746-- embedded efficiently in a text-based protocol.
4847--
4948-- If you are going to immediately encode straight to a
50−-- 'L.ByteString', it is more efficient to use 'encode' (lazy ByteString)
51−-- or @'fromEncoding' . 'toEncoding'@ (ByteString.Builder) instead.
49+-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
50+-- instead.
5251--
5352-- /Note:/ Uses 'toJSON'
5453encodeToTextBuilder :: ToJSON a => a -> Builder
Data/Aeson/Types.hs+2−23
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21-- |
32-- Module: Data.Aeson.Types
43-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -26,7 +25,6 @@ module Data.Aeson.Types
2625 -- * Convenience types and functions
2726 , DotNetTime(..)
2827 , typeMismatch
29− , unexpected
3028 -- * Type conversion
3129 , Parser
3230 , Result(..)
@@ -38,7 +36,6 @@ module Data.Aeson.Types
3836 , ToJSON(..)
3937 , KeyValue(..)
4038 , modifyFailure
41− , prependFailure
4239 , parserThrowError
4340 , parserCatchError
4441
@@ -53,12 +50,6 @@ module Data.Aeson.Types
5350 , coerceFromJSONKeyFunction
5451 , mapFromJSONKeyFunction
5552
56− -- *** Generic keys
57− , GToJSONKey()
58− , genericToJSONKey
59− , GFromJSONKey()
60− , genericFromJSONKey
61−
6253 -- ** Liftings to unary and binary type constructors
6354 , FromJSON1(..)
6455 , parseJSON1
@@ -90,9 +81,9 @@ module Data.Aeson.Types
9081 , withObject
9182 , withText
9283 , withArray
84+ , withNumber
9385 , withScientific
9486 , withBool
95− , withEmbeddedJSON
9687
9788 , pairs
9889 , foldable
@@ -124,7 +115,6 @@ module Data.Aeson.Types
124115 , sumEncoding
125116 , unwrapUnaryRecords
126117 , tagSingleConstructors
127− , rejectUnknownFields
128118
129119 -- ** Options utilities
130120 , SumEncoding(..)
@@ -132,20 +122,9 @@ module Data.Aeson.Types
132122 , camelTo2
133123 , defaultOptions
134124 , defaultTaggedObject
135−
136− -- ** Options for object keys
137− , JSONKeyOptions
138− , keyModifier
139− , defaultJSONKeyOptions
140−
141− -- * Parsing context
142− , (<?>)
143− , JSONPath
144− , JSONPathElement(..)
145− , formatPath
146− , formatRelativePath
147125 ) where
148126
127+import Prelude ()
149128import Prelude.Compat
150129
151130import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
Data/Aeson/Types/Class.hs+3−8
@@ -4,8 +4,8 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE GADTs #-}
66{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
87{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE TypeSynonymInstances #-}
99
1010-- |
1111-- Module: Data.Aeson.Types.Class
@@ -58,11 +58,6 @@ module Data.Aeson.Types.Class
5858 , fromJSONKeyCoerce
5959 , coerceFromJSONKeyFunction
6060 , mapFromJSONKeyFunction
61− -- ** Generic keys
62− , GToJSONKey()
63− , genericToJSONKey
64− , GFromJSONKey()
65− , genericFromJSONKey
6661 -- * Object key-value pairs
6762 , KeyValue(..)
6863
@@ -75,15 +70,14 @@ module Data.Aeson.Types.Class
7570 , withObject
7671 , withText
7772 , withArray
73+ , withNumber
7874 , withScientific
7975 , withBool
80− , withEmbeddedJSON
8176
8277 -- * Functions
8378 , fromJSON
8479 , ifromJSON
8580 , typeMismatch
86− , unexpected
8781 , parseField
8882 , parseFieldMaybe
8983 , parseFieldMaybe'
@@ -97,6 +91,7 @@ module Data.Aeson.Types.Class
9791 , (.!=)
9892 ) where
9993
94+import Prelude ()
10095
10196import Data.Aeson.Types.FromJSON
10297import Data.Aeson.Types.Generic (One, Zero)
Data/Aeson/Types/FromJSON.hs+412−831
@@ -5,16 +5,19 @@
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
77{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
8+{-# LANGUAGE NamedFieldPuns #-}
99{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE PolyKinds #-}
1110{-# LANGUAGE RecordWildCards #-}
1211{-# LANGUAGE ScopedTypeVariables #-}
1312{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE TupleSections #-}
13+{-# LANGUAGE TypeSynonymInstances #-}
1514{-# LANGUAGE UndecidableInstances #-}
1615{-# LANGUAGE ViewPatterns #-}
1716
17+#if __GLASGOW_HASKELL__ >= 706
18+{-# LANGUAGE PolyKinds #-}
19+#endif
20+
1821#include "incoherent-compat.h"
1922#include "overlapping-compat.h"
2023
@@ -42,9 +45,6 @@ module Data.Aeson.Types.FromJSON
4245 , coerceFromJSONKeyFunction
4346 , mapFromJSONKeyFunction
4447
45− , GFromJSONKey()
46− , genericFromJSONKey
47−
4848 -- * List functions
4949 , listParser
5050
@@ -52,22 +52,20 @@ module Data.Aeson.Types.FromJSON
5252 , withObject
5353 , withText
5454 , withArray
55+ , withNumber
5556 , withScientific
5657 , withBool
57− , withEmbeddedJSON
5858
5959 -- * Functions
6060 , fromJSON
6161 , ifromJSON
6262 , typeMismatch
63− , unexpected
6463 , parseField
6564 , parseFieldMaybe
6665 , parseFieldMaybe'
6766 , explicitParseField
6867 , explicitParseFieldMaybe
6968 , explicitParseFieldMaybe'
70− , parseIndexedJSON
7169 -- ** Operators
7270 , (.:)
7371 , (.:?)
@@ -78,16 +76,17 @@ module Data.Aeson.Types.FromJSON
7876 , parseOptionalFieldWith
7977 ) where
8078
79+import Prelude ()
8180import Prelude.Compat
8281
83−import Control.Applicative ((<|>), Const(..), liftA2)
84−import Control.Monad (zipWithM)
82+import Control.Applicative ((<|>), Const(..))
83+import Control.Monad ((<=<), zipWithM)
8584import Data.Aeson.Internal.Functions (mapKey)
86−import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
8785import Data.Aeson.Types.Generic
8886import Data.Aeson.Types.Internal
87+import Data.Attoparsec.Number (Number(..))
8988import Data.Bits (unsafeShiftR)
90−import Data.Fixed (Fixed, HasResolution (resolution), Nano)
89+import Data.Fixed (Fixed, HasResolution)
9190import Data.Functor.Compose (Compose(..))
9291import Data.Functor.Identity (Identity(..))
9392import Data.Functor.Product (Product(..))
@@ -96,31 +95,26 @@ import Data.Hashable (Hashable(..))
9695import Data.Int (Int16, Int32, Int64, Int8)
9796import Data.List.NonEmpty (NonEmpty(..))
9897import Data.Maybe (fromMaybe)
99−import Data.Semigroup ((<>))
98+import Data.Monoid ((<>))
10099import Data.Proxy (Proxy(..))
101100import Data.Ratio ((%), Ratio)
102−import Data.Scientific (Scientific, base10Exponent)
101+import Data.Scientific (Scientific)
103102import Data.Tagged (Tagged(..))
104103import Data.Text (Text, pack, unpack)
105−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
106−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
107−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
108−import Data.Time.Clock.System.Compat (SystemTime (..))
109−import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
104+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
105+import Data.Time.Format (parseTime)
106+import Data.Time.Locale.Compat (defaultTimeLocale)
110107import Data.Traversable as Tr (sequence)
111108import Data.Vector (Vector)
112109import Data.Version (Version, parseVersion)
113−import Data.Void (Void)
114110import Data.Word (Word16, Word32, Word64, Word8)
115111import Foreign.Storable (Storable)
116−import Foreign.C.Types (CTime (..))
117112import GHC.Generics
118113import Numeric.Natural (Natural)
119114import Text.ParserCombinators.ReadP (readP_to_S)
120115import Unsafe.Coerce (unsafeCoerce)
121116import qualified Data.Aeson.Parser.Time as Time
122117import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123−import qualified Data.ByteString.Lazy as L
124118import qualified Data.DList as DList
125119import qualified Data.HashMap.Strict as H
126120import qualified Data.HashSet as HashSet
@@ -143,20 +137,18 @@ import qualified Data.Vector.Primitive as VP
143137import qualified Data.Vector.Storable as VS
144138import qualified Data.Vector.Unboxed as VU
145139
146−import qualified GHC.Exts as Exts
147−import qualified Data.Primitive.Array as PM
148−import qualified Data.Primitive.SmallArray as PM
149−import qualified Data.Primitive.Types as PM
150−
151−#if MIN_VERSION_primitive(0,6,4)
152−#if !MIN_VERSION_primitive(0,7,0)
153−import qualified Data.Primitive.UnliftedArray as PM
140+#ifndef HAS_COERCIBLE
141+#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
154142#endif
155−import qualified Data.Primitive.PrimArray as PM
156−#endif
157−
158143
144+#if HAS_COERCIBLE
159145import Data.Coerce (Coercible, coerce)
146+coerce' :: Coercible a b => a -> b
147+coerce' = coerce
148+#else
149+coerce' :: a -> b
150+coerce' = unsafeCoerce
151+#endif
160152
161153parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
162154parseIndexedJSON p idx value = p value <?> Index idx
@@ -165,7 +157,7 @@ parseIndexedJSON p idx value = p value <?> Index idx
165157parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
166158parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
167159 where
168− p = withArray "(k, v)" $ \ab ->
160+ p = withArray "(k,v)" $ \ab ->
169161 let n = V.length ab
170162 in if n == 2
171163 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
@@ -177,34 +169,43 @@ parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
177169parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
178170parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
179171
172+scientificToNumber :: Scientific -> Number
173+scientificToNumber s
174+ | e < 0 = D $ Scientific.toRealFloat s
175+ | otherwise = I $ c * 10 ^ e
176+ where
177+ e = Scientific.base10Exponent s
178+ c = Scientific.coefficient s
179+{-# INLINE scientificToNumber #-}
180+
180181parseRealFloat :: RealFloat a => String -> Value -> Parser a
181−parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
182−parseRealFloat _ Null = pure (0/0)
183−parseRealFloat name v = prependContext name (unexpected v)
182+parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
183+parseRealFloat _ Null = pure (0/0)
184+parseRealFloat expected v = typeMismatch expected v
184185{-# INLINE parseRealFloat #-}
185186
186−parseIntegralFromScientific :: forall a. Integral a => Scientific -> Parser a
187−parseIntegralFromScientific s =
187+parseIntegralFromScientific :: forall a. Integral a => String -> Scientific -> Parser a
188+parseIntegralFromScientific expected s =
188189 case Scientific.floatingOrInteger s :: Either Double a of
189190 Right x -> pure x
190− Left _ -> fail $ "unexpected floating number " ++ show s
191+ Left _ -> fail $ "Floating number specified for " ++ expected ++ ": " ++ show s
191192{-# INLINE parseIntegralFromScientific #-}
192193
193194parseIntegral :: Integral a => String -> Value -> Parser a
194−parseIntegral name =
195− prependContext name . withBoundedScientific' parseIntegralFromScientific
195+parseIntegral expected =
196+ withScientific expected $ parseIntegralFromScientific expected
196197{-# INLINE parseIntegral #-}
197198
198−parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => Scientific -> Parser a
199−parseBoundedIntegralFromScientific s = maybe
200− (fail $ "value is either floating or will cause over or underflow " ++ show s)
199+parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => String -> Scientific -> Parser a
200+parseBoundedIntegralFromScientific expected s = maybe
201+ (fail $ expected ++ " is either floating or will cause over or underflow: " ++ show s)
201202 pure
202203 (Scientific.toBoundedInteger s)
203204{-# INLINE parseBoundedIntegralFromScientific #-}
204205
205206parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
206−parseBoundedIntegral name =
207− prependContext name . withScientific' parseBoundedIntegralFromScientific
207+parseBoundedIntegral expected =
208+ withScientific expected $ parseBoundedIntegralFromScientific expected
208209{-# INLINE parseBoundedIntegral #-}
209210
210211parseScientificText :: Text -> Parser Scientific
@@ -214,20 +215,13 @@ parseScientificText
214215 . T.encodeUtf8
215216
216217parseIntegralText :: Integral a => String -> Text -> Parser a
217−parseIntegralText name t =
218− prependContext name $
219− parseScientificText t
220− >>= rejectLargeExponent
221− >>= parseIntegralFromScientific
222− where
223− rejectLargeExponent :: Scientific -> Parser Scientific
224− rejectLargeExponent s = withBoundedScientific' pure (Number s)
218+parseIntegralText expected t =
219+ parseScientificText t >>= parseIntegralFromScientific expected
225220{-# INLINE parseIntegralText #-}
226221
227222parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
228−parseBoundedIntegralText name t =
229− prependContext name $
230− parseScientificText t >>= parseBoundedIntegralFromScientific
223+parseBoundedIntegralText expected t =
224+ parseScientificText t >>= parseBoundedIntegralFromScientific expected
231225
232226parseOptionalFieldWith :: (Value -> Parser (Maybe a))
233227 -> Object -> Text -> Parser (Maybe a)
@@ -287,21 +281,14 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
287281--
288282-- The basic ways to signal a failed conversion are as follows:
289283--
290−-- * 'fail' yields a custom error message: it is the recommended way of
291−-- reporting a failure;
284+-- * 'empty' and 'mzero' work, but are terse and uninformative;
292285--
293−-- * 'Control.Applicative.empty' (or 'Control.Monad.mzero') is uninformative:
294−-- use it when the error is meant to be caught by some @('<|>')@;
286+-- * 'fail' yields a custom error message;
295287--
296−-- * 'typeMismatch' can be used to report a failure when the encountered value
297−-- is not of the expected JSON type; 'unexpected' is an appropriate alternative
298−-- when more than one type may be expected, or to keep the expected type
299−-- implicit.
288+-- * 'typeMismatch' produces an informative message for cases when the
289+-- value encountered is not of the expected type.
300290--
301−-- 'prependFailure' (or 'modifyFailure') add more information to a parser's
302−-- error messages.
303−--
304−-- An example type and instance using 'typeMismatch' and 'prependFailure':
291+-- An example type and instance using 'typeMismatch':
305292--
306293-- @
307294-- \-- Allow ourselves to write 'Text' literals.
@@ -315,22 +302,20 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
315302-- '<*>' v '.:' \"y\"
316303--
317304-- \-- We do not expect a non-'Object' value here.
318−-- \-- We could use 'Control.Applicative.empty' to fail, but 'typeMismatch'
305+-- \-- We could use 'mzero' to fail, but 'typeMismatch'
319306-- \-- gives a much more informative error message.
320−-- 'parseJSON' invalid =
321−-- 'prependFailure' "parsing Coord failed, "
322−-- ('typeMismatch' \"Object\" invalid)
307+-- 'parseJSON' invalid = 'typeMismatch' \"Coord\" invalid
323308-- @
324309--
325310-- For this common case of only being concerned with a single
326−-- type of JSON value, the functions 'withObject', 'withScientific', etc.
311+-- type of JSON value, the functions 'withObject', 'withNumber', etc.
327312-- are provided. Their use is to be preferred when possible, since
328313-- they are more terse. Using 'withObject', we can rewrite the above instance
329314-- (assuming the same language extension and data type) as:
330315--
331316-- @
332317-- instance 'FromJSON' Coord where
333−-- 'parseJSON' = 'withObject' \"Coord\" $ \\v -> Coord
318+-- 'parseJSON' = 'withObject' \"Coord\" $ \v -> Coord
334319-- '<$>' v '.:' \"x\"
335320-- '<*>' v '.:' \"y\"
336321-- @
@@ -362,7 +347,7 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
362347-- @
363348--
364349-- The default implementation will be equivalent to
365−-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; if you need different
350+-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; If you need different
366351-- options, you can customize the generic decoding by defining:
367352--
368353-- @
@@ -380,11 +365,13 @@ class FromJSON a where
380365 parseJSON = genericParseJSON defaultOptions
381366
382367 parseJSONList :: Value -> Parser [a]
383− parseJSONList = withArray "[]" $ \a ->
384− zipWithM (parseIndexedJSON parseJSON) [0..]
368+ parseJSONList (Array a)
369+ = zipWithM (parseIndexedJSON parseJSON) [0..]
385370 . V.toList
386371 $ a
387372
373+ parseJSONList v = typeMismatch "[a]" v
374+
388375-------------------------------------------------------------------------------
389376-- Classes and types for map keys
390377-------------------------------------------------------------------------------
@@ -396,16 +383,6 @@ class FromJSON a where
396383-- > newtype SomeId = SomeId { getSomeId :: Text }
397384-- > deriving (Eq,Ord,Hashable,FromJSONKey)
398385--
399−-- If you have a sum of nullary constructors, you may use the generic
400−-- implementation:
401−--
402−-- @
403−-- data Color = Red | Green | Blue
404−-- deriving Generic
405−--
406−-- instance 'FromJSONKey' Color where
407−-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
408−-- @
409386class FromJSONKey a where
410387 -- | Strategy for parsing the key of a map-like container.
411388 fromJSONKey :: FromJSONKeyFunction a
@@ -428,7 +405,11 @@ class FromJSONKey a where
428405-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
429406-- can be inspected, but cannot be constructed.
430407data CoerceText a where
408+#if HAS_COERCIBLE
431409 CoerceText :: Coercible Text a => CoerceText a
410+#else
411+ CoerceText :: CoerceText a
412+#endif
432413
433414-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
434415-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
@@ -448,7 +429,7 @@ data FromJSONKeyFunction a
448429
449430-- | Only law abiding up to interpretation
450431instance Functor FromJSONKeyFunction where
451− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
432+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
452433 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
453434 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
454435 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
@@ -462,7 +443,9 @@ instance Functor FromJSONKeyFunction where
462443--
463444-- On pre GHC 7.8 this is unconstrainted function.
464445fromJSONKeyCoerce ::
446+#if HAS_COERCIBLE
465447 Coercible Text a =>
448+#endif
466449 FromJSONKeyFunction a
467450fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
468451
@@ -470,91 +453,60 @@ fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
470453--
471454-- See note on 'fromJSONKeyCoerce'.
472455coerceFromJSONKeyFunction ::
456+#if HAS_COERCIBLE
473457 Coercible a b =>
458+#endif
474459 FromJSONKeyFunction a -> FromJSONKeyFunction b
460+#if HAS_COERCIBLE
475461coerceFromJSONKeyFunction = coerce
462+#else
463+coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
464+coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
465+coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
466+coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
467+#endif
476468
477469{-# RULES
478470 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
479471 fmap id x = x
480472 #-}
473+#if HAS_COERCIBLE
481474{-# RULES
482475 "FromJSONKeyCoerce: fmap coerce" forall x .
483476 fmap coerce x = coerceFromJSONKeyFunction x
484477 #-}
478+#endif
485479
486480-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
487481mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
488482mapFromJSONKeyFunction = fmap
489483
490−-- | 'fromJSONKey' for 'Generic' types.
491−-- These types must be sums of nullary constructors, whose names will be used
492−-- as keys for JSON objects.
493−--
494−-- See also 'genericToJSONKey'.
495−--
496−-- === __Example__
497−--
498−-- @
499−-- data Color = Red | Green | Blue
500−-- deriving 'Generic'
501−--
502−-- instance 'FromJSONKey' Color where
503−-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
504−-- @
505−genericFromJSONKey :: forall a. (Generic a, GFromJSONKey (Rep a))
506− => JSONKeyOptions
507− -> FromJSONKeyFunction a
508−genericFromJSONKey opts = FromJSONKeyTextParser $ \t ->
509− case parseSumFromString (keyModifier opts) t of
510− Nothing -> fail $
511− "invalid key " ++ show t ++ ", expected one of " ++ show cnames
512− Just k -> pure (to k)
513− where
514− cnames = unTagged2 (constructorTags (keyModifier opts) :: Tagged2 (Rep a) [String])
515−
516−class (ConstructorNames f, SumFromString f) => GFromJSONKey f where
517−instance (ConstructorNames f, SumFromString f) => GFromJSONKey f where
518−
519484-------------------------------------------------------------------------------
520485-- Functions needed for documentation
521486-------------------------------------------------------------------------------
522487
523488-- | Fail parsing due to a type mismatch, with a descriptive message.
524489--
525−-- The following wrappers should generally be prefered:
526−-- 'withObject', 'withArray', 'withText', 'withBool'.
527−--
528−-- ==== Error message example
490+-- Example usage:
529491--
530−-- > typeMismatch "Object" (String "oops")
531−-- > -- Error: "expected Object, but encountered String"
532−typeMismatch :: String -- ^ The name of the JSON type being parsed
533− -- (@\"Object\"@, @\"Array\"@, @\"String\"@, @\"Number\"@,
534− -- @\"Boolean\"@, or @\"Null\"@).
492+-- @
493+-- instance FromJSON Coord where
494+-- parseJSON ('Object' v) = {- type matches, life is good -}
495+-- parseJSON wat = 'typeMismatch' \"Coord\" wat
496+-- @
497+typeMismatch :: String -- ^ The name of the type you are trying to parse.
535498 -> Value -- ^ The actual value encountered.
536499 -> Parser a
537500typeMismatch expected actual =
538− fail $ "expected " ++ expected ++ ", but encountered " ++ typeOf actual
539−
540−-- | Fail parsing due to a type mismatch, when the expected types are implicit.
541−--
542−-- ==== Error message example
543−--
544−-- > unexpected (String "oops")
545−-- > -- Error: "unexpected String"
546−unexpected :: Value -> Parser a
547−unexpected actual = fail $ "unexpected " ++ typeOf actual
548−
549−-- | JSON type of a value, name of the head constructor.
550−typeOf :: Value -> String
551−typeOf v = case v of
552− Object _ -> "Object"
553− Array _ -> "Array"
554− String _ -> "String"
555− Number _ -> "Number"
556− Bool _ -> "Boolean"
557− Null -> "Null"
501+ fail $ "expected " ++ expected ++ ", encountered " ++ name
502+ where
503+ name = case actual of
504+ Object _ -> "Object"
505+ Array _ -> "Array"
506+ String _ -> "String"
507+ Number _ -> "Number"
508+ Bool _ -> "Boolean"
509+ Null -> "Null"
558510
559511-------------------------------------------------------------------------------
560512-- Lifings of FromJSON and ToJSON to unary and binary type constructors
@@ -635,8 +587,9 @@ class FromJSON2 f where
635587 -> (Value -> Parser b)
636588 -> (Value -> Parser [b])
637589 -> Value -> Parser [f a b]
638− liftParseJSONList2 fa ga fb gb = withArray "[]" $ \vals ->
639− fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
590+ liftParseJSONList2 fa ga fb gb v = case v of
591+ Array vals -> fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
592+ _ -> typeMismatch "[a]" v
640593
641594-- | Lift the standard 'parseJSON' function through the type constructor.
642595parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
@@ -650,7 +603,7 @@ parseJSON2 = liftParseJSON2 parseJSON parseJSONList parseJSON parseJSONList
650603-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
651604listParser :: (Value -> Parser a) -> Value -> Parser [a]
652605listParser f (Array xs) = fmap V.toList (V.mapM f xs)
653−listParser _ v = typeMismatch "Array" v
606+listParser _ v = typeMismatch "[a]" v
654607{-# INLINE listParser #-}
655608
656609-------------------------------------------------------------------------------
@@ -668,139 +621,49 @@ instance (FromJSON a) => FromJSON [a] where
668621-- Functions
669622-------------------------------------------------------------------------------
670623
671−-- | Add context to a failure message, indicating the name of the structure
672−-- being parsed.
673−--
674−-- > prependContext "MyType" (fail "[error message]")
675−-- > -- Error: "parsing MyType failed, [error message]"
676−prependContext :: String -> Parser a -> Parser a
677−prependContext name = prependFailure ("parsing " ++ name ++ " failed, ")
678−
679−-- | @'withObject' name f value@ applies @f@ to the 'Object' when @value@
680−-- is an 'Data.Aeson.Object' and fails otherwise.
681−--
682−-- ==== Error message example
683−--
684−-- > withObject "MyType" f (String "oops")
685−-- > -- Error: "parsing MyType failed, expected Object, but encountered String"
624+-- | @'withObject' expected f value@ applies @f@ to the 'Object' when @value@
625+-- is an 'Object' and fails using @'typeMismatch' expected@ otherwise.
686626withObject :: String -> (Object -> Parser a) -> Value -> Parser a
687−withObject _ f (Object obj) = f obj
688−withObject name _ v = prependContext name (typeMismatch "Object" v)
627+withObject _ f (Object obj) = f obj
628+withObject expected _ v = typeMismatch expected v
689629{-# INLINE withObject #-}
690630
691−-- | @'withText' name f value@ applies @f@ to the 'Text' when @value@ is a
692−-- 'Data.Aeson.String' and fails otherwise.
693−--
694−-- ==== Error message example
695−--
696−-- > withText "MyType" f Null
697−-- > -- Error: "parsing MyType failed, expected String, but encountered Null"
631+-- | @'withText' expected f value@ applies @f@ to the 'Text' when @value@ is a
632+-- 'String' and fails using @'typeMismatch' expected@ otherwise.
698633withText :: String -> (Text -> Parser a) -> Value -> Parser a
699−withText _ f (String txt) = f txt
700−withText name _ v = prependContext name (typeMismatch "String" v)
634+withText _ f (String txt) = f txt
635+withText expected _ v = typeMismatch expected v
701636{-# INLINE withText #-}
702637
703638-- | @'withArray' expected f value@ applies @f@ to the 'Array' when @value@ is
704−-- an 'Data.Aeson.Array' and fails otherwise.
705−--
706−-- ==== Error message example
707−--
708−-- > withArray "MyType" f (String "oops")
709−-- > -- Error: "parsing MyType failed, expected Array, but encountered String"
639+-- an 'Array' and fails using @'typeMismatch' expected@ otherwise.
710640withArray :: String -> (Array -> Parser a) -> Value -> Parser a
711−withArray _ f (Array arr) = f arr
712−withArray name _ v = prependContext name (typeMismatch "Array" v)
641+withArray _ f (Array arr) = f arr
642+withArray expected _ v = typeMismatch expected v
713643{-# INLINE withArray #-}
714644
715−-- | @'withScientific' name f value@ applies @f@ to the 'Scientific' number
716−-- when @value@ is a 'Data.Aeson.Number' and fails using 'typeMismatch'
645+-- | @'withNumber' expected f value@ applies @f@ to the 'Number' when @value@
646+-- is a 'Number' and fails using @'typeMismatch' expected@ otherwise.
647+withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
648+withNumber expected f = withScientific expected (f . scientificToNumber)
649+{-# INLINE withNumber #-}
650+{-# DEPRECATED withNumber "Use withScientific instead" #-}
651+
652+-- | @'withScientific' expected f value@ applies @f@ to the 'Scientific' number
653+-- when @value@ is a 'Number' and fails using @'typeMismatch' expected@
717654-- otherwise.
718−--
719−-- /Warning/: If you are converting from a scientific to an unbounded
720−-- type such as 'Integer' you may want to add a restriction on the
721−-- size of the exponent (see 'withBoundedScientific') to prevent
722−-- malicious input from filling up the memory of the target system.
723−--
724−-- ==== Error message example
725−--
726−-- > withScientific "MyType" f (String "oops")
727−-- > -- Error: "parsing MyType failed, expected Number, but encountered String"
728655withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
729−withScientific _ f (Number scientific) = f scientific
730−withScientific name _ v = prependContext name (typeMismatch "Number" v)
656+withScientific _ f (Number scientific) = f scientific
657+withScientific expected _ v = typeMismatch expected v
731658{-# INLINE withScientific #-}
732659
733−-- | A variant of 'withScientific' which doesn't use 'prependContext', so that
734−-- such context can be added separately in a way that also applies when the
735−-- continuation @f :: Scientific -> Parser a@ fails.
736−--
737−-- /Warning/: If you are converting from a scientific to an unbounded
738−-- type such as 'Integer' you may want to add a restriction on the
739−-- size of the exponent (see 'withBoundedScientific') to prevent
740−-- malicious input from filling up the memory of the target system.
741−--
742−-- ==== Error message examples
743−--
744−-- > withScientific' f (String "oops")
745−-- > -- Error: "unexpected String"
746−-- >
747−-- > prependContext "MyType" (withScientific' f (String "oops"))
748−-- > -- Error: "parsing MyType failed, unexpected String"
749−withScientific' :: (Scientific -> Parser a) -> Value -> Parser a
750−withScientific' f v = case v of
751− Number n -> f n
752− _ -> typeMismatch "Number" v
753−{-# INLINE withScientific' #-}
754−
755−-- | @'withBoundedScientific' name f value@ applies @f@ to the 'Scientific' number
756−-- when @value@ is a 'Number' with exponent less than or equal to 1024.
757−withBoundedScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
758−withBoundedScientific name f v = withBoundedScientific_ (prependContext name) f v
759−{-# INLINE withBoundedScientific #-}
760−
761−-- | A variant of 'withBoundedScientific' which doesn't use 'prependContext',
762−-- so that such context can be added separately in a way that also applies
763−-- when the continuation @f :: Scientific -> Parser a@ fails.
764−withBoundedScientific' :: (Scientific -> Parser a) -> Value -> Parser a
765−withBoundedScientific' f v = withBoundedScientific_ id f v
766−{-# INLINE withBoundedScientific' #-}
767−
768−-- | A variant of 'withBoundedScientific_' parameterized by a function to apply
769−-- to the 'Parser' in case of failure.
770−withBoundedScientific_ :: (Parser a -> Parser a) -> (Scientific -> Parser a) -> Value -> Parser a
771−withBoundedScientific_ whenFail f (Number scientific) =
772− if exp10 > 1024
773− then whenFail (fail msg)
774− else f scientific
775− where
776− exp10 = base10Exponent scientific
777− msg = "found a number with exponent " ++ show exp10 ++ ", but it must not be greater than 1024"
778−withBoundedScientific_ whenFail _ v =
779− whenFail (typeMismatch "Number" v)
780−{-# INLINE withBoundedScientific_ #-}
781−
782660-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
783−-- 'Boolean' and fails otherwise.
784−--
785−-- ==== Error message example
786−--
787−-- > withBool "MyType" f (String "oops")
788−-- > -- Error: "parsing MyType failed, expected Boolean, but encountered String"
661+-- 'Bool' and fails using @'typeMismatch' expected@ otherwise.
789662withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
790−withBool _ f (Bool arr) = f arr
791−withBool name _ v = prependContext name (typeMismatch "Boolean" v)
663+withBool _ f (Bool arr) = f arr
664+withBool expected _ v = typeMismatch expected v
792665{-# INLINE withBool #-}
793666
794−-- | Decode a nested JSON-encoded string.
795−withEmbeddedJSON :: String -> (Value -> Parser a) -> Value -> Parser a
796−withEmbeddedJSON _ innerParser (String txt) =
797− either fail innerParser $ eitherDecode (L.fromStrict $ T.encodeUtf8 txt)
798− where
799− eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON
800− eitherFormatError = either (Left . uncurry formatError) Right
801−withEmbeddedJSON name _ v = prependContext name (typeMismatch "String" v)
802−{-# INLINE withEmbeddedJSON #-}
803−
804667-- | Convert a value from JSON, failing if the types do not match.
805668fromJSON :: (FromJSON a) => Value -> Result a
806669fromJSON = parse parseJSON
@@ -863,7 +726,7 @@ parseFieldMaybe' = (.:!)
863726-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
864727explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
865728explicitParseField p obj key = case H.lookup key obj of
866− Nothing -> fail $ "key " ++ show key ++ " not found"
729+ Nothing -> fail $ "key " ++ show key ++ " not present"
867730 Just v -> p v <?> Key key
868731{-# INLINE explicitParseField #-}
869732
@@ -903,47 +766,11 @@ pmval .!= val = fromMaybe val <$> pmval
903766-- Generic parseJSON
904767-------------------------------------------------------------------------------
905768
906−instance GFromJSON arity V1 where
907− -- Whereof we cannot format, thereof we cannot parse:
908− gParseJSON _ _ _ = fail "Attempted to parse empty type"
909−
910−
911769instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
912770 -- Meta-information, which is not handled elsewhere, is just added to the
913771 -- parsed value:
914772 gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
915773
916−-- Information for error messages
917−
918−type TypeName = String
919−type ConName = String
920−
921−-- | Add the name of the type being parsed to a parser's error messages.
922−contextType :: TypeName -> Parser a -> Parser a
923−contextType = prependContext
924−
925−-- | Add the tagKey that will be looked up while building an ADT
926−-- | Produce the error equivalent to
927−-- | Left "Error in $: parsing T failed, expected an object with keys "tag" and
928−-- | "contents", where "tag" i-- |s associated to one of ["Foo", "Bar"],
929−-- | The parser returned error was: could not find key "tag"
930−contextTag :: Text -> [String] -> Parser a -> Parser a
931−contextTag tagKey cnames = prependFailure
932− ("expected Object with key \"" ++ unpack tagKey ++ "\"" ++
933− " containing one of " ++ show cnames ++ ", ")
934−
935−-- | Add the name of the constructor being parsed to a parser's error messages.
936−contextCons :: ConName -> TypeName -> Parser a -> Parser a
937−contextCons cname tname = prependContext (showCons cname tname)
938−
939−-- | Render a constructor as @\"MyType(MyConstructor)\"@.
940−showCons :: ConName -> TypeName -> String
941−showCons cname tname = tname ++ "(" ++ cname ++ ")"
942−
943−--------------------------------------------------------------------------------
944−
945−-- Parsing single fields
946−
947774instance (FromJSON a) => GFromJSON arity (K1 i a) where
948775 -- Constant values are decoded using their FromJSON instance:
949776 gParseJSON _opts _ = fmap K1 . parseJSON
@@ -958,499 +785,339 @@ instance (FromJSON1 f) => GFromJSON One (Rec1 f) where
958785 -- FromJSON1 instance:
959786 gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
960787
961−instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
962− -- If an occurrence of the last type parameter is nested inside two
963− -- composed types, it is decoded by using the outermost type's FromJSON1
964− -- instance to generically decode the innermost type:
965− gParseJSON opts fargs =
966− let gpj = gParseJSON opts fargs in
967− fmap Comp1 . liftParseJSON gpj (listParser gpj)
968−
969−--------------------------------------------------------------------------------
970−
971−instance (GFromJSON' arity a, Datatype d) => GFromJSON arity (D1 d a) where
972− -- Meta-information, which is not handled elsewhere, is just added to the
973− -- parsed value:
974− gParseJSON opts fargs = fmap M1 . gParseJSON' (tname :* opts :* fargs)
975− where
976− tname = moduleName proxy ++ "." ++ datatypeName proxy
977− proxy = undefined :: M1 _i d _f _p
978−
979−-- | 'GFromJSON', after unwrapping the 'D1' constructor, now carrying the data
980−-- type's name.
981−class GFromJSON' arity f where
982− gParseJSON' :: TypeName :* Options :* FromArgs arity a
983− -> Value
984− -> Parser (f a)
788+instance GFromJSON arity U1 where
789+ -- Empty constructors are expected to be encoded as an empty array:
790+ gParseJSON _opts _ v
791+ | isEmptyArray v = pure U1
792+ | otherwise = typeMismatch "unit constructor (U1)" v
985793
986−-- | Single constructor.
987794instance ( ConsFromJSON arity a
988795 , AllNullary (C1 c a) allNullary
989796 , ParseSum arity (C1 c a) allNullary
990− , Constructor c
991− ) => GFromJSON' arity (C1 c a) where
797+ ) => GFromJSON arity (D1 d (C1 c a)) where
992798 -- The option 'tagSingleConstructors' determines whether to wrap
993799 -- a single-constructor type.
994− gParseJSON' p@(_ :* opts :* _)
800+ gParseJSON opts fargs
995801 | tagSingleConstructors opts
996− = (unTagged :: Tagged allNullary (Parser (C1 c a p)) -> Parser (C1 c a p))
997− . parseSum p
998− | otherwise = fmap M1 . consParseJSON (cname :* p)
999− where
1000− cname = conName (undefined :: M1 _i c _f _p)
1001−
1002−-- | Multiple constructors.
1003−instance ( AllNullary (a :+: b) allNullary
1004− , ParseSum arity (a :+: b) allNullary
1005− ) => GFromJSON' arity (a :+: b) where
802+ = fmap M1
803+ . (unTagged :: Tagged allNullary (Parser (C1 c a p)) -> Parser (C1 c a p))
804+ . parseSum opts fargs
805+ | otherwise = fmap M1 . fmap M1 . consParseJSON opts fargs
806+
807+instance (ConsFromJSON arity a) => GFromJSON arity (C1 c a) where
808+ -- Constructors need to be decoded differently depending on whether they're
809+ -- a record or not. This distinction is made by consParseJSON:
810+ gParseJSON opts fargs = fmap M1 . consParseJSON opts fargs
811+
812+instance ( FromProduct arity a, FromProduct arity b
813+ , ProductSize a, ProductSize b
814+ ) => GFromJSON arity (a :*: b) where
815+ -- Products are expected to be encoded to an array. Here we check whether we
816+ -- got an array of the same size as the product, then parse each of the
817+ -- product's elements using parseProduct:
818+ gParseJSON opts fargs = withArray "product (:*:)" $ \arr ->
819+ let lenArray = V.length arr
820+ lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
821+ productSize in
822+ if lenArray == lenProduct
823+ then parseProduct opts fargs arr 0 lenProduct
824+ else fail $ "When expecting a product of " ++ show lenProduct ++
825+ " values, encountered an Array of " ++ show lenArray ++
826+ " elements instead"
827+
828+instance ( AllNullary (a :+: b) allNullary
829+ , ParseSum arity (a :+: b) allNullary
830+ ) => GFromJSON arity (a :+: b) where
1006831 -- If all constructors of a sum datatype are nullary and the
1007832 -- 'allNullaryToStringTag' option is set they are expected to be
1008833 -- encoded as strings. This distinction is made by 'parseSum':
1009− gParseJSON' p =
1010− (unTagged :: Tagged allNullary (Parser ((a :+: b) _d)) ->
1011− Parser ((a :+: b) _d))
1012− . parseSum p
834+ gParseJSON opts fargs =
835+ (unTagged :: Tagged allNullary (Parser ((a :+: b) d)) ->
836+ Parser ((a :+: b) d))
837+ . parseSum opts fargs
838+
839+instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
840+ -- If an occurrence of the last type parameter is nested inside two
841+ -- composed types, it is decoded by using the outermost type's FromJSON1
842+ -- instance to generically decode the innermost type:
843+ gParseJSON opts fargs =
844+ let gpj = gParseJSON opts fargs in
845+ fmap Comp1 . liftParseJSON gpj (listParser gpj)
1013846
1014847--------------------------------------------------------------------------------
1015848
1016849class ParseSum arity f allNullary where
1017− parseSum :: TypeName :* Options :* FromArgs arity a
1018− -> Value
1019− -> Tagged allNullary (Parser (f a))
850+ parseSum :: Options -> FromArgs arity a
851+ -> Value -> Tagged allNullary (Parser (f a))
1020852
1021−instance ( ConstructorNames f
1022− , SumFromString f
853+instance ( SumFromString f
1023854 , FromPair arity f
1024855 , FromTaggedObject arity f
1025856 , FromUntaggedValue arity f
1026857 ) => ParseSum arity f True where
1027− parseSum p@(tname :* opts :* _)
1028− | allNullaryToStringTag opts = Tagged . parseAllNullarySum tname opts
1029− | otherwise = Tagged . parseNonAllNullarySum p
858+ parseSum opts fargs
859+ | allNullaryToStringTag opts = Tagged . parseAllNullarySum opts
860+ | otherwise = Tagged . parseNonAllNullarySum opts fargs
1030861
1031−instance ( ConstructorNames f
1032− , FromPair arity f
862+instance ( FromPair arity f
1033863 , FromTaggedObject arity f
1034864 , FromUntaggedValue arity f
1035865 ) => ParseSum arity f False where
1036− parseSum p = Tagged . parseNonAllNullarySum p
866+ parseSum opts fargs = Tagged . parseNonAllNullarySum opts fargs
1037867
1038868--------------------------------------------------------------------------------
1039869
1040−parseAllNullarySum :: (SumFromString f, ConstructorNames f)
1041− => TypeName -> Options -> Value -> Parser (f a)
1042−parseAllNullarySum tname opts =
1043− withText tname $ \tag ->
1044− maybe (badTag tag) return $
1045− parseSumFromString modifier tag
1046− where
1047− badTag tag = failWithCTags tname modifier $ \cnames ->
1048− "expected one of the tags " ++ show cnames ++
1049− ", but found tag " ++ show tag
1050− modifier = constructorTagModifier opts
1051−
1052−-- | Fail with an informative error message about a mismatched tag.
1053−-- The error message is parameterized by the list of expected tags,
1054−-- to be inferred from the result type of the parser.
1055−failWithCTags
1056− :: forall f a t. ConstructorNames f
1057− => TypeName -> (String -> t) -> ([t] -> String) -> Parser (f a)
1058−failWithCTags tname modifier f =
1059− contextType tname . fail $ f cnames
1060− where
1061− cnames = unTagged2 (constructorTags modifier :: Tagged2 f [t])
870+parseAllNullarySum :: SumFromString f => Options -> Value -> Parser (f a)
871+parseAllNullarySum opts = withText "Text" $ \key ->
872+ maybe (notFound key) return $
873+ parseSumFromString opts key
1062874
1063875class SumFromString f where
1064− parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
876+ parseSumFromString :: Options -> Text -> Maybe (f a)
1065877
1066878instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1067879 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1068880 (R1 <$> parseSumFromString opts key)
1069881
1070882instance (Constructor c) => SumFromString (C1 c U1) where
1071− parseSumFromString modifier key
1072− | key == name = Just $ M1 U1
1073− | otherwise = Nothing
1074− where
1075− name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
1076−
1077−-- For genericFromJSONKey
1078−instance SumFromString a => SumFromString (D1 d a) where
1079− parseSumFromString modifier key = M1 <$> parseSumFromString modifier key
1080−
1081−-- | List of all constructor tags.
1082−constructorTags :: ConstructorNames a => (String -> t) -> Tagged2 a [t]
1083−constructorTags modifier =
1084− fmap DList.toList (constructorNames' modifier)
1085−
1086−-- | List of all constructor names of an ADT, after a given conversion
1087−-- function. (Better inlining.)
1088−class ConstructorNames a where
1089− constructorNames' :: (String -> t) -> Tagged2 a (DList.DList t)
1090−
1091−instance (ConstructorNames a, ConstructorNames b) => ConstructorNames (a :+: b) where
1092− constructorNames' = liftA2 append constructorNames' constructorNames'
1093− where
1094− append
1095− :: Tagged2 a (DList.DList t)
1096− -> Tagged2 b (DList.DList t)
1097− -> Tagged2 (a :+: b) (DList.DList t)
1098− append (Tagged2 xs) (Tagged2 ys) = Tagged2 (DList.append xs ys)
1099−
1100−instance Constructor c => ConstructorNames (C1 c a) where
1101− constructorNames' f = Tagged2 (pure (f cname))
1102− where
1103− cname = conName (undefined :: M1 _i c _f _p)
1104−
1105−-- For genericFromJSONKey
1106−instance ConstructorNames a => ConstructorNames (D1 d a) where
1107− constructorNames' = retag . constructorNames'
1108− where
1109− retag :: Tagged2 a u -> Tagged2 (D1 d a) u
1110− retag (Tagged2 x) = Tagged2 x
883+ parseSumFromString opts key | key == name = Just $ M1 U1
884+ | otherwise = Nothing
885+ where
886+ name = pack $ constructorTagModifier opts $
887+ conName (undefined :: t c U1 p)
1111888
1112889--------------------------------------------------------------------------------
1113−parseNonAllNullarySum :: forall f c arity.
1114− ( FromPair arity f
890+
891+parseNonAllNullarySum :: ( FromPair arity f
1115892 , FromTaggedObject arity f
1116893 , FromUntaggedValue arity f
1117− , ConstructorNames f
1118− ) => TypeName :* Options :* FromArgs arity c
894+ ) => Options -> FromArgs arity c
1119895 -> Value -> Parser (f c)
1120−parseNonAllNullarySum p@(tname :* opts :* _) =
896+parseNonAllNullarySum opts fargs =
1121897 case sumEncoding opts of
1122898 TaggedObject{..} ->
1123− withObject tname $ \obj -> do
1124− tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
1125− fromMaybe (badTag tag <?> Key tagKey) $
1126− parseFromTaggedObject (tag :* contentsFieldName :* p) obj
1127− where
1128− tagKey = pack tagFieldName
1129− badTag tag = failWith_ $ \cnames ->
1130− "expected tag field to be one of " ++ show cnames ++
1131− ", but found tag " ++ show tag
1132− cnames_ = unTagged2 (constructorTags (constructorTagModifier opts) :: Tagged2 f [String])
899+ withObject "Object" $ \obj -> do
900+ tag <- obj .: pack tagFieldName
901+ fromMaybe (notFound tag) $
902+ parseFromTaggedObject opts fargs contentsFieldName obj tag
1133903
1134904 ObjectWithSingleField ->
1135− withObject tname $ \obj -> case H.toList obj of
1136− [(tag, v)] -> maybe (badTag tag) (<?> Key tag) $
1137− parsePair (tag :* p) v
1138− _ -> contextType tname . fail $
1139− "expected an Object with a single pair, but found " ++
1140− show (H.size obj) ++ " pairs"
1141− where
1142− badTag tag = failWith_ $ \cnames ->
1143− "expected an Object with a single pair where the tag is one of " ++
1144− show cnames ++ ", but found tag " ++ show tag
905+ withObject "Object" $ \obj ->
906+ case H.toList obj of
907+ [pair@(tag, _)] -> fromMaybe (notFound tag) $
908+ parsePair opts fargs pair
909+ _ -> fail "Object doesn't have a single field"
1145910
1146911 TwoElemArray ->
1147− withArray tname $ \arr -> case V.length arr of
1148− 2 | String tag <- V.unsafeIndex arr 0 ->
1149− maybe (badTag tag <?> Index 0) (<?> Index 1) $
1150− parsePair (tag :* p) (V.unsafeIndex arr 1)
1151− | otherwise ->
1152− contextType tname $
1153− fail "tag element is not a String" <?> Index 0
1154− len -> contextType tname . fail $
1155− "expected a 2-element Array, but encountered an Array of length " ++
1156− show len
1157− where
1158− badTag tag = failWith_ $ \cnames ->
1159− "expected tag of the 2-element Array to be one of " ++
1160− show cnames ++ ", but found tag " ++ show tag
912+ withArray "Array" $ \arr ->
913+ if V.length arr == 2
914+ then case V.unsafeIndex arr 0 of
915+ String tag -> fromMaybe (notFound tag) $
916+ parsePair opts fargs (tag, V.unsafeIndex arr 1)
917+ _ -> fail "First element is not a String"
918+ else fail "Array doesn't have 2 elements"
1161919
1162− UntaggedValue -> parseUntaggedValue p
1163− where
1164− failWith_ = failWithCTags tname (constructorTagModifier opts)
920+ UntaggedValue -> parseUntaggedValue opts fargs
1165921
1166922--------------------------------------------------------------------------------
1167923
1168924class FromTaggedObject arity f where
1169− -- The first two components of the parameter tuple are: the constructor tag
1170− -- to match against, and the contents field name.
1171− parseFromTaggedObject
1172− :: Text :* String :* TypeName :* Options :* FromArgs arity a
1173− -> Object
1174− -> Maybe (Parser (f a))
925+ parseFromTaggedObject :: Options -> FromArgs arity a
926+ -> String -> Object
927+ -> Text -> Maybe (Parser (f a))
1175928
1176929instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
1177930 FromTaggedObject arity (a :+: b) where
1178− parseFromTaggedObject p obj =
1179− (fmap L1 <$> parseFromTaggedObject p obj) <|>
1180− (fmap R1 <$> parseFromTaggedObject p obj)
931+ parseFromTaggedObject opts fargs contentsFieldName obj tag =
932+ (fmap L1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag) <|>
933+ (fmap R1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag)
1181934
1182−instance ( IsRecord f isRecord
1183− , FromTaggedObject' arity f isRecord
935+instance ( FromTaggedObject' arity f
1184936 , Constructor c
1185937 ) => FromTaggedObject arity (C1 c f) where
1186− parseFromTaggedObject (tag :* contentsFieldName :* p@(_ :* opts :* _))
1187− | tag == tag'
1188− = Just . fmap M1 .
1189− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
1190− parseFromTaggedObject' (contentsFieldName :* cname :* p)
1191− | otherwise = const Nothing
1192− where
1193− tag' = pack $ constructorTagModifier opts cname
1194− cname = conName (undefined :: M1 _i c _f _p)
938+ parseFromTaggedObject opts fargs contentsFieldName obj tag
939+ | tag == name = Just $ M1 <$> parseFromTaggedObject'
940+ opts fargs contentsFieldName obj
941+ | otherwise = Nothing
942+ where
943+ name = pack $ constructorTagModifier opts $
944+ conName (undefined :: t c f p)
1195945
1196946--------------------------------------------------------------------------------
1197947
1198−class FromTaggedObject' arity f isRecord where
1199− -- The first component of the parameter tuple is the contents field name.
1200− parseFromTaggedObject'
1201− :: String :* ConName :* TypeName :* Options :* FromArgs arity a
1202− -> Object -> Tagged isRecord (Parser (f a))
1203−
1204−instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
1205− -- Records are unpacked in the tagged object
1206− parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
1207−
1208−instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
1209− -- Nonnullary nonrecords are encoded in the contents field
1210− parseFromTaggedObject' p obj = Tagged $ do
1211− contents <- contextCons cname tname (obj .: key)
1212− consParseJSON p' contents <?> Key key
1213− where
1214− key = pack contentsFieldName
1215− contentsFieldName :* p'@(cname :* tname :* _) = p
948+class FromTaggedObject' arity f where
949+ parseFromTaggedObject' :: Options -> FromArgs arity a -> String
950+ -> Object -> Parser (f a)
951+
952+class FromTaggedObject'' arity f isRecord where
953+ parseFromTaggedObject'' :: Options -> FromArgs arity a -> String
954+ -> Object -> Tagged isRecord (Parser (f a))
955+
956+instance ( IsRecord f isRecord
957+ , FromTaggedObject'' arity f isRecord
958+ ) => FromTaggedObject' arity f where
959+ parseFromTaggedObject' opts fargs contentsFieldName =
960+ (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
961+ parseFromTaggedObject'' opts fargs contentsFieldName
962+
963+instance (FromRecord arity f) => FromTaggedObject'' arity f True where
964+ parseFromTaggedObject'' opts fargs _ =
965+ Tagged . parseRecord opts fargs Nothing
1216966
1217−instance OVERLAPPING_ FromTaggedObject' arity U1 False where
1218− -- Nullary constructors don't need a contents field
1219− parseFromTaggedObject' _ _ = Tagged (pure U1)
967+instance (GFromJSON arity f) => FromTaggedObject'' arity f False where
968+ parseFromTaggedObject'' opts fargs contentsFieldName = Tagged .
969+ (gParseJSON opts fargs <=< (.: pack contentsFieldName))
970+
971+instance OVERLAPPING_ FromTaggedObject'' arity U1 False where
972+ parseFromTaggedObject'' _ _ _ _ = Tagged (pure U1)
1220973
1221974--------------------------------------------------------------------------------
1222975
1223−-- | Constructors need to be decoded differently depending on whether they're
1224−-- a record or not. This distinction is made by 'ConsParseJSON'.
1225976class ConsFromJSON arity f where
1226− consParseJSON
1227− :: ConName :* TypeName :* Options :* FromArgs arity a
1228− -> Value -> Parser (f a)
977+ consParseJSON :: Options -> FromArgs arity a
978+ -> Value -> Parser (f a)
1229979
1230980class ConsFromJSON' arity f isRecord where
1231− consParseJSON'
1232− :: ConName :* TypeName :* Options :* FromArgs arity a
1233− -> Value -> Tagged isRecord (Parser (f a))
981+ consParseJSON' :: Options -> FromArgs arity a
982+ -> Maybe Text -- ^ A dummy label
983+ -- (Nothing to use proper label)
984+ -> Value -> Tagged isRecord (Parser (f a))
1234985
1235986instance ( IsRecord f isRecord
1236987 , ConsFromJSON' arity f isRecord
1237988 ) => ConsFromJSON arity f where
1238− consParseJSON p =
1239− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
1240− . consParseJSON' p
989+ consParseJSON opts fargs v = let
990+ (v2,lab) = case (unwrapUnaryRecords opts,isUnary (undefined :: f a)) of
991+ -- use a dummy object with a dummy label
992+ (True,True) -> (object [(pack "dummy",v)], Just $ pack "dummy")
993+ _ ->(v,Nothing)
994+ in (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
995+ $ consParseJSON' opts fargs lab v2
1241996
1242−instance OVERLAPPING_
1243− ( GFromJSON arity a, RecordFromJSON arity (S1 s a)
1244− ) => ConsFromJSON' arity (S1 s a) True where
1245− consParseJSON' p@(cname :* tname :* opts :* fargs)
1246− | unwrapUnaryRecords opts = Tagged . fmap M1 . gParseJSON opts fargs
1247− | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1248997
1249−instance RecordFromJSON arity f => ConsFromJSON' arity f True where
1250− consParseJSON' p@(cname :* tname :* _) =
1251− Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
998+instance (FromRecord arity f) => ConsFromJSON' arity f True where
999+ consParseJSON' opts fargs mlab = Tagged . withObject "record (:*:)"
1000+ (parseRecord opts fargs mlab)
12521001
1253−instance OVERLAPPING_
1254− ConsFromJSON' arity U1 False where
1255− -- Empty constructors are expected to be encoded as an empty array:
1256− consParseJSON' (cname :* tname :* _) v =
1257− Tagged . contextCons cname tname $ case v of
1258− Array a | V.null a -> pure U1
1259− | otherwise -> fail_ a
1260− _ -> typeMismatch "Array" v
1261− where
1262− fail_ a = fail $
1263− "expected an empty Array, but encountered an Array of length " ++
1264− show (V.length a)
1265−
1266−instance OVERLAPPING_
1267− GFromJSON arity f => ConsFromJSON' arity (S1 s f) False where
1268− consParseJSON' (_ :* _ :* opts :* fargs) =
1269− Tagged . fmap M1 . gParseJSON opts fargs
1270−
1271−instance (ProductFromJSON arity f, ProductSize f
1272− ) => ConsFromJSON' arity f False where
1273− consParseJSON' p = Tagged . productParseJSON0 p
1002+instance (GFromJSON arity f) => ConsFromJSON' arity f False where
1003+ consParseJSON' opts fargs _ = Tagged . gParseJSON opts fargs
12741004
12751005--------------------------------------------------------------------------------
12761006
1277−class FieldNames f where
1278− fieldNames :: f a -> [Text] -> [Text]
1279−
1280−instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where
1281− fieldNames _ =
1282− fieldNames (undefined :: a x) .
1283− fieldNames (undefined :: b y)
1007+class FromRecord arity f where
1008+ parseRecord :: Options -> FromArgs arity a
1009+ -> Maybe Text -- ^ A dummy label
1010+ -- (Nothing to use proper label)
1011+ -> Object -> Parser (f a)
12841012
1285−instance (Selector s) => FieldNames (S1 s f) where
1286− fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)
1287−
1288−class RecordFromJSON arity f where
1289− recordParseJSON
1290− :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
1291− -> Object -> Parser (f a)
1292−
1293−instance ( FieldNames f
1294− , RecordFromJSON' arity f
1295− ) => RecordFromJSON arity f where
1296− recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
1297− \obj -> checkUnknown obj >> recordParseJSON' p obj
1298− where
1299− knownFields :: H.HashMap Text ()
1300− knownFields = H.fromList $ map (,()) $
1301− fieldNames (undefined :: f a)
1302− [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]
1303− checkUnknown =
1304− if not (rejectUnknownFields opts)
1305− then \_ -> return ()
1306− else \obj -> case H.keys (H.difference obj knownFields) of
1307− [] -> return ()
1308− unknownFields -> contextCons cname tname $
1309− fail ("unknown fields: " ++ show unknownFields)
1310−
1311−class RecordFromJSON' arity f where
1312− recordParseJSON'
1313− :: ConName :* TypeName :* Options :* FromArgs arity a
1314− -> Object -> Parser (f a)
1315−
1316−instance ( RecordFromJSON' arity a
1317− , RecordFromJSON' arity b
1318− ) => RecordFromJSON' arity (a :*: b) where
1319− recordParseJSON' p obj =
1320− (:*:) <$> recordParseJSON' p obj
1321− <*> recordParseJSON' p obj
1013+instance ( FromRecord arity a
1014+ , FromRecord arity b
1015+ ) => FromRecord arity (a :*: b) where
1016+ parseRecord opts fargs _ obj =
1017+ (:*:) <$> parseRecord opts fargs Nothing obj
1018+ <*> parseRecord opts fargs Nothing obj
13221019
13231020instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1324− RecordFromJSON' arity (S1 s a) where
1325− recordParseJSON' (cname :* tname :* opts :* fargs) obj = do
1326− fv <- contextCons cname tname (obj .: label)
1327− M1 <$> gParseJSON opts fargs fv <?> Key label
1328− where
1329− label = pack $ fieldLabelModifier opts sname
1330− sname = selName (undefined :: M1 _i s _f _p)
1331−
1332−instance INCOHERENT_ (Selector s, FromJSON a) =>
1333− RecordFromJSON' arity (S1 s (K1 i (Maybe a))) where
1334− recordParseJSON' (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1335− where
1336− label = fieldLabelModifier opts sname
1337− sname = selName (undefined :: M1 _i s _f _p)
1021+ FromRecord arity (S1 s a) where
1022+ parseRecord opts fargs lab =
1023+ (<?> Key label) . gParseJSON opts fargs <=< (.: label)
1024+ where
1025+ label = fromMaybe defLabel lab
1026+ defLabel = pack . fieldLabelModifier opts $
1027+ selName (undefined :: t s a p)
13381028
1339−-- Parse an Option like a Maybe.
13401029instance INCOHERENT_ (Selector s, FromJSON a) =>
1341− RecordFromJSON' arity (S1 s (K1 i (Semigroup.Option a))) where
1342− recordParseJSON' p obj = wrap <$> recordParseJSON' p obj
1343− where
1344− wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
1345− wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
1030+ FromRecord arity (S1 s (K1 i (Maybe a))) where
1031+ parseRecord _ _ (Just lab) obj = (M1 . K1) <$> obj .:? lab
1032+ parseRecord opts _ Nothing obj = (M1 . K1) <$> obj .:? pack label
1033+ where
1034+ label = fieldLabelModifier opts $
1035+ selName (undefined :: t s (K1 i (Maybe a)) p)
13461036
13471037--------------------------------------------------------------------------------
13481038
1349−productParseJSON0
1350− :: forall f arity a. (ProductFromJSON arity f, ProductSize f)
1351− => ConName :* TypeName :* Options :* FromArgs arity a
1352− -> Value -> Parser (f a)
1353− -- Products are expected to be encoded to an array. Here we check whether we
1354− -- got an array of the same size as the product, then parse each of the
1355− -- product's elements using productParseJSON:
1356−productParseJSON0 p@(cname :* tname :* _ :* _) =
1357− withArray (showCons cname tname) $ \arr ->
1358− let lenArray = V.length arr
1359− lenProduct = (unTagged2 :: Tagged2 f Int -> Int)
1360− productSize in
1361− if lenArray == lenProduct
1362− then productParseJSON p arr 0 lenProduct
1363− else contextCons cname tname $
1364− fail $ "expected an Array of length " ++ show lenProduct ++
1365− ", but encountered an Array of length " ++ show lenArray
1366−
1367−--
1368−
1369−class ProductFromJSON arity f where
1370− productParseJSON :: ConName :* TypeName :* Options :* FromArgs arity a
1039+class FromProduct arity f where
1040+ parseProduct :: Options -> FromArgs arity a
13711041 -> Array -> Int -> Int
13721042 -> Parser (f a)
13731043
1374−instance ( ProductFromJSON arity a
1375− , ProductFromJSON arity b
1376− ) => ProductFromJSON arity (a :*: b) where
1377− productParseJSON p arr ix len =
1378− (:*:) <$> productParseJSON p arr ix lenL
1379− <*> productParseJSON p arr ixR lenR
1044+instance ( FromProduct arity a
1045+ , FromProduct arity b
1046+ ) => FromProduct arity (a :*: b) where
1047+ parseProduct opts fargs arr ix len =
1048+ (:*:) <$> parseProduct opts fargs arr ix lenL
1049+ <*> parseProduct opts fargs arr ixR lenR
13801050 where
13811051 lenL = len `unsafeShiftR` 1
13821052 ixR = ix + lenL
13831053 lenR = len - lenL
13841054
1385−instance (GFromJSON arity a) => ProductFromJSON arity (S1 s a) where
1386− productParseJSON (_ :* _ :* opts :* fargs) arr ix _ =
1387− M1 <$> gParseJSON opts fargs (V.unsafeIndex arr ix) <?> Index ix
1055+instance (GFromJSON arity a) => FromProduct arity (S1 s a) where
1056+ parseProduct opts fargs arr ix _ =
1057+ gParseJSON opts fargs $ V.unsafeIndex arr ix
13881058
13891059--------------------------------------------------------------------------------
13901060
13911061class FromPair arity f where
1392− -- The first component of the parameter tuple is the tag to match.
1393− parsePair :: Text :* TypeName :* Options :* FromArgs arity a
1394− -> Value
1395− -> Maybe (Parser (f a))
1062+ parsePair :: Options -> FromArgs arity a
1063+ -> Pair -> Maybe (Parser (f a))
13961064
13971065instance ( FromPair arity a
13981066 , FromPair arity b
13991067 ) => FromPair arity (a :+: b) where
1400− parsePair p pair =
1401− (fmap L1 <$> parsePair p pair) <|>
1402− (fmap R1 <$> parsePair p pair)
1068+ parsePair opts fargs pair = (fmap L1 <$> parsePair opts fargs pair) <|>
1069+ (fmap R1 <$> parsePair opts fargs pair)
14031070
14041071instance ( Constructor c
1072+ , GFromJSON arity a
14051073 , ConsFromJSON arity a
14061074 ) => FromPair arity (C1 c a) where
1407− parsePair (tag :* p@(_ :* opts :* _)) v
1408− | tag == tag' = Just $ M1 <$> consParseJSON (cname :* p) v
1075+ parsePair opts fargs (tag, value)
1076+ | tag == tag' = Just $ gParseJSON opts fargs value
14091077 | otherwise = Nothing
1410− where
1411− tag' = pack $ constructorTagModifier opts cname
1412− cname = conName (undefined :: M1 _i c _a _p)
1078+ where
1079+ tag' = pack $ constructorTagModifier opts $
1080+ conName (undefined :: t c a p)
14131081
14141082--------------------------------------------------------------------------------
14151083
14161084class FromUntaggedValue arity f where
1417− parseUntaggedValue :: TypeName :* Options :* FromArgs arity a
1418− -> Value
1419− -> Parser (f a)
1085+ parseUntaggedValue :: Options -> FromArgs arity a
1086+ -> Value -> Parser (f a)
14201087
14211088instance
14221089 ( FromUntaggedValue arity a
14231090 , FromUntaggedValue arity b
14241091 ) => FromUntaggedValue arity (a :+: b)
14251092 where
1426− parseUntaggedValue p value =
1427− L1 <$> parseUntaggedValue p value <|>
1428− R1 <$> parseUntaggedValue p value
1093+ parseUntaggedValue opts fargs value =
1094+ L1 <$> parseUntaggedValue opts fargs value <|>
1095+ R1 <$> parseUntaggedValue opts fargs value
14291096
14301097instance OVERLAPPABLE_
1431− ( ConsFromJSON arity a
1432− , Constructor c
1098+ ( GFromJSON arity a
1099+ , ConsFromJSON arity a
14331100 ) => FromUntaggedValue arity (C1 c a)
14341101 where
1435− parseUntaggedValue p = fmap M1 . consParseJSON (cname :* p)
1436− where
1437− cname = conName (undefined :: M1 _i c _f _p)
1102+ parseUntaggedValue = gParseJSON
14381103
14391104instance OVERLAPPING_
14401105 ( Constructor c )
14411106 => FromUntaggedValue arity (C1 c U1)
14421107 where
1443− parseUntaggedValue (tname :* opts :* _) v =
1444− contextCons cname tname $ case v of
1445− String tag
1446− | tag == tag' -> pure $ M1 U1
1447− | otherwise -> fail_ tag
1448− _ -> typeMismatch "String" v
1449− where
1450− tag' = pack $ constructorTagModifier opts cname
1451− cname = conName (undefined :: M1 _i c _f _p)
1452− fail_ tag = fail $
1453− "expected tag " ++ show tag' ++ ", but found tag " ++ show tag
1108+ parseUntaggedValue opts _ (String s)
1109+ | s == pack (constructorTagModifier opts (conName (undefined :: t c U1 p))) =
1110+ pure $ M1 U1
1111+ | otherwise =
1112+ fail $ "Invalid tag: " ++ unpack s
1113+ parseUntaggedValue _ _ v = typeMismatch (conName (undefined :: t c U1 p)) v
1114+
1115+
1116+--------------------------------------------------------------------------------
1117+
1118+notFound :: Text -> Parser a
1119+notFound key = fail $ "The key \"" ++ unpack key ++ "\" was not found"
1120+{-# INLINE notFound #-}
14541121
14551122-------------------------------------------------------------------------------
14561123-- Instances
@@ -1508,20 +1175,17 @@ instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
15081175 parseJSON = parseJSON2
15091176 {-# INLINE parseJSON #-}
15101177
1511−instance FromJSON Void where
1512− parseJSON _ = fail "Cannot parse Void"
1513− {-# INLINE parseJSON #-}
1178+
15141179
15151180instance FromJSON Bool where
1516− parseJSON (Bool b) = pure b
1517− parseJSON v = typeMismatch "Bool" v
1181+ parseJSON = withBool "Bool" pure
15181182 {-# INLINE parseJSON #-}
15191183
15201184instance FromJSONKey Bool where
15211185 fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
15221186 "true" -> pure True
15231187 "false" -> pure False
1524− _ -> fail $ "cannot parse key " ++ show t ++ " into Bool"
1188+ _ -> fail $ "Cannot parse key into Bool: " ++ T.unpack t
15251189
15261190instance FromJSON Ordering where
15271191 parseJSON = withText "Ordering" $ \s ->
@@ -1529,30 +1193,25 @@ instance FromJSON Ordering where
15291193 "LT" -> return LT
15301194 "EQ" -> return EQ
15311195 "GT" -> return GT
1532− _ -> fail $ "parsing Ordering failed, unexpected " ++ show s ++
1533− " (expected \"LT\", \"EQ\", or \"GT\")"
1196+ _ -> fail "Parsing Ordering value failed: expected \"LT\", \"EQ\", or \"GT\""
15341197
15351198instance FromJSON () where
15361199 parseJSON = withArray "()" $ \v ->
15371200 if V.null v
15381201 then pure ()
1539− else prependContext "()" $ fail "expected an empty array"
1202+ else fail "Expected an empty array"
15401203 {-# INLINE parseJSON #-}
15411204
15421205instance FromJSON Char where
1543− parseJSON = withText "Char" parseChar
1206+ parseJSON = withText "Char" $ \t ->
1207+ if T.compareLength t 1 == EQ
1208+ then pure $ T.head t
1209+ else fail "Expected a string of length 1"
15441210 {-# INLINE parseJSON #-}
15451211
1546− parseJSONList (String s) = pure (T.unpack s)
1547− parseJSONList v = typeMismatch "String" v
1212+ parseJSONList = withText "String" $ pure . T.unpack
15481213 {-# INLINE parseJSONList #-}
15491214
1550−parseChar :: Text -> Parser Char
1551−parseChar t =
1552− if T.compareLength t 1 == EQ
1553− then pure $ T.head t
1554− else prependContext "Char" $ fail "expected a string of length 1"
1555−
15561215instance FromJSON Double where
15571216 parseJSON = parseRealFloat "Double"
15581217 {-# INLINE parseJSON #-}
@@ -1564,6 +1223,12 @@ instance FromJSONKey Double where
15641223 "-Infinity" -> pure (negate 1/0)
15651224 _ -> Scientific.toRealFloat <$> parseScientificText t
15661225
1226+instance FromJSON Number where
1227+ parseJSON (Number s) = pure $ scientificToNumber s
1228+ parseJSON Null = pure (D (0/0))
1229+ parseJSON v = typeMismatch "Number" v
1230+ {-# INLINE parseJSON #-}
1231+
15671232instance FromJSON Float where
15681233 parseJSON = parseRealFloat "Float"
15691234 {-# INLINE parseJSON #-}
@@ -1576,30 +1241,17 @@ instance FromJSONKey Float where
15761241 _ -> Scientific.toRealFloat <$> parseScientificText t
15771242
15781243instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1579− parseJSON (Number x)
1580− | exp10 <= 1024
1581− , exp10 >= -1024 = return $! realToFrac x
1582− | otherwise = prependContext "Ratio" $ fail msg
1583− where
1584− exp10 = base10Exponent x
1585− msg = "found a number with exponent " ++ show exp10
1586− ++ ", but it must not be greater than 1024 or less than -1024"
1587− parseJSON o = objParser o
1588− where
1589− objParser = withObject "Rational" $ \obj -> do
1590− numerator <- obj .: "numerator"
1591− denominator <- obj .: "denominator"
1592− if denominator == 0
1593− then fail "Ratio denominator was 0"
1594− else pure $ numerator % denominator
1595− {-# INLINE parseJSON #-}
1596−
1597−-- | This instance includes a bounds check to prevent maliciously
1598−-- large inputs to fill up the memory of the target system. You can
1599−-- newtype 'Scientific' and provide your own instance using
1600−-- 'withScientific' if you want to allow larger inputs.
1244+ parseJSON = withObject "Rational" $ \obj ->
1245+ (%) <$> obj .: "numerator"
1246+ <*> obj .: "denominator"
1247+ {-# INLINE parseJSON #-}
1248+
1249+-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
1250+-- since an attacker could easily fill up the memory of the target
1251+-- system by specifying a scientific number with a big exponent like
1252+-- @1e1000000000@.
16011253instance HasResolution a => FromJSON (Fixed a) where
1602− parseJSON = prependContext "Fixed" . withBoundedScientific' (pure . realToFrac)
1254+ parseJSON = withScientific "Fixed" $ pure . realToFrac
16031255 {-# INLINE parseJSON #-}
16041256
16051257instance FromJSON Int where
@@ -1609,10 +1261,10 @@ instance FromJSON Int where
16091261instance FromJSONKey Int where
16101262 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
16111263
1612−-- | This instance includes a bounds check to prevent maliciously
1613−-- large inputs to fill up the memory of the target system. You can
1614−-- newtype 'Scientific' and provide your own instance using
1615−-- 'withScientific' if you want to allow larger inputs.
1264+-- | /WARNING:/ Only parse Integers from trusted input since an
1265+-- attacker could easily fill up the memory of the target system by
1266+-- specifying a scientific number with a big exponent like
1267+-- @1e1000000000@.
16161268instance FromJSON Integer where
16171269 parseJSON = parseIntegral "Integer"
16181270 {-# INLINE parseJSON #-}
@@ -1621,21 +1273,16 @@ instance FromJSONKey Integer where
16211273 fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
16221274
16231275instance FromJSON Natural where
1624− parseJSON value = do
1625− integer <- parseIntegral "Natural" value
1626− parseNatural integer
1276+ parseJSON = withScientific "Natural" $ \s ->
1277+ if Scientific.coefficient s < 0
1278+ then fail $ "Expected a Natural number but got the negative number: " <> show s
1279+ else pure $ truncate s
16271280
16281281instance FromJSONKey Natural where
1629− fromJSONKey = FromJSONKeyTextParser $ \text -> do
1630− integer <- parseIntegralText "Natural" text
1631− parseNatural integer
1632−
1633−parseNatural :: Integer -> Parser Natural
1634−parseNatural integer =
1635− if integer < 0 then
1636− fail $ "parsing Natural failed, unexpected negative number " <> show integer
1637− else
1638− pure $ fromIntegral integer
1282+ fromJSONKey = FromJSONKeyTextParser $ \t -> parseScientificText t >>= \s ->
1283+ if Scientific.coefficient s < 0
1284+ then fail $ "Expected a Natural number but got the negative number: " <> show s
1285+ else pure $ truncate s
16391286
16401287instance FromJSON Int8 where
16411288 parseJSON = parseBoundedIntegral "Int8"
@@ -1700,9 +1347,6 @@ instance FromJSON Word64 where
17001347instance FromJSONKey Word64 where
17011348 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
17021349
1703−instance FromJSON CTime where
1704− parseJSON = fmap CTime . parseJSON
1705− {-# INLINE parseJSON #-}
17061350
17071351instance FromJSON Text where
17081352 parseJSON = withText "Text" pure
@@ -1732,17 +1376,17 @@ parseVersionText = go . readP_to_S parseVersion . unpack
17321376 where
17331377 go [(v,[])] = return v
17341378 go (_ : xs) = go xs
1735− go _ = fail "parsing Version failed"
1379+ go _ = fail "could not parse Version"
17361380
17371381-------------------------------------------------------------------------------
17381382-- semigroups NonEmpty
17391383-------------------------------------------------------------------------------
17401384
17411385instance FromJSON1 NonEmpty where
1742− liftParseJSON p _ = withArray "NonEmpty" $
1386+ liftParseJSON p _ = withArray "NonEmpty a" $
17431387 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
17441388 where
1745− ne [] = fail "parsing NonEmpty failed, unexpected empty list"
1389+ ne [] = fail "Expected a NonEmpty but got an empty list"
17461390 ne (x:xs) = pure (x :| xs)
17471391 {-# INLINE liftParseJSON #-}
17481392
@@ -1763,7 +1407,7 @@ instance FromJSON Scientific where
17631407-------------------------------------------------------------------------------
17641408
17651409instance FromJSON1 DList.DList where
1766− liftParseJSON p _ = withArray "DList" $
1410+ liftParseJSON p _ = withArray "DList a" $
17671411 fmap DList.fromList .
17681412 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
17691413 {-# INLINE liftParseJSON #-}
@@ -1840,7 +1484,7 @@ instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Sum f g) where
18401484 inr = "InR"
18411485
18421486 liftParseJSON _ _ _ = fail $
1843− "parsing Sum failed, expected an object with a single property " ++
1487+ "expected an object with a single property " ++
18441488 "where the property key should be either " ++
18451489 "\"InL\" or \"InR\""
18461490 {-# INLINE liftParseJSON #-}
@@ -1854,7 +1498,7 @@ instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Sum f g a) where
18541498-------------------------------------------------------------------------------
18551499
18561500instance FromJSON1 Seq.Seq where
1857− liftParseJSON p _ = withArray "Seq" $
1501+ liftParseJSON p _ = withArray "Seq a" $
18581502 fmap Seq.fromList .
18591503 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
18601504 {-# INLINE liftParseJSON #-}
@@ -1888,13 +1532,13 @@ instance FromJSON a => FromJSON (IntMap.IntMap a) where
18881532
18891533instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
18901534 liftParseJSON p _ = case fromJSONKey of
1891− FromJSONKeyCoerce _ -> withObject "Map" $
1535+ FromJSONKeyCoerce _-> withObject "Map k v" $
18921536 fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1893− FromJSONKeyText f -> withObject "Map" $
1537+ FromJSONKeyText f -> withObject "Map k v" $
18941538 fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1895− FromJSONKeyTextParser f -> withObject "Map" $
1539+ FromJSONKeyTextParser f -> withObject "Map k v" $
18961540 H.foldrWithKey (\k v m -> M.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure M.empty)
1897− FromJSONKeyValue f -> withArray "Map" $ \arr ->
1541+ FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
18981542 fmap M.fromList . Tr.sequence .
18991543 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
19001544 {-# INLINE liftParseJSON #-}
@@ -1922,18 +1566,18 @@ instance (FromJSON v) => FromJSON (Tree.Tree v) where
19221566
19231567instance FromJSON UUID.UUID where
19241568 parseJSON = withText "UUID" $
1925− maybe (fail "invalid UUID") pure . UUID.fromText
1569+ maybe (fail "Invalid UUID") pure . UUID.fromText
19261570
19271571instance FromJSONKey UUID.UUID where
19281572 fromJSONKey = FromJSONKeyTextParser $
1929− maybe (fail "invalid UUID") pure . UUID.fromText
1573+ maybe (fail "Invalid UUID") pure . UUID.fromText
19301574
19311575-------------------------------------------------------------------------------
19321576-- vector
19331577-------------------------------------------------------------------------------
19341578
19351579instance FromJSON1 Vector where
1936− liftParseJSON p _ = withArray "Vector" $
1580+ liftParseJSON p _ = withArray "Vector a" $
19371581 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
19381582 {-# INLINE liftParseJSON #-}
19391583
@@ -1947,14 +1591,14 @@ vectorParseJSON s = withArray s $ fmap V.convert . V.mapM (uncurry $ parseIndexe
19471591{-# INLINE vectorParseJSON #-}
19481592
19491593instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1950− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector"
1594+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
19511595
19521596instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1953− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector"
1597+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
19541598 {-# INLINE parseJSON #-}
19551599
19561600instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
1957− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector"
1601+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
19581602 {-# INLINE parseJSON #-}
19591603
19601604-------------------------------------------------------------------------------
@@ -1968,13 +1612,13 @@ instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
19681612
19691613instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
19701614 liftParseJSON p _ = case fromJSONKey of
1971− FromJSONKeyCoerce _ -> withObject "HashMap ~Text" $
1615+ FromJSONKeyCoerce _ -> withObject "HashMap ~Text v" $
19721616 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1973− FromJSONKeyText f -> withObject "HashMap" $
1617+ FromJSONKeyText f -> withObject "HashMap k v" $
19741618 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1975− FromJSONKeyTextParser f -> withObject "HashMap" $
1619+ FromJSONKeyTextParser f -> withObject "HashMap k v" $
19761620 H.foldrWithKey (\k v m -> H.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure H.empty)
1977− FromJSONKeyValue f -> withArray "Map" $ \arr ->
1621+ FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
19781622 fmap H.fromList . Tr.sequence .
19791623 zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
19801624 where
@@ -1997,33 +1641,11 @@ instance FromJSON DotNetTime where
19971641 parseJSON = withText "DotNetTime" $ \t ->
19981642 let (s,m) = T.splitAt (T.length t - 5) t
19991643 t' = T.concat [s,".",m]
2000− in case parseTimeM True defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
1644+ in case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
20011645 Just d -> pure (DotNetTime d)
20021646 _ -> fail "could not parse .NET time"
20031647 {-# INLINE parseJSON #-}
20041648
2005−-------------------------------------------------------------------------------
2006−-- primitive
2007−-------------------------------------------------------------------------------
2008−
2009−instance FromJSON a => FromJSON (PM.Array a) where
2010− -- note: we could do better than this if vector exposed the data
2011− -- constructor in Data.Vector.
2012− parseJSON = fmap Exts.fromList . parseJSON
2013−
2014−instance FromJSON a => FromJSON (PM.SmallArray a) where
2015− parseJSON = fmap Exts.fromList . parseJSON
2016−
2017−#if MIN_VERSION_primitive(0,6,4)
2018−instance (PM.Prim a,FromJSON a) => FromJSON (PM.PrimArray a) where
2019− parseJSON = fmap Exts.fromList . parseJSON
2020−
2021−#if !MIN_VERSION_primitive(0,7,0)
2022−instance (PM.PrimUnlifted a,FromJSON a) => FromJSON (PM.UnliftedArray a) where
2023− parseJSON = fmap Exts.fromList . parseJSON
2024−#endif
2025−#endif
2026−
20271649-------------------------------------------------------------------------------
20281650-- time
20291651-------------------------------------------------------------------------------
@@ -2074,57 +1696,14 @@ instance FromJSONKey UTCTime where
20741696 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
20751697
20761698
2077−-- | This instance includes a bounds check to prevent maliciously
2078−-- large inputs to fill up the memory of the target system. You can
2079−-- newtype 'Scientific' and provide your own instance using
2080−-- 'withScientific' if you want to allow larger inputs.
1699+-- | /WARNING:/ Only parse lengths of time from trusted input
1700+-- since an attacker could easily fill up the memory of the target
1701+-- system by specifying a scientific number with a big exponent like
1702+-- @1e1000000000@.
20811703instance FromJSON NominalDiffTime where
2082− parseJSON = withBoundedScientific "NominalDiffTime" $ pure . realToFrac
2083− {-# INLINE parseJSON #-}
2084−
2085−
2086−-- | This instance includes a bounds check to prevent maliciously
2087−-- large inputs to fill up the memory of the target system. You can
2088−-- newtype 'Scientific' and provide your own instance using
2089−-- 'withScientific' if you want to allow larger inputs.
2090−instance FromJSON DiffTime where
2091− parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
1704+ parseJSON = withScientific "NominalDiffTime" $ pure . realToFrac
20921705 {-# INLINE parseJSON #-}
20931706
2094−instance FromJSON SystemTime where
2095− parseJSON v = prependContext "SystemTime" $ do
2096− n <- parseJSON v
2097− let n' = floor (n * fromInteger (resolution n) :: Nano)
2098− let (secs, nano) = n' `divMod` resolution n
2099− return (MkSystemTime (fromInteger secs) (fromInteger nano))
2100−
2101−instance FromJSON CalendarDiffTime where
2102− parseJSON = withObject "CalendarDiffTime" $ \obj -> CalendarDiffTime
2103− <$> obj .: "months"
2104− <*> obj .: "time"
2105−
2106−instance FromJSON CalendarDiffDays where
2107− parseJSON = withObject "CalendarDiffDays" $ \obj -> CalendarDiffDays
2108− <$> obj .: "months"
2109− <*> obj .: "days"
2110−
2111−instance FromJSON DayOfWeek where
2112− parseJSON = withText "DaysOfWeek" parseDayOfWeek
2113−
2114−parseDayOfWeek :: T.Text -> Parser DayOfWeek
2115−parseDayOfWeek t = case T.toLower t of
2116− "monday" -> return Monday
2117− "tuesday" -> return Tuesday
2118− "wednesday" -> return Wednesday
2119− "thursday" -> return Thursday
2120− "friday" -> return Friday
2121− "saturday" -> return Saturday
2122− "sunday" -> return Sunday
2123− _ -> fail "Invalid week day"
2124−
2125−instance FromJSONKey DayOfWeek where
2126− fromJSONKey = FromJSONKeyTextParser parseDayOfWeek
2127−
21281707-------------------------------------------------------------------------------
21291708-- base Monoid/Semigroup
21301709-------------------------------------------------------------------------------
@@ -2245,15 +1824,14 @@ instance FromJSON a => FromJSON (Semigroup.Option a) where
22451824
22461825instance FromJSON1 Proxy where
22471826 {-# INLINE liftParseJSON #-}
2248− liftParseJSON _ _ = fromNull "Proxy" Proxy
1827+ liftParseJSON _ _ Null = pure Proxy
1828+ liftParseJSON _ _ v = typeMismatch "Proxy" v
22491829
22501830instance FromJSON (Proxy a) where
22511831 {-# INLINE parseJSON #-}
2252− parseJSON = fromNull "Proxy" Proxy
1832+ parseJSON Null = pure Proxy
1833+ parseJSON v = typeMismatch "Proxy" v
22531834
2254−fromNull :: String -> a -> Value -> Parser a
2255−fromNull _ a Null = pure a
2256−fromNull c _ v = prependContext c (typeMismatch "Null" v)
22571835
22581836instance FromJSON2 Tagged where
22591837 liftParseJSON2 _ _ p _ = fmap Tagged . p
@@ -2280,7 +1858,10 @@ instance (FromJSON a, FromJSON b, FromJSON c) => FromJSONKey (a,b,c)
22801858instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSONKey (a,b,c,d)
22811859
22821860instance FromJSONKey Char where
2283− fromJSONKey = FromJSONKeyTextParser parseChar
1861+ fromJSONKey = FromJSONKeyTextParser $ \t ->
1862+ if T.length t == 1
1863+ then return (T.index t 0)
1864+ else typeMismatch "Expected Char but String didn't contain exactly one character" (String t)
22841865 fromJSONKeyList = FromJSONKeyText T.unpack
22851866
22861867instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
Data/Aeson/Types/Generic.hs+8−12
@@ -1,11 +1,10 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DeriveFunctor #-}
43{-# LANGUAGE EmptyDataDecls #-}
54{-# LANGUAGE FlexibleInstances #-}
65{-# LANGUAGE FunctionalDependencies #-}
76{-# LANGUAGE KindSignatures #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE ScopedTypeVariables #-}
109{-# LANGUAGE TypeOperators #-}
1110{-# LANGUAGE UndecidableInstances #-}
@@ -26,7 +25,7 @@
2625
2726module Data.Aeson.Types.Generic
2827 (
29− IsRecord
28+ IsRecord(..)
3029 , AllNullary
3130 , Tagged2(..)
3231 , True
@@ -35,9 +34,9 @@ module Data.Aeson.Types.Generic
3534 , Zero
3635 , One
3736 , ProductSize(..)
38− , (:*)(..)
3937 ) where
4038
39+import Prelude ()
4140import Prelude.Compat
4241
4342import GHC.Generics
@@ -45,8 +44,12 @@ import GHC.Generics
4544--------------------------------------------------------------------------------
4645
4746class IsRecord (f :: * -> *) isRecord | f -> isRecord
47+ where
48+ isUnary :: f a -> Bool
49+ isUnary = const True
4850
4951instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
52+ where isUnary = const False
5053#if MIN_VERSION_base(4,9,0)
5154instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
5255#else
@@ -58,6 +61,7 @@ instance IsRecord Par1 True
5861instance IsRecord (Rec1 f) True
5962instance IsRecord (f :.: g) True
6063instance IsRecord U1 False
64+ where isUnary = const False
6165
6266--------------------------------------------------------------------------------
6367
@@ -76,7 +80,6 @@ instance AllNullary (Rec1 f) False
7680instance AllNullary U1 True
7781
7882newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
79− deriving Functor
8083
8184--------------------------------------------------------------------------------
8285
@@ -109,10 +112,3 @@ instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
109112
110113instance ProductSize (S1 s a) where
111114 productSize = Tagged2 1
112−
113−--------------------------------------------------------------------------------
114−
115−-- | Simple extensible tuple type to simplify passing around many parameters.
116−data a :* b = a :* b
117−
118−infixr 1 :*
Data/Aeson/Types/Internal.hs+10−121
@@ -2,7 +2,6 @@
22{-# LANGUAGE DeriveDataTypeable #-}
33{-# LANGUAGE DeriveGeneric #-}
44{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE NoImplicitPrelude #-}
65{-# LANGUAGE Rank2Types #-}
76#if __GLASGOW_HASKELL__ >= 800
87-- a) THQ works on cross-compilers and unregisterised GHCs
@@ -45,12 +44,9 @@ module Data.Aeson.Types.Internal
4544 , parseEither
4645 , parseMaybe
4746 , modifyFailure
48− , prependFailure
4947 , parserThrowError
5048 , parserCatchError
5149 , formatError
52− , formatPath
53− , formatRelativePath
5450 , (<?>)
5551 -- * Constructors and accessors
5652 , object
@@ -64,14 +60,11 @@ module Data.Aeson.Types.Internal
6460 , sumEncoding
6561 , unwrapUnaryRecords
6662 , tagSingleConstructors
67− , rejectUnknownFields
6863 )
6964
7065 , SumEncoding(..)
71− , JSONKeyOptions(keyModifier)
7266 , defaultOptions
7367 , defaultTaggedObject
74− , defaultJSONKeyOptions
7568
7669 -- * Used for changing CamelCase names into something else.
7770 , camelTo
@@ -81,6 +74,7 @@ module Data.Aeson.Types.Internal
8174 , DotNetTime(..)
8275 ) where
8376
77+import Prelude ()
8478import Prelude.Compat
8579
8680import Control.Applicative (Alternative(..))
@@ -102,7 +96,6 @@ import Data.Time.Format (FormatTime)
10296import Data.Typeable (Typeable)
10397import Data.Vector (Vector)
10498import GHC.Generics (Generic)
105−import qualified Control.Monad as Monad
10699import qualified Control.Monad.Fail as Fail
107100import qualified Data.HashMap.Strict as H
108101import qualified Data.Scientific as S
@@ -121,7 +114,7 @@ data JSONPathElement = Key Text
121114 | Index {-# UNPACK #-} !Int
122115 -- ^ JSON path element of an index into an
123116 -- array, \"array[index]\".
124− deriving (Eq, Show, Typeable, Ord)
117+ deriving (Eq, Show, Typeable)
125118type JSONPath = [JSONPathElement]
126119
127120-- | The internal result of running a 'Parser'.
@@ -156,7 +149,7 @@ instance Functor Result where
156149 fmap _ (Error err) = Error err
157150 {-# INLINE fmap #-}
158151
159−instance Monad.Monad IResult where
152+instance Monad IResult where
160153 return = pure
161154 {-# INLINE return #-}
162155
@@ -164,16 +157,14 @@ instance Monad.Monad IResult where
164157 IError path err >>= _ = IError path err
165158 {-# INLINE (>>=) #-}
166159
167−#if !(MIN_VERSION_base(4,13,0))
168160 fail = Fail.fail
169161 {-# INLINE fail #-}
170−#endif
171162
172163instance Fail.MonadFail IResult where
173164 fail err = IError [] err
174165 {-# INLINE fail #-}
175166
176−instance Monad.Monad Result where
167+instance Monad Result where
177168 return = pure
178169 {-# INLINE return #-}
179170
@@ -181,10 +172,8 @@ instance Monad.Monad Result where
181172 Error err >>= _ = Error err
182173 {-# INLINE (>>=) #-}
183174
184−#if !(MIN_VERSION_base(4,13,0))
185175 fail = Fail.fail
186176 {-# INLINE fail #-}
187−#endif
188177
189178instance Fail.MonadFail Result where
190179 fail err = Error err
@@ -281,9 +270,7 @@ type Failure f r = JSONPath -> String -> f r
281270-- | Success continuation.
282271type Success a f r = a -> f r
283272
284−-- | A JSON parser. N.B. This might not fit your usual understanding of
285−-- "parser". Instead you might like to think of 'Parser' as a "parse result",
286−-- i.e. a parser to which the input has already been applied.
273+-- | A JSON parser.
287274newtype Parser a = Parser {
288275 runParser :: forall f r.
289276 JSONPath
@@ -292,17 +279,14 @@ newtype Parser a = Parser {
292279 -> f r
293280 }
294281
295−instance Monad.Monad Parser where
282+instance Monad Parser where
296283 m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
297284 in runParser m path kf ks'
298285 {-# INLINE (>>=) #-}
299286 return = pure
300287 {-# INLINE return #-}
301−
302−#if !(MIN_VERSION_base(4,13,0))
303288 fail = Fail.fail
304289 {-# INLINE fail #-}
305−#endif
306290
307291instance Fail.MonadFail Parser where
308292 fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
@@ -345,7 +329,8 @@ instance Monoid (Parser a) where
345329apP :: Parser (a -> b) -> Parser a -> Parser b
346330apP d e = do
347331 b <- d
348− b <$> e
332+ a <- e
333+ return (b a)
349334{-# INLINE apP #-}
350335
351336-- | A JSON \"object\" (key\/value map).
@@ -461,17 +446,7 @@ parseEither m v = runParser (m v) [] onError Right
461446-- | Annotate an error message with a
462447-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
463448formatError :: JSONPath -> String -> String
464−formatError path msg = "Error in " ++ formatPath path ++ ": " ++ msg
465−
466−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String',
467−-- representing the root object as @$@.
468−formatPath :: JSONPath -> String
469−formatPath path = "$" ++ formatRelativePath path
470−
471−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
472−-- which represents the path relative to some root object.
473−formatRelativePath :: JSONPath -> String
474−formatRelativePath path = format "" path
449+formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
475450 where
476451 format :: String -> JSONPath -> String
477452 format pfx [] = pfx
@@ -537,15 +512,6 @@ modifyFailure :: (String -> String) -> Parser a -> Parser a
537512modifyFailure f (Parser p) = Parser $ \path kf ks ->
538513 p path (\p' m -> kf p' (f m)) ks
539514
540−-- | If the inner 'Parser' failed, prepend the given string to the failure
541−-- message.
542−--
543−-- @
544−-- 'prependFailure' s = 'modifyFailure' (s '++')
545−-- @
546−prependFailure :: String -> Parser a -> Parser a
547−prependFailure = modifyFailure . (++)
548−
549515-- | Throw a parser error with an additional path.
550516--
551517-- @since 1.2.1.0
@@ -584,39 +550,6 @@ data Options = Options
584550 -- ^ If 'True' record fields with a 'Nothing' value will be
585551 -- omitted from the resulting object. If 'False' the resulting
586552 -- object will include those fields mapping to @null@.
587− --
588− -- === Note
589− --
590− -- 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
593− -- will not be omitted from the JSON object, unless the field is
594− -- specialized upfront in the instance.
595− --
596− -- ==== __Example__
597− --
598− -- The generic instance for the following type @Fruit@ depends on whether
599− -- the instance head is @Fruit a@ or @Fruit (Maybe a)@.
600− --
601− -- @
602− -- data Fruit a =
603− -- { apples :: a -- A field whose type is a type variable.
604− -- , oranges :: 'Maybe' Int
605− -- }
606− --
607− -- options :: 'Options'
608− -- options = 'defaultOptions' { 'omitNothingFields' = 'True' }
609− --
610− -- -- apples required, oranges optional
611− -- -- 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
614− --
615− -- -- apples optional, oranges optional
616− -- -- 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
619− -- @
620553 , sumEncoding :: SumEncoding
621554 -- ^ Specifies how to encode constructors of a sum datatype.
622555 , unwrapUnaryRecords :: Bool
@@ -625,14 +558,10 @@ data Options = Options
625558 , tagSingleConstructors :: Bool
626559 -- ^ Encode types with a single constructor as sums,
627560 -- so that `allNullaryToStringTag` and `sumEncoding` apply.
628− , rejectUnknownFields :: Bool
629− -- ^ Applies only to 'Data.Aeson.FromJSON' instances. If a field appears in
630− -- the parsed object map, but does not appear in the target object, parsing
631− -- will fail, with an error message indicating which fields were unknown.
632561 }
633562
634563instance Show Options where
635− show (Options f c a o s u t r) =
564+ show (Options f c a o s u t) =
636565 "Options {"
637566 ++ intercalate ", "
638567 [ "fieldLabelModifier =~ " ++ show (f "exampleField")
@@ -642,7 +571,6 @@ instance Show Options where
642571 , "sumEncoding = " ++ show s
643572 , "unwrapUnaryRecords = " ++ show u
644573 , "tagSingleConstructors = " ++ show t
645− , "rejectUnknownFields = " ++ show r
646574 ]
647575 ++ "}"
648576
@@ -686,33 +614,6 @@ data SumEncoding =
686614 -- contents of the constructor.
687615 deriving (Eq, Show)
688616
689−-- | Options for encoding keys with 'Data.Aeson.Types.genericFromJSONKey' and
690−-- 'Data.Aeson.Types.genericToJSONKey'.
691−data JSONKeyOptions = JSONKeyOptions
692− { keyModifier :: String -> String
693− -- ^ Function applied to keys. Its result is what goes into the encoded
694− -- 'Value'.
695− --
696− -- === __Example__
697− --
698− -- The following instances encode the constructor @Bar@ to lower-case keys
699− -- @\"bar\"@.
700− --
701− -- @
702− -- data Foo = Bar
703− -- deriving 'Generic'
704− --
705− -- opts :: 'JSONKeyOptions'
706− -- opts = 'defaultJSONKeyOptions' { 'keyModifier' = 'toLower' }
707− --
708− -- instance 'ToJSONKey' Foo where
709− -- 'toJSONKey' = 'genericToJSONKey' opts
710− --
711− -- instance 'FromJSONKey' Foo where
712− -- 'fromJSONKey' = 'genericFromJSONKey' opts
713− -- @
714− }
715−
716617-- | Default encoding 'Options':
717618--
718619-- @
@@ -724,7 +625,6 @@ data JSONKeyOptions = JSONKeyOptions
724625-- , 'sumEncoding' = 'defaultTaggedObject'
725626-- , 'unwrapUnaryRecords' = False
726627-- , 'tagSingleConstructors' = False
727−-- , 'rejectUnknownFields' = False
728628-- }
729629-- @
730630defaultOptions :: Options
@@ -736,7 +636,6 @@ defaultOptions = Options
736636 , sumEncoding = defaultTaggedObject
737637 , unwrapUnaryRecords = False
738638 , tagSingleConstructors = False
739− , rejectUnknownFields = False
740639 }
741640
742641-- | Default 'TaggedObject' 'SumEncoding' options:
@@ -753,16 +652,6 @@ defaultTaggedObject = TaggedObject
753652 , contentsFieldName = "contents"
754653 }
755654
756−-- | Default 'JSONKeyOptions':
757−--
758−-- @
759−-- defaultJSONKeyOptions = 'JSONKeyOptions'
760−-- { 'keyModifier' = 'id'
761−-- }
762−-- @
763−defaultJSONKeyOptions :: JSONKeyOptions
764−defaultJSONKeyOptions = JSONKeyOptions id
765−
766655-- | Converts from CamelCase to another lower case, interspersing
767656-- the character between all capital letters and their previous
768657-- entries, except those capital letters that appear together,
Data/Aeson/Types/ToJSON.hs+83−248
@@ -5,15 +5,20 @@
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE FunctionalDependencies #-}
77{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE NoImplicitPrelude #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE NamedFieldPuns #-}
910{-# LANGUAGE OverloadedStrings #-}
1011{-# LANGUAGE PatternGuards #-}
11−{-# LANGUAGE PolyKinds #-}
1212{-# LANGUAGE RecordWildCards #-}
1313{-# LANGUAGE ScopedTypeVariables #-}
1414{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE TypeSynonymInstances #-}
1516{-# LANGUAGE UndecidableInstances #-}
1617
18+#if __GLASGOW_HASKELL__ >= 706
19+{-# LANGUAGE PolyKinds #-}
20+#endif
21+
1722#include "overlapping-compat.h"
1823#include "incoherent-compat.h"
1924
@@ -43,20 +48,15 @@ module Data.Aeson.Types.ToJSON
4348 , ToJSONKeyFunction(..)
4449 , toJSONKeyText
4550 , contramapToJSONKeyFunction
46−
47− , GToJSONKey()
48− , genericToJSONKey
49−
5051 -- * Object key-value pairs
5152 , KeyValue(..)
52− , KeyValuePair(..)
53− , FromPairs(..)
5453 -- * Functions needed for documentation
5554 -- * Encoding functions
5655 , listEncoding
5756 , listValue
5857 ) where
5958
59+import Prelude ()
6060import Prelude.Compat
6161
6262import Control.Applicative (Const(..))
@@ -64,42 +64,38 @@ import Control.Monad.ST (ST)
6464import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
6565import Data.Aeson.Encoding.Internal ((>*<))
6666import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
67−import Data.Aeson.Types.Generic (AllNullary, False, IsRecord, One, ProductSize, Tagged2(..), True, Zero, productSize)
67+import Data.Aeson.Types.Generic (AllNullary, False, IsRecord(..), One, ProductSize, Tagged2(..), True, Zero, productSize)
6868import Data.Aeson.Types.Internal
6969import Data.Attoparsec.Number (Number(..))
7070import Data.Bits (unsafeShiftR)
7171import Data.DList (DList)
72−import Data.Fixed (Fixed, HasResolution, Nano)
72+import Data.Fixed (Fixed, HasResolution)
7373import Data.Foldable (toList)
7474import Data.Functor.Compose (Compose(..))
75−import Data.Functor.Contravariant (Contravariant (..))
7675import Data.Functor.Identity (Identity(..))
7776import Data.Functor.Product (Product(..))
7877import Data.Functor.Sum (Sum(..))
7978import Data.Int (Int16, Int32, Int64, Int8)
8079import Data.List (intersperse)
8180import Data.List.NonEmpty (NonEmpty(..))
82−import Data.Semigroup ((<>))
81+import Data.Monoid ((<>))
8382import Data.Proxy (Proxy(..))
8483import Data.Ratio (Ratio, denominator, numerator)
8584import Data.Scientific (Scientific)
8685import Data.Tagged (Tagged(..))
8786import Data.Text (Text, pack)
88−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
89−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
90−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
91−import Data.Time.Clock.System.Compat (SystemTime (..))
92−import Data.Time.Format.Compat (FormatTime, formatTime, defaultTimeLocale)
87+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
88+import Data.Time.Format (FormatTime, formatTime)
89+import Data.Time.Locale.Compat (defaultTimeLocale)
9390import Data.Vector (Vector)
9491import Data.Version (Version, showVersion)
95−import Data.Void (Void, absurd)
9692import Data.Word (Word16, Word32, Word64, Word8)
9793import Foreign.Storable (Storable)
98−import Foreign.C.Types (CTime (..))
9994import GHC.Generics
10095import Numeric.Natural (Natural)
10196import qualified Data.Aeson.Encoding as E
10297import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
98+import qualified Data.ByteString as S
10399import qualified Data.ByteString.Lazy as L
104100import qualified Data.DList as DList
105101import qualified Data.HashMap.Strict as H
@@ -128,18 +124,16 @@ import qualified Data.Vector.Unboxed as VU
128124import qualified Data.Aeson.Encoding.Builder as EB
129125import qualified Data.ByteString.Builder as B
130126
131−import qualified GHC.Exts as Exts
132−import qualified Data.Primitive.Array as PM
133−import qualified Data.Primitive.SmallArray as PM
134−import qualified Data.Primitive.Types as PM
135−
136−#if MIN_VERSION_primitive(0,6,4)
137−#if !MIN_VERSION_primitive(0,7,0)
138−import qualified Data.Primitive.UnliftedArray as PM
139−#endif
140−import qualified Data.Primitive.PrimArray as PM
127+#if !(MIN_VERSION_bytestring(0,10,0))
128+import Foreign.ForeignPtr (withForeignPtr)
129+import Foreign.Marshal.Utils (copyBytes)
130+import Foreign.Ptr (plusPtr)
131+import qualified Data.ByteString.Internal as S
132+import qualified Data.ByteString.Lazy.Internal as L
141133#endif
142134
135+{-# ANN module ("HLint: ignore Reduce duplication"::String) #-}
136+
143137toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
144138toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
145139{-# INLINE toJSONPair #-}
@@ -267,7 +261,7 @@ genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1
267261-- @
268262--
269263-- Previous versions of this library only had the 'toJSON' method. Adding
270−-- 'toEncoding' had two reasons:
264+-- 'toEncoding' had to reasons:
271265--
272266-- 1. toEncoding is more efficient for the common case that the output of
273267-- 'toJSON' is directly serialized to a @ByteString@.
@@ -338,13 +332,6 @@ instance KeyValue Pair where
338332 name .= value = (name, toJSON value)
339333 {-# INLINE (.=) #-}
340334
341−-- | Constructs a singleton 'H.HashMap'. For calling functions that
342−-- demand an 'Object' for constructing objects. To be used in
343−-- conjunction with 'mconcat'. Prefer to use 'object' where possible.
344−instance KeyValue Object where
345− name .= value = H.singleton name (toJSON value)
346− {-# INLINE (.=) #-}
347−
348335-------------------------------------------------------------------------------
349336-- Classes and types for map keys
350337-------------------------------------------------------------------------------
@@ -402,7 +389,7 @@ instance KeyValue Object where
402389-- newtype wrapper around 'Text'. The recommended approach is to use
403390-- generalized newtype deriving:
404391--
405−-- > newtype RecordId = RecordId { getRecordId :: Text }
392+-- > newtype RecordId = RecordId { getRecordId :: Text}
406393-- > deriving (Eq,Ord,ToJSONKey)
407394--
408395-- Then we may write:
@@ -417,18 +404,8 @@ instance KeyValue Object where
417404--
418405-- It is possible to get the 'ToJSONKey' instance for free as we did
419406-- with 'Foo'. However, in this case, we have a natural way to go to
420−-- and from 'Text' that does not require any escape sequences. So
421−-- 'ToJSONKeyText' can be used instead of 'ToJSONKeyValue' to encode maps
422−-- as objects instead of arrays of pairs. This instance may be
423−-- implemented using generics as follows:
424−--
425−-- @
426−-- instance 'ToJSONKey' Color where
427−-- 'toJSONKey' = 'genericToJSONKey' 'defaultJSONKeyOptions'
428−-- @
429−--
430−-- === __Low-level implementations__
431−--
407+-- and from 'Text' that does not require any escape sequences. So, in
408+-- this example, 'ToJSONKeyText' will be used instead of 'ToJSONKeyValue'.
432409-- The 'Show' instance can be used to help write 'ToJSONKey':
433410--
434411-- > instance ToJSONKey Color where
@@ -503,42 +480,16 @@ toJSONKeyTextEnc e = ToJSONKeyText tot e
503480 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
504481 tot = T.dropAround (== '"')
505482 . T.decodeLatin1
506− . L.toStrict
483+ . lazyToStrictByteString
507484 . E.encodingToLazyByteString
508485 . e
509486
510−instance Contravariant ToJSONKeyFunction where
511− contramap = contramapToJSONKeyFunction
512−
513487-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
514488contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
515489contramapToJSONKeyFunction h x = case x of
516490 ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
517491 ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
518492
519−-- 'toJSONKey' for 'Generic' types.
520−-- Deriving is supported for enumeration types, i.e. the sums of nullary
521−-- constructors. The names of constructors will be used as keys for JSON
522−-- objects.
523−--
524−-- See also 'genericFromJSONKey'.
525−--
526−-- === __Example__
527−--
528−-- @
529−-- data Color = Red | Green | Blue
530−-- deriving 'Generic'
531−--
532−-- instance 'ToJSONKey' Color where
533−-- 'toJSONKey' = 'genericToJSONKey' 'defaultJSONKeyOptions'
534−-- @
535−genericToJSONKey :: (Generic a, GToJSONKey (Rep a))
536− => JSONKeyOptions -> ToJSONKeyFunction a
537−genericToJSONKey opts = toJSONKeyText (pack . keyModifier opts . getConName . from)
538−
539−class GetConName f => GToJSONKey f
540−instance GetConName f => GToJSONKey f
541−
542493-------------------------------------------------------------------------------
543494-- Lifings of FromJSON and ToJSON to unary and binary type constructors
544495-------------------------------------------------------------------------------
@@ -703,13 +654,11 @@ instance (ToJSON a) => ToJSON [a] where
703654instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where
704655 -- Meta-information, which is not handled elsewhere, is ignored:
705656 gToJSON opts targs = gToJSON opts targs . unM1
706− {-# INLINE gToJSON #-}
707657
708658instance GToJSON enc One Par1 where
709659 -- Direct occurrences of the last type parameter are encoded with the
710660 -- function passed in as an argument:
711661 gToJSON _opts (To1Args tj _) = tj . unPar1
712− {-# INLINE gToJSON #-}
713662
714663instance ( ConsToJSON enc arity a
715664 , AllNullary (C1 c a) allNullary
@@ -722,13 +671,11 @@ instance ( ConsToJSON enc arity a
722671 . sumToJSON opts targs
723672 . unM1
724673 | otherwise = consToJSON opts targs . unM1 . unM1
725− {-# INLINE gToJSON #-}
726674
727675instance (ConsToJSON enc arity a) => GToJSON enc arity (C1 c a) where
728676 -- Constructors need to be encoded differently depending on whether they're
729677 -- a record or not. This distinction is made by 'consToJSON':
730678 gToJSON opts targs = consToJSON opts targs . unM1
731− {-# INLINE gToJSON #-}
732679
733680instance ( AllNullary (a :+: b) allNullary
734681 , SumToJSON enc arity (a :+: b) allNullary
@@ -739,7 +686,6 @@ instance ( AllNullary (a :+: b) allNullary
739686 -- strings. This distinction is made by 'sumToJSON':
740687 gToJSON opts targs = (unTagged :: Tagged allNullary enc -> enc)
741688 . sumToJSON opts targs
742− {-# INLINE gToJSON #-}
743689
744690--------------------------------------------------------------------------------
745691-- Generic toJSON
@@ -748,27 +694,18 @@ instance ( AllNullary (a :+: b) allNullary
748694-- possible but makes error messages a bit harder to understand for missing
749695-- instances.
750696
751−instance GToJSON Value arity V1 where
752− -- Empty values do not exist, which makes the job of formatting them
753− -- rather easy:
754− gToJSON _ _ x = x `seq` error "case: V1"
755− {-# INLINE gToJSON #-}
756−
757697instance ToJSON a => GToJSON Value arity (K1 i a) where
758698 -- Constant values are encoded using their ToJSON instance:
759699 gToJSON _opts _ = toJSON . unK1
760− {-# INLINE gToJSON #-}
761700
762701instance ToJSON1 f => GToJSON Value One (Rec1 f) where
763702 -- Recursive occurrences of the last type parameter are encoded using their
764703 -- ToJSON1 instance:
765704 gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
766− {-# INLINE gToJSON #-}
767705
768706instance GToJSON Value arity U1 where
769707 -- Empty constructors are encoded to an empty array:
770708 gToJSON _opts _ _ = emptyArray
771− {-# INLINE gToJSON #-}
772709
773710instance ( WriteProduct arity a, WriteProduct arity b
774711 , ProductSize a, ProductSize b
@@ -785,7 +722,6 @@ instance ( WriteProduct arity a, WriteProduct arity b
785722 where
786723 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
787724 productSize
788− {-# INLINE gToJSON #-}
789725
790726instance ( ToJSON1 f
791727 , GToJSON Value One g
@@ -797,7 +733,6 @@ instance ( ToJSON1 f
797733 gToJSON opts targs =
798734 let gtj = gToJSON opts targs in
799735 liftToJSON gtj (listValue gtj) . unComp1
800− {-# INLINE gToJSON #-}
801736
802737--------------------------------------------------------------------------------
803738-- Generic toEncoding
@@ -805,18 +740,15 @@ instance ( ToJSON1 f
805740instance ToJSON a => GToJSON Encoding arity (K1 i a) where
806741 -- Constant values are encoded using their ToJSON instance:
807742 gToJSON _opts _ = toEncoding . unK1
808− {-# INLINE gToJSON #-}
809743
810744instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where
811745 -- Recursive occurrences of the last type parameter are encoded using their
812746 -- ToEncoding1 instance:
813747 gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1
814− {-# INLINE gToJSON #-}
815748
816749instance GToJSON Encoding arity U1 where
817750 -- Empty constructors are encoded to an empty array:
818751 gToJSON _opts _ _ = E.emptyArray_
819− {-# INLINE gToJSON #-}
820752
821753instance ( EncodeProduct arity a
822754 , EncodeProduct arity b
@@ -826,7 +758,6 @@ instance ( EncodeProduct arity a
826758 -- the same size as the product and write the product's elements to it using
827759 -- 'encodeProduct':
828760 gToJSON opts targs p = E.list E.retagEncoding [encodeProduct opts targs p]
829− {-# INLINE gToJSON #-}
830761
831762instance ( ToJSON1 f
832763 , GToJSON Encoding One g
@@ -838,7 +769,6 @@ instance ( ToJSON1 f
838769 gToJSON opts targs =
839770 let gte = gToJSON opts targs in
840771 liftToEncoding gte (listEncoding gte) . unComp1
841− {-# INLINE gToJSON #-}
842772
843773--------------------------------------------------------------------------------
844774
@@ -922,14 +852,14 @@ instance ( IsRecord a isRecord
922852 , TaggedObject' enc pairs arity a isRecord
923853 , FromPairs enc pairs
924854 , FromString enc
925− , KeyValuePair enc pairs
855+ , GKeyValue enc pairs
926856 , Constructor c
927857 ) => TaggedObject enc arity (C1 c a)
928858 where
929859 taggedObject opts targs tagFieldName contentsFieldName =
930− fromPairs . mappend tag . contents
860+ fromPairs . (tag <>) . contents
931861 where
932− tag = tagFieldName `pair`
862+ tag = tagFieldName `gPair`
933863 (fromString (constructorTagModifier opts (conName (undefined :: t c a p)))
934864 :: enc)
935865 contents =
@@ -941,11 +871,11 @@ class TaggedObject' enc pairs arity f isRecord where
941871 -> String -> f a -> Tagged isRecord pairs
942872
943873instance ( GToJSON enc arity f
944− , KeyValuePair enc pairs
874+ , GKeyValue enc pairs
945875 ) => TaggedObject' enc pairs arity f False
946876 where
947877 taggedObject' opts targs contentsFieldName =
948− Tagged . (contentsFieldName `pair`) . gToJSON opts targs
878+ Tagged . (contentsFieldName `gPair`) . gToJSON opts targs
949879
950880instance OVERLAPPING_ Monoid pairs => TaggedObject' enc pairs arity U1 False where
951881 taggedObject' _ _ _ _ = Tagged mempty
@@ -968,9 +898,6 @@ instance (GetConName a, GetConName b) => GetConName (a :+: b) where
968898instance (Constructor c) => GetConName (C1 c a) where
969899 getConName = conName
970900
971−-- For genericToJSONKey
972−instance GetConName a => GetConName (D1 d a) where
973− getConName (M1 x) = getConName x
974901
975902--------------------------------------------------------------------------------
976903
@@ -1026,6 +953,7 @@ class ConsToJSON enc arity f where
1026953
1027954class ConsToJSON' enc arity f isRecord where
1028955 consToJSON' :: Options -> ToArgs enc arity a
956+ -> Bool -- ^ Are we a record with one field?
1029957 -> f a -> Tagged isRecord enc
1030958
1031959instance ( IsRecord f isRecord
@@ -1034,29 +962,23 @@ instance ( IsRecord f isRecord
1034962 where
1035963 consToJSON opts targs =
1036964 (unTagged :: Tagged isRecord enc -> enc)
1037− . consToJSON' opts targs
965+ . consToJSON' opts targs (isUnary (undefined :: f a))
1038966 {-# INLINE consToJSON #-}
1039967
1040−instance OVERLAPPING_
1041− ( RecordToPairs enc pairs arity (S1 s f)
1042− , FromPairs enc pairs
1043− , GToJSON enc arity f
1044− ) => ConsToJSON' enc arity (S1 s f) True
1045− where
1046− consToJSON' opts targs
1047− | unwrapUnaryRecords opts = Tagged . gToJSON opts targs
1048− | otherwise = Tagged . fromPairs . recordToPairs opts targs
1049− {-# INLINE consToJSON' #-}
1050−
1051968instance ( RecordToPairs enc pairs arity f
1052969 , FromPairs enc pairs
970+ , GToJSON enc arity f
1053971 ) => ConsToJSON' enc arity f True
1054972 where
1055− consToJSON' opts targs = Tagged . fromPairs . recordToPairs opts targs
973+ consToJSON' opts targs isUn =
974+ Tagged .
975+ case (isUn, unwrapUnaryRecords opts) of
976+ (True, True) -> gToJSON opts targs
977+ _ -> fromPairs . recordToPairs opts targs
1056978 {-# INLINE consToJSON' #-}
1057979
1058980instance GToJSON enc arity f => ConsToJSON' enc arity f False where
1059− consToJSON' opts targs = Tagged . gToJSON opts targs
981+ consToJSON' opts targs _ = Tagged . gToJSON opts targs
1060982 {-# INLINE consToJSON' #-}
1061983
1062984--------------------------------------------------------------------------------
@@ -1074,7 +996,7 @@ instance ( Monoid pairs
1074996 ) => RecordToPairs enc pairs arity (a :*: b)
1075997 where
1076998 recordToPairs opts (targs :: ToArgs enc arity p) (a :*: b) =
1077− pairsOf a `mappend` pairsOf b
999+ pairsOf a <> pairsOf b
10781000 where
10791001 pairsOf :: (RecordToPairs enc pairs arity f) => f p -> pairs
10801002 pairsOf = recordToPairs opts targs
@@ -1082,7 +1004,7 @@ instance ( Monoid pairs
10821004
10831005instance ( Selector s
10841006 , GToJSON enc arity a
1085− , KeyValuePair enc pairs
1007+ , GKeyValue enc pairs
10861008 ) => RecordToPairs enc pairs arity (S1 s a)
10871009 where
10881010 recordToPairs = fieldToPair
@@ -1091,7 +1013,7 @@ instance ( Selector s
10911013instance INCOHERENT_
10921014 ( Selector s
10931015 , GToJSON enc arity (K1 i (Maybe a))
1094− , KeyValuePair enc pairs
1016+ , GKeyValue enc pairs
10951017 , Monoid pairs
10961018 ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))
10971019 where
@@ -1100,28 +1022,15 @@ instance INCOHERENT_
11001022 recordToPairs opts targs m1 = fieldToPair opts targs m1
11011023 {-# INLINE recordToPairs #-}
11021024
1103−instance INCOHERENT_
1104− ( Selector s
1105− , GToJSON enc arity (K1 i (Maybe a))
1106− , KeyValuePair enc pairs
1107− , Monoid pairs
1108− ) => RecordToPairs enc pairs arity (S1 s (K1 i (Semigroup.Option a)))
1109− where
1110− recordToPairs opts targs = recordToPairs opts targs . unwrap
1111− where
1112− unwrap :: S1 s (K1 i (Semigroup.Option a)) p -> S1 s (K1 i (Maybe a)) p
1113− unwrap (M1 (K1 (Semigroup.Option a))) = M1 (K1 a)
1114− {-# INLINE recordToPairs #-}
1115−
11161025fieldToPair :: (Selector s
11171026 , GToJSON enc arity a
1118− , KeyValuePair enc pairs)
1027+ , GKeyValue enc pairs)
11191028 => Options -> ToArgs enc arity p
11201029 -> S1 s a p -> pairs
11211030fieldToPair opts targs m1 =
11221031 let key = fieldLabelModifier opts (selName m1)
11231032 value = gToJSON opts targs (unM1 m1)
1124− in key `pair` value
1033+ in key `gPair` value
11251034{-# INLINE fieldToPair #-}
11261035
11271036--------------------------------------------------------------------------------
@@ -1145,12 +1054,10 @@ instance ( WriteProduct arity a
11451054 lenL = len `unsafeShiftR` 1
11461055 lenR = len - lenL
11471056 ixR = ix + lenL
1148− {-# INLINE writeProduct #-}
11491057
11501058instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where
11511059 writeProduct opts targs mv ix _ =
11521060 VM.unsafeWrite mv ix . gToJSON opts targs
1153− {-# INLINE writeProduct #-}
11541061
11551062--------------------------------------------------------------------------------
11561063
@@ -1168,23 +1075,21 @@ instance ( EncodeProduct arity a
11681075 encodeProduct opts targs (a :*: b) =
11691076 encodeProduct opts targs a >*<
11701077 encodeProduct opts targs b
1171− {-# INLINE encodeProduct #-}
11721078
11731079instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where
11741080 encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a
1175− {-# INLINE encodeProduct #-}
11761081
11771082--------------------------------------------------------------------------------
11781083
11791084instance ( GToJSON enc arity a
11801085 , ConsToJSON enc arity a
11811086 , FromPairs enc pairs
1182− , KeyValuePair enc pairs
1087+ , GKeyValue enc pairs
11831088 , Constructor c
11841089 ) => SumToJSON' ObjectWithSingleField enc arity (C1 c a)
11851090 where
11861091 sumToJSON' opts targs =
1187− Tagged . fromPairs . (typ `pair`) . gToJSON opts targs
1092+ Tagged . fromPairs . (typ `gPair`) . gToJSON opts targs
11881093 where
11891094 typ = constructorTagModifier opts $
11901095 conName (undefined :: t c a p)
@@ -1276,13 +1181,6 @@ instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
12761181 toEncoding = toEncoding2
12771182 {-# INLINE toEncoding #-}
12781183
1279−instance ToJSON Void where
1280− toJSON = absurd
1281− {-# INLINE toJSON #-}
1282−
1283− toEncoding = absurd
1284− {-# INLINE toEncoding #-}
1285−
12861184
12871185instance ToJSON Bool where
12881186 toJSON = Bool
@@ -1468,6 +1366,7 @@ instance ToJSONKey Int64 where
14681366 toJSONKey = toJSONKeyTextEnc E.int64Text
14691367 {-# INLINE toJSONKey #-}
14701368
1369+
14711370instance ToJSON Word where
14721371 toJSON = Number . fromIntegral
14731372 {-# INLINE toJSON #-}
@@ -1527,12 +1426,6 @@ instance ToJSONKey Word64 where
15271426 toJSONKey = toJSONKeyTextEnc E.word64Text
15281427 {-# INLINE toJSONKey #-}
15291428
1530−instance ToJSON CTime where
1531− toJSON (CTime i) = toJSON i
1532− {-# INLINE toJSON #-}
1533−
1534− toEncoding (CTime i) = toEncoding i
1535− {-# INLINE toEncoding #-}
15361429
15371430instance ToJSON Text where
15381431 toJSON = String
@@ -1966,32 +1859,6 @@ dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
19661859formatMillis :: (FormatTime t) => t -> String
19671860formatMillis = take 3 . formatTime defaultTimeLocale "%q"
19681861
1969−-------------------------------------------------------------------------------
1970−-- primitive
1971−-------------------------------------------------------------------------------
1972−
1973−instance ToJSON a => ToJSON (PM.Array a) where
1974− -- note: we could do better than this if vector exposed the data
1975− -- constructor in Data.Vector.
1976− toJSON = toJSON . Exts.toList
1977− toEncoding = toEncoding . Exts.toList
1978−
1979−instance ToJSON a => ToJSON (PM.SmallArray a) where
1980− toJSON = toJSON . Exts.toList
1981− toEncoding = toEncoding . Exts.toList
1982−
1983−#if (MIN_VERSION_primitive(0,6,4))
1984−instance (PM.Prim a,ToJSON a) => ToJSON (PM.PrimArray a) where
1985− toJSON = toJSON . Exts.toList
1986− toEncoding = toEncoding . Exts.toList
1987−
1988−#if !MIN_VERSION_primitive(0,7,0)
1989−instance (PM.PrimUnlifted a,ToJSON a) => ToJSON (PM.UnliftedArray a) where
1990− toJSON = toJSON . Exts.toList
1991− toEncoding = toEncoding . Exts.toList
1992−#endif
1993−#endif
1994−
19951862-------------------------------------------------------------------------------
19961863-- time
19971864-------------------------------------------------------------------------------
@@ -2040,11 +1907,10 @@ stringEncoding :: Encoding' Text -> Value
20401907stringEncoding = String
20411908 . T.dropAround (== '"')
20421909 . T.decodeLatin1
2043− . L.toStrict
1910+ . lazyToStrictByteString
20441911 . E.encodingToLazyByteString
20451912{-# INLINE stringEncoding #-}
20461913
2047−
20481914instance ToJSON NominalDiffTime where
20491915 toJSON = Number . realToFrac
20501916 {-# INLINE toJSON #-}
@@ -2052,58 +1918,6 @@ instance ToJSON NominalDiffTime where
20521918 toEncoding = E.scientific . realToFrac
20531919 {-# INLINE toEncoding #-}
20541920
2055−
2056−instance ToJSON DiffTime where
2057− toJSON = Number . realToFrac
2058− {-# INLINE toJSON #-}
2059−
2060− toEncoding = E.scientific . realToFrac
2061− {-# INLINE toEncoding #-}
2062−
2063−-- | Encoded as number
2064−instance ToJSON SystemTime where
2065− toJSON (MkSystemTime secs nsecs) =
2066− toJSON (fromIntegral secs + fromIntegral nsecs / 1000000000 :: Nano)
2067− toEncoding (MkSystemTime secs nsecs) =
2068− toEncoding (fromIntegral secs + fromIntegral nsecs / 1000000000 :: Nano)
2069−
2070−instance ToJSON CalendarDiffTime where
2071− toJSON (CalendarDiffTime m nt) = object
2072− [ "months" .= m
2073− , "time" .= nt
2074− ]
2075− toEncoding (CalendarDiffTime m nt) = E.pairs
2076− ("months" .= m <> "time" .= nt)
2077−
2078−instance ToJSON CalendarDiffDays where
2079− toJSON (CalendarDiffDays m d) = object
2080− [ "months" .= m
2081− , "days" .= d
2082− ]
2083− toEncoding (CalendarDiffDays m d) = E.pairs
2084− ("months" .= m <> "days" .= d)
2085−
2086−instance ToJSON DayOfWeek where
2087− toJSON Monday = "monday"
2088− toJSON Tuesday = "tuesday"
2089− toJSON Wednesday = "wednesday"
2090− toJSON Thursday = "thursday"
2091− toJSON Friday = "friday"
2092− toJSON Saturday = "saturday"
2093− toJSON Sunday = "sunday"
2094−
2095−toEncodingDayOfWeek :: DayOfWeek -> E.Encoding' Text
2096−toEncodingDayOfWeek Monday = E.unsafeToEncoding "\"monday\""
2097−toEncodingDayOfWeek Tuesday = E.unsafeToEncoding "\"tuesday\""
2098−toEncodingDayOfWeek Wednesday = E.unsafeToEncoding "\"wednesday\""
2099−toEncodingDayOfWeek Thursday = E.unsafeToEncoding "\"thursday\""
2100−toEncodingDayOfWeek Friday = E.unsafeToEncoding "\"friday\""
2101−toEncodingDayOfWeek Saturday = E.unsafeToEncoding "\"saturday\""
2102−toEncodingDayOfWeek Sunday = E.unsafeToEncoding "\"sunday\""
2103−
2104−instance ToJSONKey DayOfWeek where
2105− toJSONKey = toJSONKeyTextEnc toEncodingDayOfWeek
2106−
21071921-------------------------------------------------------------------------------
21081922-- base Monoid/Semigroup
21091923-------------------------------------------------------------------------------
@@ -2847,26 +2661,47 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
28472661 toEncoding = toEncoding2
28482662 {-# INLINE toEncoding #-}
28492663
2664+-------------------------------------------------------------------------------
2665+-- pre-bytestring-0.10 compatibility
2666+-------------------------------------------------------------------------------
2667+
2668+{-# INLINE lazyToStrictByteString #-}
2669+lazyToStrictByteString :: L.ByteString -> S.ByteString
2670+#if MIN_VERSION_bytestring(0,10,0)
2671+lazyToStrictByteString = L.toStrict
2672+#else
2673+lazyToStrictByteString = packChunks
2674+
2675+-- packChunks is taken from the blaze-builder package.
2676+
2677+-- | Pack the chunks of a lazy bytestring into a single strict bytestring.
2678+packChunks :: L.ByteString -> S.ByteString
2679+packChunks lbs =
2680+ S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)
2681+ where
2682+ copyChunks L.Empty _pf = return ()
2683+ copyChunks (L.Chunk (S.PS fpbuf o l) lbs') pf = do
2684+ withForeignPtr fpbuf $ \pbuf ->
2685+ copyBytes pf (pbuf `plusPtr` o) l
2686+ copyChunks lbs' (pf `plusPtr` l)
2687+#endif
2688+
28502689--------------------------------------------------------------------------------
28512690
2852−-- | Wrap a list of pairs as an object.
28532691class Monoid pairs => FromPairs enc pairs | enc -> pairs where
28542692 fromPairs :: pairs -> enc
28552693
2856−instance (a ~ Value) => FromPairs (Encoding' a) Series where
2694+instance FromPairs Encoding Series where
28572695 fromPairs = E.pairs
28582696
28592697instance FromPairs Value (DList Pair) where
28602698 fromPairs = object . toList
28612699
2862−-- | Like 'KeyValue' but the value is already converted to JSON
2863−-- ('Value' or 'Encoding'), and the result actually represents lists of pairs
2864−-- so it can be readily concatenated.
2865−class Monoid kv => KeyValuePair v kv where
2866− pair :: String -> v -> kv
2700+class Monoid kv => GKeyValue v kv where
2701+ gPair :: String -> v -> kv
28672702
2868−instance (v ~ Value) => KeyValuePair v (DList Pair) where
2869− pair k v = DList.singleton (pack k .= v)
2703+instance ToJSON v => GKeyValue v (DList Pair) where
2704+ gPair k v = DList.singleton (pack k .= v)
28702705
2871−instance (e ~ Encoding) => KeyValuePair e Series where
2872− pair = E.pairStr
2706+instance GKeyValue Encoding Series where
2707+ gPair = E.pairStr
HLint.hsnew file+10
@@ -0,0 +1,10 @@
1+module HLint.HLint where
2+
3+import "hint" HLint.Default
4+import "hint" HLint.Builtin.All
5+import "hint" HLint.Dollar
6+
7+ignore "Avoid lambda"
8+ignore "Eta reduce"
9+ignore "Functor law"
10+ignore "Use >=>"
Makefile+1−1
@@ -1,2 +1,2 @@
11lint:
2− ./run-hlint.sh --cpp-include include/ --cpp-file travis/cabal_macros.h Data/ attoparsec-iso8601/ benchmarks/ examples/ ffi/ pure/ tests/
2+ hlint --cpp-include include/ --cpp-file .stack-work/dist/*/*Cabal-*/build/autogen/cabal_macros.h Data/ attoparsec-iso8601/ benchmarks/ examples/ ffi/ pure/ tests/
README.markdown+2−1
@@ -24,4 +24,5 @@ See what's changed in recent (and upcoming) releases:
2424
2525# Authors
2626
27−This library was originally written by Bryan O'Sullivan.
27+This library is written and maintained by Bryan O'Sullivan,
28+<bos@serpentine.com>.
aeson.cabal+53−65
@@ -1,5 +1,5 @@
11name: aeson
2−version: 1.4.6.0
2+version: 1.2.1.0
33license: BSD3
44license-file: LICENSE
55category: Text, Web, JSON
@@ -21,6 +21,20 @@ description:
2121 To get started, see the documentation for the @Data.Aeson@ module
2222 below.
2323 .
24+ Parsing performance on a late 2013 MacBook Pro (2.6GHz Core i7),
25+ running 64-bit GHC 7.10.1, for mostly-English tweets from Twitter's
26+ JSON search API:
27+ .
28+ * 6.4 KB payloads, English: 7570 msg\/sec (47.6 MB\/sec)
29+ .
30+ * 14.6 KB payloads, Japanese: 3261 msg\/sec (46.6 MB\/sec)
31+ .
32+ Encoding performance on the same machine and data:
33+ .
34+ * 6.4 KB payloads, English: 22738 msg\/sec (142.9 MB\/sec)
35+ .
36+ * 14.6 KB payloads, Japanese: 15911 msg\/sec (227.4 MB\/sec)
37+ .
2438 (A note on naming: in Greek mythology, Aeson was the father of Jason.)
2539
2640extra-source-files:
@@ -42,7 +56,6 @@ extra-source-files:
4256 include/*.h
4357 tests/JSONTestSuite/test_parsing/*.json
4458 tests/JSONTestSuite/test_transform/*.json
45− tests/golden/*.expected
4659 pure/Data/Aeson/Parser/*.hs
4760
4861flag developer
@@ -76,7 +89,6 @@ library
7689 Data.Aeson.Text
7790 Data.Aeson.Types
7891 Data.Aeson.TH
79− Data.Aeson.QQ.Simple
8092
8193 Data.Aeson.Encoding.Internal
8294 Data.Aeson.Internal
@@ -100,59 +112,43 @@ library
100112 Data.Attoparsec.Time
101113 Data.Attoparsec.Time.Internal
102114
103− -- GHC bundled libs
104115 build-depends:
105− base >= 4.7.0.0 && < 5,
106− bytestring >= 0.10.4.0 && < 0.11,
107− containers >= 0.5.5.1 && < 0.7,
108− deepseq >= 1.3.0.0 && < 1.5,
109− ghc-prim >= 0.2 && < 0.6,
110− template-haskell >= 2.9.0.0 && < 2.16,
111− text >= 1.2.3.0 && < 1.3,
112− time >= 1.4 && < 1.10
113−
114− if impl(ghc >= 8.0)
115− build-depends: bytestring >= 0.10.8.1
116−
117− -- Compat
118− build-depends:
119− base-compat >= 0.9.1 && < 0.12,
120− time-compat >= 1.9.2.2 && < 1.10
116+ attoparsec >= 0.13.0.1,
117+ base >= 4.5 && < 5,
118+ base-compat >= 0.9.1 && < 0.10,
119+ containers >= 0.4.2,
120+ deepseq >= 1.3 && < 1.5,
121+ dlist >= 0.6,
122+ ghc-prim >= 0.2,
123+ hashable >= 1.1.2.0,
124+ scientific >= 0.3.4.7 && < 0.4,
125+ tagged >=0.8.3 && <0.9,
126+ template-haskell >= 2.7,
127+ text >= 1.1.1.0,
128+ th-abstraction >= 0.2.2 && < 0.3,
129+ time >= 1.1.1.4,
130+ time-locale-compat >= 0.1.1 && < 0.2,
131+ unordered-containers >= 0.2.5.0 && < 0.3,
132+ uuid-types >= 1.0.3 && <1.1,
133+ vector >= 0.8
121134
122− if !impl(ghc >= 8.6)
123− build-depends:
124− contravariant >=1.4.1 && <1.6
135+ if flag(bytestring-builder)
136+ build-depends: bytestring >= 0.9.2 && < 0.10.4,
137+ bytestring-builder >= 0.10.4 && < 1
138+ else
139+ build-depends: bytestring >= 0.10.4 && < 0.11
125140
126141 if !impl(ghc >= 8.0)
127142 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
128143 build-depends:
129− semigroups >= 0.18.5 && < 0.20,
130− transformers >= 0.3.0.0 && < 0.6,
131− transformers-compat >= 0.6.2 && < 0.7,
144+ semigroups >= 0.18.2 && < 0.19,
145+ transformers >= 0.2.2.0,
146+ transformers-compat >= 0.3,
132147 fail == 4.9.*
133148
134149 if !impl(ghc >= 7.10)
135150 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
136− build-depends: nats >= 1.1.1 && < 1.2,
137− void >= 0.7.2 && < 0.8
138−
139− -- cannot use latest version
140− build-depends:
141− unordered-containers >= 0.2.8.0 && < 0.3,
142−
143− -- not in LTS-12.10
144− tagged >= 0.8.5 && < 0.9,
145− primitive >= 0.6.3.0 && < 0.8
146−
147− -- Other dependencies
148− build-depends:
149− attoparsec >= 0.13.2.2 && < 0.14,
150− dlist >= 0.8.0.4 && < 0.9,
151− hashable >= 1.2.7.0 && < 1.4,
152− scientific >= 0.3.6.2 && < 0.4,
153− th-abstraction >= 0.2.8.0 && < 0.4,
154− uuid-types >= 1.0.3 && < 1.1,
155− vector >= 0.12.0.1 && < 0.13
151+ build-depends: nats >= 1 && < 1.2
156152
157153 ghc-options: -Wall
158154
@@ -195,49 +191,42 @@ test-suite tests
195191 Functions
196192 Instances
197193 Options
198− PropUtils
199194 Properties
200− PropertyGeneric
201− PropertyKeys
202− PropertyRoundTrip
203− PropertyRTFunctors
204− PropertyTH
205195 SerializationFormatSpec
206196 Types
207197 UnitTests
208198 UnitTests.NullaryConstructors
209199
210200 build-depends:
211− QuickCheck >= 2.10.0.1 && < 2.14,
201+ HUnit,
202+ QuickCheck >= 2.8 && <2.11,
212203 aeson,
213204 integer-logarithms >= 1 && <1.1,
214205 attoparsec,
215206 base,
216207 base-compat,
217− base-orphans >= 0.5.3 && <0.9,
208+ base-orphans >= 0.5.3 && <0.7,
218209 base16-bytestring,
219210 containers,
220211 directory,
221212 dlist,
222− Diff >= 0.4 && < 0.5,
223213 filepath,
224− generic-deriving >= 1.10 && < 1.14,
214+ generic-deriving >= 1.10 && < 1.12,
225215 ghc-prim >= 0.2,
226216 hashable >= 1.2.4.0,
227217 scientific,
228218 tagged,
229219 template-haskell,
230− tasty,
231− tasty-golden,
232− tasty-hunit,
233− tasty-quickcheck,
220+ test-framework,
221+ test-framework-hunit,
222+ test-framework-quickcheck2,
234223 text,
235224 time,
236− time-compat,
225+ time-locale-compat,
237226 unordered-containers,
238227 uuid-types,
239228 vector,
240− quickcheck-instances >= 0.3.21 && <0.4
229+ quickcheck-instances >=0.3.14
241230
242231 if flag(bytestring-builder)
243232 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -247,13 +236,12 @@ test-suite tests
247236
248237 if !impl(ghc >= 8.0)
249238 build-depends:
250− semigroups >= 0.18.2 && < 0.20,
239+ semigroups >= 0.18.2 && < 0.19,
251240 transformers >= 0.2.2.0,
252241 transformers-compat >= 0.3
253242
254243 if !impl(ghc >= 7.10)
255− build-depends: nats >=1 && <1.2,
256− void >=0.7.2 && <0.8
244+ build-depends: nats >=1 && <1.2
257245
258246 if impl(ghc >= 7.8)
259247 build-depends: hashable-time >= 0.2 && <0.3
attoparsec-iso8601/Data/Attoparsec/Time.hs+5−4
@@ -1,6 +1,6 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE ScopedTypeVariables #-}
3+
44-- |
55-- Module: Data.Aeson.Parser.Time
66-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -21,6 +21,7 @@ module Data.Attoparsec.Time
2121 , zonedTime
2222 ) where
2323
24+import Prelude ()
2425import Prelude.Compat
2526
2627import Control.Applicative ((<|>))
@@ -41,9 +42,9 @@ import qualified Data.Time.LocalTime as Local
4142day :: Parser Day
4243day = do
4344 absOrNeg <- negate <$ char '-' <|> id <$ char '+' <|> pure id
44− y <- (decimal <* char '-') <|> fail "date must be of form [+,-]YYYY-MM-DD"
45− m <- (twoDigits <* char '-') <|> fail "date must be of form [+,-]YYYY-MM-DD"
46− d <- twoDigits <|> fail "date must be of form [+,-]YYYY-MM-DD"
45+ y <- decimal <* char '-'
46+ m <- twoDigits <* char '-'
47+ d <- twoDigits
4748 maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
4849
4950-- | Parse a two-digit integer (e.g. day of month, hour).
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+18−21
@@ -1,5 +1,5 @@
11{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
2+
33-- |
44-- Module: Data.Aeson.Internal.Time
55-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -17,48 +17,45 @@ module Data.Attoparsec.Time.Internal
1717 , toTimeOfDay64
1818 ) where
1919
20+import Prelude ()
2021import Prelude.Compat
2122
2223import Data.Int (Int64)
2324import Data.Time
2425import Unsafe.Coerce (unsafeCoerce)
2526
26−#if MIN_VERSION_time(1,6,0)
27−
28−import Data.Time.Clock (diffTimeToPicoseconds)
29−
30−#endif
27+#if MIN_VERSION_base(4,7,0)
3128
3229import Data.Fixed (Pico, Fixed(MkFixed))
3330
34−#if !MIN_VERSION_time(1,6,0)
31+toPico :: Integer -> Pico
32+toPico = MkFixed
3533
36−diffTimeToPicoseconds :: DiffTime -> Integer
37−diffTimeToPicoseconds = unsafeCoerce
34+fromPico :: Pico -> Integer
35+fromPico (MkFixed i) = i
3836
39−#endif
37+#else
38+
39+import Data.Fixed (Pico)
4040
4141toPico :: Integer -> Pico
42−toPico = MkFixed
42+toPico = unsafeCoerce
4343
4444fromPico :: Pico -> Integer
45−fromPico (MkFixed i) = i
45+fromPico = unsafeCoerce
46+
47+#endif
4648
4749-- | Like TimeOfDay, but using a fixed-width integer for seconds.
4850data TimeOfDay64 = TOD {-# UNPACK #-} !Int
4951 {-# UNPACK #-} !Int
5052 {-# UNPACK #-} !Int64
5153
52−posixDayLength :: DiffTime
53−posixDayLength = 86400
54−
5554diffTimeOfDay64 :: DiffTime -> TimeOfDay64
56−diffTimeOfDay64 t
57− | t >= posixDayLength = TOD 23 59 (60000000000000 + pico (t - posixDayLength))
58− | otherwise = TOD (fromIntegral h) (fromIntegral m) s
59− where (h,mp) = pico t `quotRem` 3600000000000000
60− (m,s) = mp `quotRem` 60000000000000
61− pico = fromIntegral . diffTimeToPicoseconds
55+diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
56+ where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
57+ (m,s) = mp `quotRem` 60000000000000
58+ pico = unsafeCoerce t :: Integer
6259
6360toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
6461toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
attoparsec-iso8601/attoparsec-iso8601.cabal+3−3
@@ -1,5 +1,5 @@
11name: attoparsec-iso8601
2−version: 1.0.1.0
2+version: 1.0.0.0
33synopsis: Parsing of ISO 8601 dates, originally from aeson.
44description: Parsing of ISO 8601 dates, originally from aeson.
55license: BSD3
@@ -36,8 +36,8 @@ library
3636 Data.Attoparsec.Time
3737 build-depends:
3838 attoparsec >= 0.13.0.1,
39− base >= 4.7 && < 5,
40− base-compat >= 0.9.1 && < 0.12,
39+ base >= 4.5 && < 5,
40+ base-compat >= 0.9.1 && < 0.10,
4141 text >= 1.1.1.0,
4242 time >= 1.1.1.4
4343
attoparsec-iso8601/changelog.mddeleted−5
@@ -1,5 +0,0 @@
1−For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md](https://github.com/bos/aeson/blob/master/attoparsec-iso8601/changelog.md).
2−
3−### 1.0.1.0
4−
5−* Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann
benchmarks/AesonCompareAutoInstances.hsnew file+282
@@ -0,0 +1,282 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE TemplateHaskell #-}
4+
5+module Main (main) where
6+
7+import Prelude ()
8+import Prelude.Compat
9+
10+import Control.Monad
11+import Control.DeepSeq (NFData, rnf, deepseq)
12+import Criterion.Main hiding (defaultOptions)
13+import Data.Aeson
14+import Data.Aeson.Encoding
15+import Data.Aeson.TH
16+import Data.Aeson.Types
17+import Data.ByteString.Lazy (ByteString)
18+import Data.Data (Data)
19+import Data.Typeable (Typeable)
20+import GHC.Generics (Generic, Rep)
21+import Options
22+
23+toBS :: Encoding -> ByteString
24+toBS = encodingToLazyByteString
25+
26+gEncode :: (Generic a, GToEncoding Zero (Rep a)) => a -> ByteString
27+gEncode = toBS . genericToEncoding opts
28+
29+--------------------------------------------------------------------------------
30+
31+data D a = Nullary
32+ | Unary Int
33+ | Product String Char a
34+ | Record { testOne :: Double
35+ , testTwo :: Bool
36+ , testThree :: D a
37+ }
38+ deriving (Show, Eq)
39+
40+deriveJSON opts ''D
41+
42+instance NFData a => NFData (D a) where
43+ rnf Nullary = ()
44+ rnf (Unary n) = rnf n
45+ rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
46+ rnf (Record d b y) = d `deepseq` b `deepseq` rnf y
47+
48+type T = D (D (D ()))
49+
50+d :: T
51+d = Record
52+ { testOne = 1234.56789
53+ , testTwo = True
54+ , testThree = Product "Hello World!" 'a'
55+ Record
56+ { testOne = 9876.54321
57+ , testTwo = False
58+ , testThree = Product "Yeehaa!!!" '\n' Nullary
59+ }
60+ }
61+
62+--------------------------------------------------------------------------------
63+
64+data D' a = Nullary'
65+ | Unary' Int
66+ | Product' String Char a
67+ | Record' { testOne' :: Double
68+ , testTwo' :: Bool
69+ , testThree' :: D' a
70+ }
71+ deriving (Show, Eq, Generic)
72+
73+instance ToJSON a => ToJSON (D' a) where
74+ toJSON = genericToJSON opts
75+
76+instance FromJSON a => FromJSON (D' a) where
77+ parseJSON = genericParseJSON opts
78+
79+instance NFData a => NFData (D' a) where
80+ rnf Nullary' = ()
81+ rnf (Unary' n) = rnf n
82+ rnf (Product' s c x) = s `deepseq` c `deepseq` rnf x
83+ rnf (Record' d b y) = d `deepseq` b `deepseq` rnf y
84+
85+type T' = D' (D' (D' ()))
86+
87+d' :: T'
88+d' = Record'
89+ { testOne' = 1234.56789
90+ , testTwo' = True
91+ , testThree' = Product' "Hello World!" 'a'
92+ Record'
93+ { testOne' = 9876.54321
94+ , testTwo' = False
95+ , testThree' = Product' "Yeehaa!!!" '\n' Nullary'
96+ }
97+ }
98+
99+--------------------------------------------------------------------------------
100+
101+data BigRecord = BigRecord
102+ { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
103+ , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
104+ , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
105+ , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
106+ , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
107+ } deriving (Show, Eq, Generic)
108+
109+instance NFData BigRecord
110+
111+bigRecord = BigRecord 1 2 3 4 5
112+ 6 7 8 9 10
113+ 11 12 13 14 15
114+ 16 17 18 19 20
115+ 21 22 23 24 25
116+
117+return []
118+
119+gBigRecordToJSON :: BigRecord -> Value
120+gBigRecordToJSON = genericToJSON opts
121+
122+gBigRecordEncode :: BigRecord -> ByteString
123+gBigRecordEncode = gEncode
124+
125+gBigRecordFromJSON :: Value -> Result BigRecord
126+gBigRecordFromJSON = parse $ genericParseJSON opts
127+
128+thBigRecordToJSON :: BigRecord -> Value
129+thBigRecordToJSON = $(mkToJSON opts ''BigRecord)
130+
131+thBigRecordEncode :: BigRecord -> ByteString
132+thBigRecordEncode = toBS . $(mkToEncoding opts ''BigRecord)
133+
134+thBigRecordFromJSON :: Value -> Result BigRecord
135+thBigRecordFromJSON = parse $(mkParseJSON opts ''BigRecord)
136+
137+--------------------------------------------------------------------------------
138+
139+data BigProduct = BigProduct
140+ !Int !Int !Int !Int !Int
141+ !Int !Int !Int !Int !Int
142+ !Int !Int !Int !Int !Int
143+ !Int !Int !Int !Int !Int
144+ !Int !Int !Int !Int !Int
145+ deriving (Show, Eq, Generic)
146+
147+instance NFData BigProduct
148+
149+bigProduct = BigProduct 1 2 3 4 5
150+ 6 7 8 9 10
151+ 11 12 13 14 15
152+ 16 17 18 19 20
153+ 21 22 23 24 25
154+
155+return []
156+
157+gBigProductToJSON :: BigProduct -> Value
158+gBigProductToJSON = genericToJSON opts
159+
160+gBigProductEncode :: BigProduct -> ByteString
161+gBigProductEncode = gEncode
162+
163+gBigProductFromJSON :: Value -> Result BigProduct
164+gBigProductFromJSON = parse $ genericParseJSON opts
165+
166+thBigProductToJSON :: BigProduct -> Value
167+thBigProductToJSON = $(mkToJSON opts ''BigProduct)
168+
169+thBigProductEncode :: BigProduct -> ByteString
170+thBigProductEncode = toBS . $(mkToEncoding opts ''BigProduct)
171+
172+thBigProductFromJSON :: Value -> Result BigProduct
173+thBigProductFromJSON = parse $(mkParseJSON opts ''BigProduct)
174+
175+--------------------------------------------------------------------------------
176+
177+data BigSum = F01 | F02 | F03 | F04 | F05
178+ | F06 | F07 | F08 | F09 | F10
179+ | F11 | F12 | F13 | F14 | F15
180+ | F16 | F17 | F18 | F19 | F20
181+ | F21 | F22 | F23 | F24 | F25
182+ deriving (Show, Eq, Generic)
183+
184+instance NFData BigSum
185+
186+bigSum = F25
187+
188+return []
189+
190+gBigSumToJSON :: BigSum -> Value
191+gBigSumToJSON = genericToJSON opts
192+
193+gBigSumEncode :: BigSum -> ByteString
194+gBigSumEncode = gEncode
195+
196+gBigSumFromJSON :: Value -> Result BigSum
197+gBigSumFromJSON = parse $ genericParseJSON opts
198+
199+thBigSumToJSON :: BigSum -> Value
200+thBigSumToJSON = $(mkToJSON opts ''BigSum)
201+
202+thBigSumEncode :: BigSum -> ByteString
203+thBigSumEncode = toBS . $(mkToEncoding opts ''BigSum)
204+
205+thBigSumFromJSON :: Value -> Result BigSum
206+thBigSumFromJSON = parse $(mkParseJSON opts ''BigSum)
207+
208+--------------------------------------------------------------------------------
209+
210+type FJ a = Value -> Result a
211+
212+runBench :: IO ()
213+runBench = defaultMain
214+ [ let v = toJSON d
215+ in (d, d', v) `deepseq`
216+ bgroup "D"
217+ [ group "toJSON" (nf toJSON d)
218+ (nf toJSON d')
219+ , group "encode" (nf encode d)
220+ (nf encode d')
221+ , group "fromJSON" (nf ( fromJSON :: FJ T ) v)
222+ (nf ( fromJSON :: FJ T') v)
223+ ]
224+ , let v = thBigRecordToJSON bigRecord
225+ in bigRecord `deepseq` v `deepseq`
226+ bgroup "BigRecord"
227+ [ group "toJSON" (nf thBigRecordToJSON bigRecord)
228+ (nf gBigRecordToJSON bigRecord)
229+ , group "encode" (nf thBigRecordEncode bigRecord)
230+ (nf gBigRecordEncode bigRecord)
231+ , group "fromJSON" (nf (thBigRecordFromJSON :: FJ BigRecord) v)
232+ (nf ( gBigRecordFromJSON :: FJ BigRecord) v)
233+ ]
234+ , let v = thBigProductToJSON bigProduct
235+ in bigProduct `deepseq` v `deepseq`
236+ bgroup "BigProduct"
237+ [ group "toJSON" (nf thBigProductToJSON bigProduct)
238+ (nf gBigProductToJSON bigProduct)
239+ , group "encode" (nf thBigProductEncode bigProduct)
240+ (nf gBigProductEncode bigProduct)
241+ , group "fromJSON" (nf (thBigProductFromJSON :: FJ BigProduct) v)
242+ (nf (gBigProductFromJSON :: FJ BigProduct) v)
243+ ]
244+ , let v = thBigSumToJSON bigSum
245+ in bigSum `deepseq` v `deepseq`
246+ bgroup "BigSum"
247+ [ group "toJSON" (nf thBigSumToJSON bigSum)
248+ (nf gBigSumToJSON bigSum)
249+ , group "encode" (nf thBigSumEncode bigSum)
250+ (nf gBigSumEncode bigSum)
251+ , group "fromJSON" (nf (thBigSumFromJSON :: FJ BigSum) v)
252+ (nf (gBigSumFromJSON :: FJ BigSum) v)
253+ ]
254+ ]
255+
256+group n th gen = bgroup n [ bench "th" th
257+ , bench "generic" gen
258+ ]
259+
260+sanityCheck = do
261+ check d toJSON fromJSON encode
262+ check d' toJSON fromJSON encode
263+ check bigRecord thBigRecordToJSON thBigRecordFromJSON thBigRecordEncode
264+ check bigRecord gBigRecordToJSON gBigRecordFromJSON gBigRecordEncode
265+ check bigProduct thBigProductToJSON thBigProductFromJSON thBigProductEncode
266+ check bigProduct gBigProductToJSON gBigProductFromJSON gBigProductEncode
267+ check bigSum thBigSumToJSON thBigSumFromJSON thBigSumEncode
268+ check bigSum gBigSumToJSON gBigSumFromJSON gBigSumEncode
269+
270+check :: (Show a, Eq a)
271+ => a -> (a -> Value) -> (Value -> Result a) -> (a -> ByteString) -> IO ()
272+check x toJSON fromJSON encode = do
273+ unless (Success x == (fromJSON . toJSON) x) $ fail $ "toJSON: " ++ show x
274+ unless (Success x == (decode' . encode) x) $ fail $ "encode: " ++ show x
275+ where
276+ decode' s = case decode s of
277+ Just v -> fromJSON v
278+ Nothing -> fail ""
279+
280+main = do
281+ sanityCheck
282+ runBench
benchmarks/AesonEncode.hs+2−2
@@ -1,13 +1,13 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE OverloadedStrings #-}
43
54module Main (main) where
65
6+import Prelude ()
77import Prelude.Compat
88
99import Control.DeepSeq
10−import Control.Monad (forM_)
10+import Control.Monad
1111import Data.Aeson
1212import Data.Attoparsec.ByteString (IResult(..), parseWith)
1313import Data.Char (isDigit)
benchmarks/AesonFoldable.hs+42−14
@@ -1,14 +1,16 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE PackageImports #-}
33
44module Main (main) where
55
66import Criterion.Main
77
8+import Prelude ()
89import Prelude.Compat
910
1011import Data.Foldable (toList)
11−import qualified Data.Aeson as A
12+import qualified "aeson" Data.Aeson as A
13+import qualified "aeson-benchmarks" Data.Aeson as B
1214import qualified Data.Sequence as S
1315import qualified Data.Vector as V
1416import qualified Data.Vector.Unboxed as U
@@ -19,6 +21,10 @@ import qualified Data.Vector.Unboxed as U
1921
2022newtype L f = L { getL :: f Int }
2123
24+instance Foldable f => B.ToJSON (L f) where
25+ toJSON = error "do not use this"
26+ toEncoding = B.toEncoding . toList . getL
27+
2228instance Foldable f => A.ToJSON (L f) where
2329 toJSON = error "do not use this"
2430 toEncoding = A.toEncoding . toList . getL
@@ -29,6 +35,10 @@ instance Foldable f => A.ToJSON (L f) where
2935
3036newtype F f = F { getF :: f Int }
3137
38+instance Foldable f => B.ToJSON (F f) where
39+ toJSON = error "do not use this"
40+ toEncoding = B.foldable . getF
41+
3242instance Foldable f => A.ToJSON (F f) where
3343 toJSON = error "do not use this"
3444 toEncoding = A.foldable . getF
@@ -53,34 +63,52 @@ valueUVector = U.fromList valueList
5363-- Main
5464-------------------------------------------------------------------------------
5565
56−benchEncode
66+benchEncodeA
5767 :: A.ToJSON a
5868 => String
5969 -> a
6070 -> Benchmark
61−benchEncode name val
71+benchEncodeA name val
6272 = bench ("A " ++ name) $ nf A.encode val
6373
74+benchEncodeB
75+ :: B.ToJSON a
76+ => String
77+ -> a
78+ -> Benchmark
79+benchEncodeB name val
80+ = bench ("B " ++ name) $ nf B.encode val
81+
6482main :: IO ()
6583main = defaultMain
6684 [ bgroup "encode"
6785 [ bgroup "List"
68− [ benchEncode "-" valueList
69− , benchEncode "L" $ L valueList
70− , benchEncode "F" $ F valueList
86+ [ benchEncodeB "-" valueList
87+ , benchEncodeB "L" $ L valueList
88+ , benchEncodeB "F" $ F valueList
89+ , benchEncodeA "-" valueList
90+ , benchEncodeA "L" $ L valueList
91+ , benchEncodeA "F" $ F valueList
7192 ]
7293 , bgroup "Seq"
73− [ benchEncode "-" valueSeq
74− , benchEncode "L" $ L valueSeq
75− , benchEncode "F" $ F valueSeq
94+ [ benchEncodeB "-" valueSeq
95+ , benchEncodeB "L" $ L valueSeq
96+ , benchEncodeB "F" $ F valueSeq
97+ , benchEncodeA "-" valueSeq
98+ , benchEncodeA "L" $ L valueSeq
99+ , benchEncodeA "F" $ F valueSeq
76100 ]
77101 , bgroup "Vector"
78− [ benchEncode "-" valueVector
79− , benchEncode "L" $ L valueVector
80− , benchEncode "F" $ F valueVector
102+ [ benchEncodeB "-" valueVector
103+ , benchEncodeB "L" $ L valueVector
104+ , benchEncodeB "F" $ F valueVector
105+ , benchEncodeA "-" valueVector
106+ , benchEncodeA "L" $ L valueVector
107+ , benchEncodeA "F" $ F valueVector
81108 ]
82109 , bgroup "Vector.Unboxed"
83− [ benchEncode "-" valueUVector
110+ [ benchEncodeB "-" valueUVector
111+ , benchEncodeA "-" valueUVector
84112 ]
85113 ]
86114 ]
benchmarks/AesonMap.hs+56−39
@@ -1,11 +1,13 @@
11{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE PackageImports #-}
34{-# LANGUAGE RankNTypes #-}
45
56{-# OPTIONS_GHC -fno-warn-orphans #-}
67
78module Main (main) where
89
10+import Prelude ()
911import Prelude.Compat
1012
1113import Control.DeepSeq
@@ -13,8 +15,9 @@ import Criterion.Main
1315import Data.Hashable
1416import Data.Proxy (Proxy (..))
1517import Data.Tagged (Tagged (..))
16−import Data.Aeson
17−import Data.Aeson.Types (fromJSONKeyCoerce)
18+import qualified "aeson" Data.Aeson as A
19+import qualified "aeson-benchmarks" Data.Aeson as B
20+import qualified "aeson-benchmarks" Data.Aeson.Types as B (fromJSONKeyCoerce)
1821import qualified Data.ByteString.Lazy as LBS
1922import qualified Data.HashMap.Strict as HM
2023import qualified Data.Map as M
@@ -44,10 +47,10 @@ instance NFData T1 where
4447instance Hashable T1 where
4548 hashWithSalt salt (T1 t) = hashWithSalt salt t
4649
47−instance FromJSON T1 where
48− parseJSON = withText "T1" $ pure . T1
49−instance FromJSONKey T1 where
50− fromJSONKey = FromJSONKeyText T1
50+instance B.FromJSON T1 where
51+ parseJSON = B.withText "T1" $ pure . T1
52+instance B.FromJSONKey T1 where
53+ fromJSONKey = B.FromJSONKeyText T1
5154
5255-------------------------------------------------------------------------------
5356-- Coerce
@@ -61,10 +64,10 @@ instance NFData T2 where
6164instance Hashable T2 where
6265 hashWithSalt salt (T2 t) = hashWithSalt salt t
6366
64−instance FromJSON T2 where
65− parseJSON = withText "T2" $ pure . T2
66−instance FromJSONKey T2 where
67− fromJSONKey = fromJSONKeyCoerce
67+instance B.FromJSON T2 where
68+ parseJSON = B.withText "T2" $ pure . T2
69+instance B.FromJSONKey T2 where
70+ fromJSONKey = B.fromJSONKeyCoerce
6871
6972-------------------------------------------------------------------------------
7073-- TextParser
@@ -78,10 +81,10 @@ instance NFData T3 where
7881instance Hashable T3 where
7982 hashWithSalt salt (T3 t) = hashWithSalt salt t
8083
81−instance FromJSON T3 where
82− parseJSON = withText "T3" $ pure . T3
83−instance FromJSONKey T3 where
84− fromJSONKey = FromJSONKeyTextParser (pure . T3)
84+instance B.FromJSON T3 where
85+ parseJSON = B.withText "T3" $ pure . T3
86+instance B.FromJSONKey T3 where
87+ fromJSONKey = B.FromJSONKeyTextParser (pure . T3)
8588
8689-------------------------------------------------------------------------------
8790-- Values
@@ -94,30 +97,40 @@ value1000 = value 1000
9497value10000 = value 10000
9598
9699encodedValue10 :: LBS.ByteString
97−encodedValue10 = encode value10
100+encodedValue10 = B.encode value10
98101
99102encodedValue100 :: LBS.ByteString
100−encodedValue100 = encode value100
103+encodedValue100 = B.encode value100
101104
102105encodedValue1000 :: LBS.ByteString
103−encodedValue1000 = encode value1000
106+encodedValue1000 = B.encode value1000
104107
105108encodedValue10000 :: LBS.ByteString
106−encodedValue10000 = encode value10000
109+encodedValue10000 = B.encode value10000
107110
108111-------------------------------------------------------------------------------
109112-- Helpers
110113-------------------------------------------------------------------------------
111114
112−decodeHM
113− :: (FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
115+decodeHMB
116+ :: (B.FromJSONKey k, Eq k, Hashable k)
114117 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
115−decodeHM _ = decode
118+decodeHMB _ = B.decode
116119
117−decodeMap
118− :: (FromJSON (M.Map k T.Text), Ord k)
120+decodeHMA
121+ :: (A.FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
122+ => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
123+decodeHMA _ = A.decode
124+
125+decodeMapB
126+ :: (B.FromJSONKey k, Ord k)
127+ => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
128+decodeMapB _ = B.decode
129+
130+decodeMapA
131+ :: (A.FromJSON (M.Map k T.Text), Ord k)
119132 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
120−decodeMap _ = decode
133+decodeMapA _ = A.decode
121134
122135proxyText :: Proxy T.Text
123136proxyText = Proxy
@@ -143,14 +156,15 @@ benchDecodeHM
143156 -> LBS.ByteString
144157 -> Benchmark
145158benchDecodeHM name val = bgroup name
146− [ bench "Text" $ nf (decodeHM proxyText) val
147− , bench "Identity" $ nf (decodeHM proxyT1) val
148− , bench "Coerce" $ nf (decodeHM proxyT2) val
149− , bench "Parser" $ nf (decodeHM proxyT3) val
150− , bench "Tagged Text" $ nf (decodeHM $ proxyTagged proxyText) val
151− , bench "Tagged Identity" $ nf (decodeHM $ proxyTagged proxyT1) val
152− , bench "Tagged Coerce" $ nf (decodeHM $ proxyTagged proxyT2) val
153− , bench "Tagged Parser" $ nf (decodeHM $ proxyTagged proxyT3) val
159+ [ bench "Text" $ nf (decodeHMB proxyText) val
160+ , bench "Identity" $ nf (decodeHMB proxyT1) val
161+ , bench "Coerce" $ nf (decodeHMB proxyT2) val
162+ , bench "Parser" $ nf (decodeHMB proxyT3) val
163+ , bench "aeson-hackage" $ nf (decodeHMA proxyText) val
164+ , bench "Tagged Text" $ nf (decodeHMB $ proxyTagged proxyText) val
165+ , bench "Tagged Identity" $ nf (decodeHMB $ proxyTagged proxyT1) val
166+ , bench "Tagged Coerce" $ nf (decodeHMB $ proxyTagged proxyT2) val
167+ , bench "Tagged Parser" $ nf (decodeHMB $ proxyTagged proxyT3) val
154168 ]
155169
156170benchDecodeMap
@@ -158,10 +172,11 @@ benchDecodeMap
158172 -> LBS.ByteString
159173 -> Benchmark
160174benchDecodeMap name val = bgroup name
161− [ bench "Text" $ nf (decodeMap proxyText) val
162− , bench "Identity" $ nf (decodeMap proxyT1) val
163− , bench "Coerce" $ nf (decodeMap proxyT2) val
164− , bench "Parser" $ nf (decodeMap proxyT3) val
175+ [ bench "Text" $ nf (decodeMapB proxyText) val
176+ , bench "Identity" $ nf (decodeMapB proxyT1) val
177+ , bench "Coerce" $ nf (decodeMapB proxyT2) val
178+ , bench "Parser" $ nf (decodeMapB proxyT3) val
179+ , bench "aeson-hackage" $ nf (decodeMapA proxyText) val
165180 ]
166181
167182benchEncodeHM
@@ -169,7 +184,8 @@ benchEncodeHM
169184 -> HM.HashMap T.Text T.Text
170185 -> Benchmark
171186benchEncodeHM name val = bgroup name
172− [ bench "Text" $ nf encode val
187+ [ bench "Text" $ nf B.encode val
188+ , bench "aeson-0.11" $ nf A.encode val
173189 ]
174190
175191benchEncodeMap
@@ -177,7 +193,8 @@ benchEncodeMap
177193 -> HM.HashMap T.Text T.Text
178194 -> Benchmark
179195benchEncodeMap name val = bgroup name
180− [ bench "Text" $ nf encode val'
196+ [ bench "Text" $ nf B.encode val'
197+ , bench "aeson-0.11" $ nf A.encode val'
181198 ]
182199 where
183200 val' :: M.Map T.Text T.Text
benchmarks/AesonParse.hs+3−2
@@ -1,12 +1,13 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE PackageImports #-}
44
55module Main (main) where
66
7+import Prelude ()
78import Prelude.Compat
89
9−import Data.Aeson
10+import "aeson-benchmarks" Data.Aeson
1011import Control.Monad
1112import Data.Attoparsec.ByteString (IResult(..), parseWith)
1213import Data.Time.Clock
benchmarks/AesonTuples.hs+1−2
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Main (main) where
42
3+import Prelude ()
54import Prelude.Compat
65
76import Control.DeepSeq (deepseq)
benchmarks/Auto/G/BigProduct.hsdeleted−33
@@ -1,33 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigProduct where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigProduct = BigProduct
11− !Int !Int !Int !Int !Int
12− !Int !Int !Int !Int !Int
13− !Int !Int !Int !Int !Int
14− !Int !Int !Int !Int !Int
15− !Int !Int !Int !Int !Int
16− deriving (Show, Eq, Generic)
17−
18−instance NFData BigProduct where
19− rnf a = a `seq` ()
20−
21−instance ToJSON BigProduct where
22− toJSON = genericToJSON opts
23− toEncoding = genericToEncoding opts
24−
25−instance FromJSON BigProduct where
26− parseJSON = genericParseJSON opts
27−
28−bigProduct :: BigProduct
29−bigProduct = BigProduct 1 2 3 4 5
30− 6 7 8 9 10
31− 11 12 13 14 15
32− 16 17 18 19 20
33− 21 22 23 24 25
benchmarks/Auto/G/BigRecord.hsdeleted−33
@@ -1,33 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigRecord where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigRecord = BigRecord
11− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
12− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
13− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
14− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
15− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
16− } deriving (Show, Eq, Generic)
17−
18−instance NFData BigRecord where
19− rnf a = a `seq` ()
20−
21−instance ToJSON BigRecord where
22− toJSON = genericToJSON opts
23− toEncoding = genericToEncoding opts
24−
25−instance FromJSON BigRecord where
26− parseJSON = genericParseJSON opts
27−
28−bigRecord :: BigRecord
29−bigRecord = BigRecord 1 2 3 4 5
30− 6 7 8 9 10
31− 11 12 13 14 15
32− 16 17 18 19 20
33− 21 22 23 24 25
benchmarks/Auto/G/BigSum.hsdeleted−28
@@ -1,28 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.BigSum where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data BigSum = F01 | F02 | F03 | F04 | F05
11− | F06 | F07 | F08 | F09 | F10
12− | F11 | F12 | F13 | F14 | F15
13− | F16 | F17 | F18 | F19 | F20
14− | F21 | F22 | F23 | F24 | F25
15− deriving (Show, Eq, Generic)
16−
17−instance NFData BigSum where
18− rnf a = a `seq` ()
19−
20−instance ToJSON BigSum where
21− toJSON = genericToJSON opts
22− toEncoding = genericToEncoding opts
23−
24−instance FromJSON BigSum where
25− parseJSON = genericParseJSON opts
26−
27−bigSum :: BigSum
28−bigSum = F25
benchmarks/Auto/G/D.hsdeleted−44
@@ -1,44 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−
3−module Auto.G.D where
4−
5−import Control.DeepSeq
6−import Data.Aeson
7−import GHC.Generics (Generic)
8−import Options
9−
10−data D a = Nullary
11− | Unary Int
12− | Product String Char a
13− | Record { testOne :: Double
14− , testTwo :: Bool
15− , testThree :: D a
16− }
17− deriving (Show, Eq, Generic)
18−
19−instance NFData a => NFData (D a) where
20− rnf Nullary = ()
21− rnf (Unary n) = rnf n
22− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
23− rnf (Record a b y) = a `deepseq` b `deepseq` rnf y
24−
25−instance ToJSON a => ToJSON (D a) where
26− toJSON = genericToJSON opts
27− toEncoding = genericToEncoding opts
28−
29−instance FromJSON a => FromJSON (D a) where
30− parseJSON = genericParseJSON opts
31−
32−type T = D (D (D ()))
33−
34−d :: T
35−d = Record
36− { testOne = 1234.56789
37− , testTwo = True
38− , testThree = Product "Hello World!" 'a'
39− Record
40− { testOne = 9876.54321
41− , testTwo = False
42− , testThree = Product "Yeehaa!!!" '\n' Nullary
43− }
44− }
benchmarks/Auto/T/BigProduct.hsdeleted−27
@@ -1,27 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigProduct where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigProduct = BigProduct
10− !Int !Int !Int !Int !Int
11− !Int !Int !Int !Int !Int
12− !Int !Int !Int !Int !Int
13− !Int !Int !Int !Int !Int
14− !Int !Int !Int !Int !Int
15− deriving (Show, Eq)
16−
17−instance NFData BigProduct where
18− rnf a = a `seq` ()
19−
20−deriveJSON opts ''BigProduct
21−
22−bigProduct :: BigProduct
23−bigProduct = BigProduct 1 2 3 4 5
24− 6 7 8 9 10
25− 11 12 13 14 15
26− 16 17 18 19 20
27− 21 22 23 24 25
benchmarks/Auto/T/BigRecord.hsdeleted−27
@@ -1,27 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigRecord where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigRecord = BigRecord
10− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
11− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
12− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
13− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
14− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
15− } deriving (Show, Eq)
16−
17−instance NFData BigRecord where
18− rnf a = a `seq` ()
19−
20−deriveJSON opts ''BigRecord
21−
22−bigRecord :: BigRecord
23−bigRecord = BigRecord 1 2 3 4 5
24− 6 7 8 9 10
25− 11 12 13 14 15
26− 16 17 18 19 20
27− 21 22 23 24 25
benchmarks/Auto/T/BigSum.hsdeleted−22
@@ -1,22 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.BigSum where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data BigSum = F01 | F02 | F03 | F04 | F05
10− | F06 | F07 | F08 | F09 | F10
11− | F11 | F12 | F13 | F14 | F15
12− | F16 | F17 | F18 | F19 | F20
13− | F21 | F22 | F23 | F24 | F25
14− deriving (Show, Eq)
15−
16−instance NFData BigSum where
17− rnf a = a `seq` ()
18−
19−deriveJSON opts ''BigSum
20−
21−bigSum :: BigSum
22−bigSum = F25
benchmarks/Auto/T/D.hsdeleted−38
@@ -1,38 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−module Auto.T.D where
4−
5−import Control.DeepSeq
6−import Data.Aeson.TH
7−import Options
8−
9−data D a = Nullary
10− | Unary Int
11− | Product String Char a
12− | Record { testOne :: Double
13− , testTwo :: Bool
14− , testThree :: D a
15− }
16− deriving (Show, Eq)
17−
18−instance NFData a => NFData (D a) where
19− rnf Nullary = ()
20− rnf (Unary n) = rnf n
21− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
22− rnf (Record a b y) = a `deepseq` b `deepseq` rnf y
23−
24−deriveJSON opts ''D
25−
26−type T = D (D (D ()))
27−
28−d :: T
29−d = Record
30− { testOne = 1234.56789
31− , testTwo = True
32− , testThree = Product "Hello World!" 'a'
33− Record
34− { testOne = 9876.54321
35− , testTwo = False
36− , testThree = Product "Yeehaa!!!" '\n' Nullary
37− }
38− }
benchmarks/AutoCompare.hsdeleted−73
@@ -1,73 +0,0 @@
1−{-# LANGUAGE RankNTypes #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−module Main (main) where
5−
6−import Control.DeepSeq
7−import Control.Monad
8−import Criterion.Main
9−import Data.Aeson
10−
11−import qualified Auto.T.D as T
12−import qualified Auto.T.BigRecord as T
13−import qualified Auto.T.BigProduct as T
14−import qualified Auto.T.BigSum as T
15−import qualified Auto.G.D as G
16−import qualified Auto.G.BigRecord as G
17−import qualified Auto.G.BigProduct as G
18−import qualified Auto.G.BigSum as G
19−
20−--------------------------------------------------------------------------------
21−
22−runBench :: IO ()
23−runBench = defaultMain
24− [ compareBench "D" T.d G.d
25− , compareBench "BigRecord" T.bigRecord G.bigRecord
26− , compareBench "BigProduct" T.bigProduct G.bigProduct
27− , compareBench "BigSum" T.bigSum G.bigSum
28− ]
29−
30−group :: String -> Benchmarkable -> Benchmarkable -> Benchmark
31−group n th gen = bgroup n [ bench "th" th
32− , bench "generic" gen
33− ]
34−
35−compareBench
36− :: forall a b
37− . (ToJSON a, FromJSON a, NFData a, ToJSON b, FromJSON b, NFData b)
38− => String -> a -> b -> Benchmark
39−compareBench name a b = v `deepseq` bgroup name
40− [ group "toJSON" (nf toJSON a)
41− (nf toJSON b)
42− , group "encode" (nf encode a)
43− (nf encode b)
44− , group "fromJSON" (nf (fromJSON :: Value -> Result a) v)
45− (nf (fromJSON :: Value -> Result b) v)
46− ] where
47− v = toJSON a -- == toJSON b
48−
49−sanityCheck :: IO ()
50−sanityCheck = do
51− check T.d
52− check G.d
53− check T.bigRecord
54− check G.bigRecord
55− check T.bigProduct
56− check G.bigProduct
57− check T.bigSum
58− check G.bigSum
59−
60−check :: (Show a, Eq a, FromJSON a, ToJSON a)
61− => a -> IO ()
62−check x = do
63− unless (Success x == (fromJSON . toJSON) x) $ fail $ "toJSON: " ++ show x
64− unless (Success x == (decode_ . encode) x) $ fail $ "encode: " ++ show x
65− where
66− decode_ s = case decode s of
67− Just v -> fromJSON v
68− Nothing -> fail ""
69−
70−main :: IO ()
71−main = do
72− sanityCheck
73− runBench
benchmarks/Compare.hs+5−10
@@ -1,25 +1,22 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE PackageImports #-}
32{-# LANGUAGE ScopedTypeVariables #-}
43
54module Main (main) where
65
6+import Prelude ()
77import Prelude.Compat
88
99import Compare.BufferBuilder ()
10+import Compare.JsonBuilder ()
1011import Criterion.Main
1112import Data.BufferBuilder.Json
13+import Data.Json.Builder
1214import Twitter
1315import Twitter.Manual ()
1416import Typed.Common
15−import qualified Data.Aeson as Aeson
17+import qualified "aeson-benchmarks" Data.Aeson as Aeson
1618import qualified Compare.JsonBench as JsonBench
1719
18−#ifdef MIN_VERSION_json_builder
19−import Data.Json.Builder
20−import Compare.JsonBuilder ()
21−#endif
22−
2320main :: IO ()
2421main =
2522 defaultMain [
@@ -27,9 +24,7 @@ main =
2724 bgroup "twitter" [
2825 bench "aeson" $ nf Aeson.encode twtr
2926 , bench "buffer-builder" $ nf encodeJson twtr
30−#ifdef MIN_VERSION_json_builder
3127 , bench "json-builder" $ nf toJsonLBS twtr
32−#endif
3328 ]
3429 , JsonBench.benchmarks
3530 ]
benchmarks/Compare/BufferBuilder.hs+3−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE OverloadedStrings #-}
32{-# LANGUAGE RecordWildCards #-}
43
@@ -7,11 +6,13 @@
76
87module Compare.BufferBuilder () where
98
10−import Prelude.Compat hiding ((<>))
9+import Prelude ()
10+import Prelude.Compat
1111
1212import Data.BufferBuilder.Json
1313import Data.Int (Int64)
1414import Data.Monoid ((<>))
15+import Prelude hiding (id)
1516import Twitter
1617import qualified Data.BufferBuilder.Utf8 as UB
1718
benchmarks/Compare/JsonBench.hs+9−18
@@ -1,18 +1,17 @@
1−{-# LANGUAGE CPP #-}
1+-- Adapted from a buffer-builder benchmark:
2+--
3+-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
4+
25{-# LANGUAGE BangPatterns #-}
36{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
57{-# LANGUAGE OverloadedStrings #-}
68{-# LANGUAGE RecordWildCards #-}
79{-# LANGUAGE ScopedTypeVariables #-}
810
9−-- Adapted from a buffer-builder benchmark:
10−--
11−-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
12−
1311module Compare.JsonBench (benchmarks) where
1412
15−import Prelude.Compat hiding ((<>))
13+import Prelude ()
14+import Prelude.Compat
1615
1716import Control.DeepSeq (NFData(..))
1817import Criterion
@@ -20,13 +19,9 @@ import Data.Aeson ((.:))
2019import Data.Monoid ((<>))
2120import Data.Text (Text)
2221import Typed.Common (load)
23−import qualified Control.Monad.Fail as Fail
2422import qualified Data.Aeson as Aeson
2523import qualified Data.BufferBuilder.Json as Json
26−
27−#ifdef MIN_VERSION_json_builder
2824import qualified Data.Json.Builder as JB
29−#endif
3025
3126data EyeColor = Green | Blue | Brown
3227 deriving (Eq, Show)
@@ -69,10 +64,10 @@ instance NFData Gender where rnf !_ = ()
6964instance NFData Fruit where rnf !_ = ()
7065
7166instance NFData Friend where
72− rnf Friend {..} = rnf fId `seq` rnf fName
67+ rnf Friend {..} = rnf fId `seq` rnf fName `seq` ()
7368
7469instance NFData User where
75− rnf User {..} = rnf uId `seq` rnf uIndex `seq` rnf uGuid `seq` rnf uIsActive `seq` rnf uBalance `seq` rnf uPicture `seq` rnf uAge `seq` rnf uEyeColor `seq` rnf uName `seq` rnf uGender `seq` rnf uCompany `seq` rnf uEmail `seq` rnf uPhone `seq` rnf uAddress `seq` rnf uAbout `seq` rnf uRegistered `seq` rnf uLatitude `seq` rnf uLongitude `seq` rnf uTags `seq` rnf uFriends `seq` rnf uGreeting `seq` rnf uFavouriteFruit
70+ rnf User {..} = rnf uId `seq` rnf uIndex `seq` rnf uGuid `seq` rnf uIsActive `seq` rnf uBalance `seq` rnf uPicture `seq` rnf uAge `seq` rnf uEyeColor `seq` rnf uName `seq` rnf uGender `seq` rnf uCompany `seq` rnf uEmail `seq` rnf uPhone `seq` rnf uAddress `seq` rnf uAbout `seq` rnf uRegistered `seq` rnf uLatitude `seq` rnf uLongitude `seq` rnf uTags `seq` rnf uFriends `seq` rnf uGreeting `seq` rnf uFavouriteFruit `seq` ()
7671
7772eyeColorTable :: [(Text, EyeColor)]
7873eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
@@ -83,7 +78,7 @@ genderTable = [("male", Male), ("female", Female)]
8378fruitTable :: [(Text, Fruit)]
8479fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
8580
86−enumFromJson :: Fail.MonadFail m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
81+enumFromJson :: Monad m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
8782enumFromJson enumName table extract v = do
8883 s <- extract v
8984 case lookup s table of
@@ -273,7 +268,6 @@ instance Json.ToJson User where
273268 <> "greeting"# Json..=# uGreeting
274269 <> "favoriteFruit"# Json..=# uFavouriteFruit
275270
276−#ifdef MIN_VERSION_json_builder
277271---- json-builder instances ----
278272
279273instance JB.Value EyeColor where
@@ -325,7 +319,6 @@ instance JB.Value User where
325319 <> t "friends" `JB.row` uFriends
326320 <> t "greeting" `JB.row` uGreeting
327321 <> t "favoriteFruit" `JB.row` uFavouriteFruit
328−#endif
329322
330323benchmarks :: Benchmark
331324benchmarks = env (load "json-data/buffer-builder.json") $
@@ -333,7 +326,5 @@ benchmarks = env (load "json-data/buffer-builder.json") $
333326 bgroup "json-bench" [
334327 bench "aeson" $ nf Aeson.encode parsedUserList
335328 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
336−#ifdef MIN_VERSION_json_builder
337329 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
338−#endif
339330 ]
benchmarks/Compare/JsonBuilder.hs+3−2
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE RecordWildCards #-}
32
43{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
@@ -6,10 +5,12 @@
65
76module Compare.JsonBuilder () where
87
9−import Prelude.Compat hiding ((<>))
8+import Prelude ()
9+import Prelude.Compat
1010
1111import Data.Json.Builder
1212import Data.Monoid ((<>))
13+import Prelude hiding (id)
1314import Twitter
1415
1516instance JsObject Metadata where
benchmarks/CompareWithJSON.hs+33−23
@@ -1,9 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
1+{-# LANGUAGE PackageImports #-}
32{-# OPTIONS_GHC -fno-warn-orphans #-}
43
54module Main (main) where
65
6+import Prelude ()
77import Prelude.Compat
88
99import Blaze.ByteString.Builder (toLazyByteString)
@@ -11,9 +11,10 @@ import Blaze.ByteString.Builder.Char.Utf8 (fromString)
1111import Control.DeepSeq (NFData(rnf))
1212import Criterion.Main
1313import Data.Maybe (fromMaybe)
14−import qualified Data.Aeson as A
15−import qualified Data.Aeson.Text as A
16−import qualified Data.Aeson.Parser.Internal as I
14+import qualified "aeson-benchmarks" Data.Aeson as A
15+import qualified "aeson-benchmarks" Data.Aeson.Text as A
16+import qualified "aeson-benchmarks" Data.Aeson.Parser.Internal as I
17+import qualified "aeson" Data.Aeson as B
1718import qualified Data.ByteString as BS
1819import qualified Data.ByteString.Lazy as BL
1920import qualified Data.Text.Lazy as TL
@@ -27,7 +28,7 @@ instance (NFData v) => NFData (J.JSObject v) where
2728instance NFData J.JSValue where
2829 rnf J.JSNull = ()
2930 rnf (J.JSBool b) = rnf b
30− rnf (J.JSRational a b) = rnf a `seq` rnf b
31+ rnf (J.JSRational a b) = rnf a `seq` rnf b `seq` ()
3132 rnf (J.JSString s) = rnf (J.fromJSString s)
3233 rnf (J.JSArray lst) = rnf lst
3334 rnf (J.JSObject o) = rnf o
@@ -38,14 +39,20 @@ decodeJ s =
3839 J.Ok v -> v
3940 J.Error _ -> error "fail to parse via JSON"
4041
41−decode :: BL.ByteString -> A.Value
42−decode s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
42+decodeA :: BL.ByteString -> A.Value
43+decodeA s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
44+
45+decodeA' :: BL.ByteString -> A.Value
46+decodeA' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s
47+
48+decodeAS :: BS.ByteString -> A.Value
49+decodeAS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
4350
44−decode' :: BL.ByteString -> A.Value
45−decode' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s
51+decodeB :: BL.ByteString -> B.Value
52+decodeB s = fromMaybe (error "fail to parse via Aeson") $ B.decode s
4653
47−decodeS :: BS.ByteString -> A.Value
48−decodeS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
54+decodeBS :: BS.ByteString -> B.Value
55+decodeBS s = fromMaybe (error "fail to parse via Aeson") $ B.decodeStrict' s
4956
5057decodeIP :: BL.ByteString -> A.Value
5158decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
@@ -73,29 +80,32 @@ main = do
7380 defaultMain [
7481 bgroup "decode" [
7582 bgroup "en" [
76− bench "aeson/lazy" $ nf decode enA
77− , bench "aeson/strict" $ nf decode' enA
78− , bench "aeson/stricter" $ nf decodeS enS
83+ bench "aeson/lazy" $ nf decodeA enA
84+ , bench "aeson/strict" $ nf decodeA' enA
85+ , bench "aeson/stricter" $ nf decodeAS enS
86+ , bench "aeson/hackage" $ nf decodeB enA
87+ , bench "aeson/hackage'" $ nf decodeBS enS
7988 , bench "aeson/parser" $ nf decodeIP enA
8089 , bench "json" $ nf decodeJ enJ
8190 ]
8291 , bgroup "jp" [
83− bench "aeson" $ nf decode jpA
84− , bench "aeson/stricter" $ nf decodeS jpS
92+ bench "aeson" $ nf decodeA jpA
93+ , bench "aeson/stricter" $ nf decodeAS jpS
94+ , bench "aeson/hackage" $ nf decodeB jpA
8595 , bench "json" $ nf decodeJ jpJ
8696 ]
8797 ]
8898 , bgroup "encode" [
8999 bgroup "en" [
90− bench "aeson-to-bytestring" $ nf A.encode (decode enA)
91− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode enA)
92− , bench "aeson-to-text" $ nf encodeToText (decode enA)
100+ bench "aeson-to-bytestring" $ nf A.encode (decodeA enA)
101+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA enA)
102+ , bench "aeson-to-text" $ nf encodeToText (decodeA enA)
93103 , bench "json" $ nf encodeJ (decodeJ enJ)
94104 ]
95105 , bgroup "jp" [
96− bench "aeson-to-bytestring" $ nf A.encode (decode jpA)
97− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode jpA)
98− , bench "aeson-to-text" $ nf encodeToText (decode jpA)
106+ bench "aeson-to-bytestring" $ nf A.encode (decodeA jpA)
107+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA jpA)
108+ , bench "aeson-to-text" $ nf encodeToText (decodeA jpA)
99109 , bench "json" $ nf encodeJ (decodeJ jpJ)
100110 ]
101111 ]
benchmarks/Dates.hs+2−2
@@ -1,7 +1,7 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
1+{-# OPTIONS_GHC -fsimpl-tick-factor=0 #-}
32module Main (main) where
43
4+import Prelude ()
55import Prelude.Compat
66
77import Criterion.Main
benchmarks/Escape.hsdeleted−32
@@ -1,32 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
5−
6−module Main (main) where
7−
8−import Prelude.Compat
9−
10−import Criterion.Main
11−import qualified Data.Aeson.Parser.UnescapeFFI as FFI
12−import qualified Data.Aeson.Parser.UnescapePure as Pure
13−
14−import qualified Data.ByteString.Char8 as BS
15−import System.Environment (getArgs, withArgs)
16−
17−main :: IO ()
18−main = do
19− args_ <- getArgs
20− let (args, p, n) =
21− case args_ of
22− "--pattern" : p : args_ -> k p args_
23− _ -> k "\\\"" args_
24− k p args_ =
25− case args_ of
26− "--repeat" : n : args_ -> (args_, p, read n)
27− args_ -> (args_, p, 10000)
28− input = BS.concat $ replicate n $ BS.pack p
29− withArgs args $ defaultMain
30− [ bench "ffi" $ whnf FFI.unescapeText input
31− , bench "pure" $ whnf Pure.unescapeText input
32− ]
benchmarks/Issue673.hsdeleted−165
@@ -1,165 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−
4−module Main (
5− main,
6− input17,
7− input32,
8− input64,
9− input128,
10− input256,
11− input2048,
12− input4096,
13− input8192,
14− input16384,
15− ) where
16−
17−import Criterion.Main
18−import Prelude.Compat
19−import Data.Int (Int64)
20−import Data.Scientific (Scientific)
21−import Data.Semigroup ((<>))
22−import Data.Aeson.Parser (scientific)
23−
24−import qualified Data.Attoparsec.ByteString.Lazy as AttoL
25−import qualified Data.Attoparsec.ByteString.Char8 as Atto8
26−import qualified Data.Aeson as A
27−import qualified Data.ByteString as BS
28−import qualified Data.ByteString.Lazy as LBS
29−import qualified Data.ByteString.Lazy.Char8 as LBS8
30−
31−decodeInt :: LBS.ByteString -> Maybe Int
32−decodeInt = A.decode
33−
34−decodeString :: LBS.ByteString -> Maybe String
35−decodeString = A.decode
36−
37−decodeScientific :: LBS.ByteString -> Maybe Scientific
38−decodeScientific = A.decode
39−
40−decodeViaRead :: LBS.ByteString -> Integer
41−decodeViaRead = read . LBS8.unpack
42−
43−decodeAtto :: LBS.ByteString -> Maybe Scientific
44−decodeAtto
45− = parseOnly (scientific <* AttoL.endOfInput)
46− where
47− parseOnly p lbs = case AttoL.parse p lbs of
48− AttoL.Done _ r -> Just r
49− AttoL.Fail {} -> Nothing
50−
51−decodeAtto8 :: LBS.ByteString -> Maybe Scientific
52−decodeAtto8
53− = parseOnly (Atto8.scientific <* AttoL.endOfInput)
54− where
55− parseOnly p lbs = case AttoL.parse p lbs of
56− AttoL.Done _ r -> Just r
57− AttoL.Fail {} -> Nothing
58−
59−generate :: Int64 -> LBS.ByteString
60−generate n = LBS8.replicate n '1'
61−
62−input17 :: LBS.ByteString
63−input17 = generate 17
64−
65−input32 :: LBS.ByteString
66−input32 = generate 32
67−
68−input64 :: LBS.ByteString
69−input64 = generate 64
70−
71−input128 :: LBS.ByteString
72−input128 = generate 128
73−
74−input256 :: LBS.ByteString
75−input256 = generate 256
76−
77−input2048 :: LBS.ByteString
78−input2048 = generate 2048
79−
80−input4096 :: LBS.ByteString
81−input4096 = generate 4096
82−
83−input8192 :: LBS.ByteString
84−input8192 = generate 8192
85−
86−input16384 :: LBS.ByteString
87−input16384 = generate 16384
88−
89−
90−main :: IO ()
91−main = defaultMain
92− -- works on 64bit
93− [ benchPair "17" input17
94− -- , benchPair "32" input32
95− -- , benchPair "64" input64
96− -- , benchPair "128" input128
97− -- , benchPair "256" input256
98− , benchPair "2048" input2048
99− , benchPair "4096" input4096
100− , benchPair "8192" input8192
101− , benchPair "16384" input16384
102− ]
103− where
104− benchPair name input = bgroup name
105− [ bench "Int" $ whnf decodeInt input
106− , bench "Simple" $ whnf bsToIntegerSimple (LBS.toStrict input)
107− , bench "Optim" $ whnf bsToInteger (LBS.toStrict input)
108− , bench "Read" $ whnf decodeViaRead input
109− , bench "Scientific" $ whnf decodeScientific input
110− , bench "parserA" $ whnf decodeAtto input
111− , bench "parserS" $ whnf decodeAtto8 input
112− , bench "String" $ whnf decodeString $ "\"" <> input <> "\""
113− ]
114−
115−-------------------------------------------------------------------------------
116−-- better fromInteger
117−-------------------------------------------------------------------------------
118−
119−bsToInteger :: BS.ByteString -> Integer
120−bsToInteger bs
121− | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]
122− | otherwise = bsToIntegerSimple bs
123− where
124− l = BS.length bs
125−
126−bsToIntegerSimple :: BS.ByteString -> Integer
127−bsToIntegerSimple = BS.foldl' step 0 where
128− step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
129−
130−-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
131−-- digits are combined into a single radix b^2 digit. This process is
132−-- repeated until we are left with a single digit. This algorithm
133−-- performs well only on large inputs, so we use the simple algorithm
134−-- for smaller inputs.
135−valInteger :: Integer -> Int -> [Integer] -> Integer
136−valInteger = go
137− where
138− go :: Integer -> Int -> [Integer] -> Integer
139− go _ _ [] = 0
140− go _ _ [d] = d
141− go b l ds
142− | l > 40 = b' `seq` go b' l' (combine b ds')
143− | otherwise = valSimple b ds
144− where
145− -- ensure that we have an even number of digits
146− -- before we call combine:
147− ds' = if even l then ds else 0 : ds
148− b' = b * b
149− l' = (l + 1) `quot` 2
150−
151− combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
152− where
153− d = d1 * b + d2
154− combine _ [] = []
155− combine _ [_] = errorWithoutStackTrace "this should not happen"
156−
157−-- The following algorithm is only linear for types whose Num operations
158−-- are in constant time.
159−valSimple :: Integer -> [Integer] -> Integer
160−valSimple base = go 0
161− where
162− go r [] = r
163− go r (d : ds) = r' `seq` go r' ds
164− where
165− r' = r * base + fromIntegral d
benchmarks/JsonParse.hs+2−3
@@ -1,11 +1,10 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE ScopedTypeVariables #-}
4−
53{-# OPTIONS_GHC -fno-warn-orphans #-}
64
75module Main (main) where
86
7+import Prelude ()
98import Prelude.Compat
109
1110import Control.DeepSeq
@@ -17,7 +16,7 @@ import Text.JSON
1716instance NFData JSValue where
1817 rnf JSNull = ()
1918 rnf (JSBool b) = rnf b
20− rnf (JSRational b r) = rnf b `seq` rnf r
19+ rnf (JSRational b r) = rnf b `seq` rnf r `seq` ()
2120 rnf (JSString s) = rnf (fromJSString s)
2221 rnf (JSArray vs) = rnf vs
2322 rnf (JSObject kvs) = rnf (fromJSObject kvs)
benchmarks/Micro.hs+6−8
@@ -1,13 +1,11 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Main (main) where
42
3+import Prelude ()
54import Prelude.Compat
65
76import Criterion.Main
8−
9−-- Encoding is a newtype wrapper around Builder
10−import Data.Aeson.Encoding (text, string, encodingToLazyByteString)
7+import qualified Data.Aeson.Encoding.Builder as AB
8+import qualified Data.ByteString.Builder as B
119import qualified Data.Text as T
1210
1311main :: IO ()
@@ -15,8 +13,8 @@ main = do
1513 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
1614 defaultMain [
1715 bgroup "string" [
18− bench "text" $ nf (encodingToLazyByteString . text) (T.pack txt)
19− , bench "string direct" $ nf (encodingToLazyByteString . string) txt
20− , bench "string via text" $ nf (encodingToLazyByteString . text . T.pack) txt
16+ bench "text" $ nf (B.toLazyByteString . AB.text) (T.pack txt)
17+ , bench "string direct" $ nf (B.toLazyByteString . AB.string) txt
18+ , bench "string via text" $ nf (B.toLazyByteString . AB.text . T.pack) txt
2119 ]
2220 ]
benchmarks/Options.hs+3
@@ -1,5 +1,8 @@
11module Options (opts) where
22
3+import Prelude ()
4+import Prelude.Compat
5+
36import Data.Aeson.Types
47
58opts :: Options
benchmarks/ReadFile.hs+1−1
@@ -1,9 +1,9 @@
11{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32{-# LANGUAGE OverloadedStrings #-}
43
54module Main (main) where
65
6+import Prelude ()
77import Prelude.Compat
88
99import Control.DeepSeq
benchmarks/Typed.hs+1−2
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Main (main) where
42
3+import Prelude ()
54import Prelude.Compat
65
76import Criterion.Main
benchmarks/Typed/Common.hs+10−1
@@ -1,16 +1,25 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE CPP #-}
2+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
3+{-# LANGUAGE PackageImports #-}
4+#endif
25
36{-# OPTIONS_GHC -fno-warn-unused-imports #-}
47
58module Typed.Common (load) where
69
10+import Prelude ()
711import Prelude.Compat
812
913import Data.ByteString.Lazy as L
1014import System.Exit
1115import System.IO
1216
17+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
1318import Data.Aeson hiding (Result)
19+#else
20+import "aeson" Data.Aeson hiding (Result)
21+import qualified "aeson-benchmarks" Data.Aeson as B
22+#endif
1423
1524load :: FromJSON a => FilePath -> IO a
1625load fileName = do
benchmarks/Typed/Generic.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
1+{-# LANGUAGE PackageImports #-}
32module Typed.Generic (benchmarks, decodeBenchmarks) where
43
4+import Prelude ()
55import Prelude.Compat
66
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
88import Criterion
99import Data.ByteString.Lazy as L
1010import Twitter.Generic
1111import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
1216
13−encodeDirect :: Result -> L.ByteString
14−encodeDirect = encode
17+encodeViaValueA :: Result -> L.ByteString
18+encodeViaValueA = encode . toJSON
1519
16−encodeViaValue :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
20+encodeDirectB :: Result -> L.ByteString
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
1825
1926benchmarks :: Benchmark
2027benchmarks =
2128 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
2229 bgroup "encodeGeneric" [
2330 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
25− , bench "jp100" $ nf encodeDirect jp100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
2635 ]
2736 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
37+ bench "twitter100" $ nf encodeViaValueB twitter100
38+ , bench "jp100" $ nf encodeViaValueB jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
40+ , bench "jp100 baseline" $ nf encodeViaValueA jp100
3041 ]
3142 ]
3243
33−decodeDirect :: L.ByteString -> Maybe Result
34−decodeDirect = decode
44+decodeDirectA :: L.ByteString -> Maybe Result
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
3549
3650decodeBenchmarks :: Benchmark
3751decodeBenchmarks =
3852 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
3953 bgroup "decodeGeneric"
4054 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
42− , bench "jp100" $ nf decodeDirect jp100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
4359 ]
4460 ]
benchmarks/Typed/Manual.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
1+{-# LANGUAGE PackageImports #-}
32module Typed.Manual (benchmarks, decodeBenchmarks) where
43
4+import Prelude ()
55import Prelude.Compat
66
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
88import Criterion
99import Data.ByteString.Lazy as L
1010import Twitter.Manual
1111import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
1216
13−encodeDirect :: Result -> L.ByteString
14−encodeDirect = encode
17+encodeViaValueA :: Result -> L.ByteString
18+encodeViaValueA = encode . toJSON
1519
16−encodeViaValue :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
20+encodeDirectB :: Result -> L.ByteString
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
1825
1926benchmarks :: Benchmark
2027benchmarks =
2128 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
2229 bgroup "encodeManual" [
2330 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
25− , bench "jp100" $ nf encodeDirect jp100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
2635 ]
2736 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
37+ bench "twitter100" $ nf encodeViaValueB twitter100
38+ , bench "jp100" $ nf encodeViaValueB jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
40+ , bench "jp100 baseline" $ nf encodeViaValueA jp100
3041 ]
3142 ]
3243
33−decodeDirect :: L.ByteString -> Maybe Result
34−decodeDirect = decode
44+decodeDirectA :: L.ByteString -> Maybe Result
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
3549
3650decodeBenchmarks :: Benchmark
3751decodeBenchmarks =
3852 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
3953 bgroup "decodeManual"
4054 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
42− , bench "jp100" $ nf decodeDirect jp100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
4359 ]
4460 ]
benchmarks/Typed/TH.hs+31−15
@@ -1,44 +1,60 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
1+{-# LANGUAGE PackageImports #-}
32module Typed.TH (benchmarks, decodeBenchmarks) where
43
4+import Prelude ()
55import Prelude.Compat
66
7−import Data.Aeson hiding (Result)
7+import "aeson" Data.Aeson hiding (Result)
88import Criterion
99import Data.ByteString.Lazy as L
1010import Twitter.TH
1111import Typed.Common
12+import qualified "aeson-benchmarks" Data.Aeson as B
13+
14+encodeDirectA :: Result -> L.ByteString
15+encodeDirectA = encode
1216
13−encodeDirect :: Result -> L.ByteString
14−encodeDirect = encode
17+encodeViaValueA :: Result -> L.ByteString
18+encodeViaValueA = encode . toJSON
1519
16−encodeViaValue :: Result -> L.ByteString
17−encodeViaValue = encode . toJSON
20+encodeDirectB :: Result -> L.ByteString
21+encodeDirectB = B.encode
22+
23+encodeViaValueB :: Result -> L.ByteString
24+encodeViaValueB = B.encode . B.toJSON
1825
1926benchmarks :: Benchmark
2027benchmarks =
2128 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
2229 bgroup "encodeTH" [
2330 bgroup "direct" [
24− bench "twitter100" $ nf encodeDirect twitter100
25− , bench "jp100" $ nf encodeDirect jp100
31+ bench "twitter100" $ nf encodeDirectB twitter100
32+ , bench "jp100" $ nf encodeDirectB jp100
33+ , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34+ , bench "jp100 baseline" $ nf encodeDirectA jp100
2635 ]
2736 , bgroup "viaValue" [
28− bench "twitter100" $ nf encodeViaValue twitter100
29− , bench "jp100" $ nf encodeViaValue jp100
37+ bench "twitter100" $ nf encodeViaValueA twitter100
38+ , bench "jp100" $ nf encodeViaValueA jp100
39+ , bench "twitter100 baseline" $ nf encodeViaValueB twitter100
40+ , bench "jp100 baseline" $ nf encodeViaValueB jp100
3041 ]
3142 ]
3243
33−decodeDirect :: L.ByteString -> Maybe Result
34−decodeDirect = decode
44+decodeDirectA :: L.ByteString -> Maybe Result
45+decodeDirectA = decode
46+
47+decodeDirectB :: L.ByteString -> Maybe Result
48+decodeDirectB = B.decode
3549
3650decodeBenchmarks :: Benchmark
3751decodeBenchmarks =
3852 env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
3953 bgroup "decodeTH"
4054 [ bgroup "direct"
41− [ bench "twitter100" $ nf decodeDirect twitter100
42− , bench "jp100" $ nf decodeDirect jp100
55+ [ bench "twitter100" $ nf decodeDirectB twitter100
56+ , bench "jp100" $ nf decodeDirectB jp100
57+ , bench "twitter100 baseline" $ nf decodeDirectA twitter100
58+ , bench "jp100 baseline" $ nf decodeDirectA jp100
4359 ]
4460 ]
benchmarks/aeson-benchmarks.cabal+70−167
@@ -1,21 +1,65 @@
11name: aeson-benchmarks
22version: 0
33build-type: Simple
4−cabal-version: >=1.10
4+
5+cabal-version: >=1.8
56
67flag bytestring-builder
78 description: Depend on the bytestring-builder package for backwards compatibility.
89 default: False
910 manual: False
1011
11−flag local-aeson
12− description: Build the local version of aeson, to avoid rebuilding aeson's
13− reverse dependencies for benchmarking (statistics, criterion).
14− default: True
15− manual: True
16−
1712library
18− default-language: Haskell2010
13+ hs-source-dirs: .. . ../ffi ../pure ../attoparsec-iso8601
14+ c-sources: ../cbits/unescape_string.c
15+ exposed-modules:
16+ Data.Aeson
17+ Data.Aeson.Encoding
18+ Data.Aeson.Encoding.Builder
19+ Data.Aeson.Encoding.Internal
20+ Data.Aeson.Internal
21+ Data.Aeson.Internal.Functions
22+ Data.Aeson.Internal.Time
23+ Data.Aeson.Parser
24+ Data.Aeson.Parser.Internal
25+ Data.Aeson.Parser.Time
26+ Data.Aeson.Parser.Unescape
27+ Data.Aeson.Parser.UnescapeFFI
28+ Data.Aeson.Parser.UnescapePure
29+ Data.Aeson.TH
30+ Data.Aeson.Text
31+ Data.Aeson.Types
32+ Data.Aeson.Types.Class
33+ Data.Aeson.Types.FromJSON
34+ Data.Aeson.Types.Generic
35+ Data.Aeson.Types.Internal
36+ Data.Aeson.Types.ToJSON
37+ Data.Attoparsec.Time
38+ Data.Attoparsec.Time.Internal
39+
40+ build-depends:
41+ attoparsec >= 0.13.0.1,
42+ base == 4.*,
43+ base-compat >= 0.9.1 && <0.10,
44+ time-locale-compat >=0.1.1 && <0.2,
45+ containers,
46+ deepseq,
47+ dlist >= 0.2,
48+ fail == 4.9.*,
49+ ghc-prim >= 0.2,
50+ hashable >= 1.1.2.0,
51+ mtl,
52+ scientific >= 0.3.4.7 && < 0.4,
53+ syb,
54+ tagged >=0.8.3 && <0.9,
55+ template-haskell >= 2.4,
56+ text >= 1.1.1.0,
57+ th-abstraction >= 0.2.2 && < 0.3,
58+ time,
59+ transformers,
60+ unordered-containers >= 0.2.3.0,
61+ uuid-types >= 1.0.3 && <1.1,
62+ vector >= 0.7.1
1963
2064 if flag(bytestring-builder)
2165 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -23,139 +67,26 @@ library
2367 else
2468 build-depends: bytestring >= 0.10.4
2569
26− if flag(local-aeson)
27−
28− hs-source-dirs: .. ../ffi ../pure ../attoparsec-iso8601
29− c-sources: ../cbits/unescape_string.c
30− build-depends:
31− attoparsec,
32− base,
33− base-compat,
34− containers,
35− deepseq,
36− dlist,
37− fail,
38− ghc-prim,
39− hashable,
40− mtl,
41− primitive,
42− scientific,
43− syb,
44− tagged,
45− template-haskell,
46− text,
47− th-abstraction,
48− time-compat,
49− time,
50− transformers,
51− unordered-containers,
52− uuid-types,
53− vector
54−
55− if !impl(ghc >= 8.6)
56− build-depends: contravariant
57−
58− if !impl(ghc >= 8.0)
59− -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
60− build-depends: semigroups,
61− transformers-compat
70+ if impl(ghc >=7.8)
71+ cpp-options: -DHAS_COERCIBLE
6272
63− if !impl(ghc >= 7.10)
64− -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
65− build-depends: nats,
66− void
73+ if !impl(ghc >= 8.0)
74+ -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
75+ build-depends: semigroups >= 0.18.2 && < 0.19
6776
68− include-dirs: ../include
69−
70− ghc-options: -O2 -Wall
71− cpp-options: -DGENERICS
72−
73− exposed-modules:
74− Data.Aeson
75− Data.Aeson.Encoding
76− Data.Aeson.Encoding.Builder
77− Data.Aeson.Encoding.Internal
78− Data.Aeson.Internal
79− Data.Aeson.Internal.Functions
80− Data.Aeson.Internal.Time
81− Data.Aeson.Parser
82− Data.Aeson.Parser.Internal
83− Data.Aeson.Parser.Time
84− Data.Aeson.Parser.Unescape
85− Data.Aeson.Parser.UnescapeFFI
86− Data.Aeson.Parser.UnescapePure
87− Data.Aeson.TH
88− Data.Aeson.Text
89− Data.Aeson.Types
90− Data.Aeson.Types.Class
91− Data.Aeson.Types.FromJSON
92− Data.Aeson.Types.Generic
93− Data.Aeson.Types.Internal
94− Data.Aeson.Types.ToJSON
95− Data.Attoparsec.Time
96− Data.Attoparsec.Time.Internal
97−
98− else
99−
100− build-depends: aeson
101−
102− reexported-modules:
103− Data.Aeson,
104− Data.Aeson.Encoding,
105− Data.Aeson.Parser.Internal,
106− Data.Aeson.Text,
107− Data.Aeson.TH,
108− Data.Aeson.Types
109−
110−executable aeson-benchmark-auto-compare
111− default-language: Haskell2010
112− main-is: AutoCompare.hs
113− hs-source-dirs: .
114− ghc-options: -Wall -O2 -rtsopts
115− other-modules:
116− Auto.T.D
117− Auto.T.BigRecord
118− Auto.T.BigProduct
119− Auto.T.BigSum
120− Auto.G.D
121− Auto.G.BigRecord
122− Auto.G.BigProduct
123− Auto.G.BigSum
124− Options
125− build-depends:
126− aeson-benchmarks,
127− base,
128− criterion,
129− deepseq,
130− template-haskell
77+ include-dirs: ../include
13178
132−executable aeson-benchmark-escape
133− default-language: Haskell2010
134− main-is: Escape.hs
135− hs-source-dirs: ../examples .
136− ghc-options: -Wall -O2 -rtsopts
137− if flag(local-aeson)
138− build-depends:
139− aeson-benchmarks,
140− base,
141− base-compat,
142− bytestring,
143− criterion >= 1.0,
144− deepseq,
145− ghc-prim,
146− text
147− else
148− -- Disabled because of inaccessible internals
149− buildable: False
79+ ghc-options: -O2 -Wall
80+ cpp-options: -DGENERICS
15081
15182executable aeson-benchmark-compare
152− default-language: Haskell2010
15383 main-is: Compare.hs
15484 hs-source-dirs: ../examples .
15585 ghc-options: -Wall -O2 -rtsopts
15686 other-modules:
15787 Compare.BufferBuilder
15888 Compare.JsonBench
89+ Compare.JsonBuilder
15990 Twitter
16091 Twitter.Manual
16192 Typed.Common
@@ -168,19 +99,10 @@ executable aeson-benchmark-compare
16899 criterion >= 1.0,
169100 deepseq,
170101 ghc-prim,
102+ json-builder,
171103 text
172104
173− if impl(ghc < 8.4)
174− other-modules:
175− Compare.JsonBuilder
176− build-depends:
177− json-builder
178− if impl(ghc < 8.0)
179− build-depends:
180− semigroups
181−
182105executable aeson-benchmark-micro
183− default-language: Haskell2010
184106 main-is: Micro.hs
185107 hs-source-dirs: ../examples .
186108 ghc-options: -Wall -O2 -rtsopts
@@ -195,10 +117,12 @@ executable aeson-benchmark-micro
195117 text
196118
197119executable aeson-benchmark-typed
198− default-language: Haskell2010
199120 main-is: Typed.hs
200121 hs-source-dirs: ../examples .
201122 ghc-options: -Wall -O2 -rtsopts
123+ -- We must help ourself in situations when there is both
124+ -- aeson and aeson-benchmakrs
125+ cpp-options: -DHAS_BOTH_AESON_AND_BENCHMARKS
202126 other-modules:
203127 Twitter
204128 Twitter.Generic
@@ -210,6 +134,7 @@ executable aeson-benchmark-typed
210134 Typed.Manual
211135 Typed.TH
212136 build-depends:
137+ aeson,
213138 aeson-benchmarks,
214139 base,
215140 base-compat,
@@ -224,14 +149,13 @@ executable aeson-benchmark-typed
224149 bytestring-builder >= 0.10.4 && < 1
225150 else
226151 build-depends: bytestring >= 0.10.4
227− if impl(ghc < 8.0)
228− build-depends: semigroups
229152
230153executable aeson-benchmark-compare-with-json
231− default-language: Haskell2010
232154 main-is: CompareWithJSON.hs
233155 ghc-options: -Wall -O2 -rtsopts
156+ cpp-options: -DHAS_BOTH_AESON_AND_BENCHMARKS
234157 build-depends:
158+ aeson,
235159 aeson-benchmarks,
236160 base,
237161 base-compat,
@@ -243,7 +167,6 @@ executable aeson-benchmark-compare-with-json
243167 text
244168
245169executable aeson-benchmark-aeson-encode
246− default-language: Haskell2010
247170 main-is: AesonEncode.hs
248171 ghc-options: -Wall -O2 -rtsopts
249172 build-depends:
@@ -256,7 +179,6 @@ executable aeson-benchmark-aeson-encode
256179 time
257180
258181executable aeson-benchmark-aeson-parse
259− default-language: Haskell2010
260182 main-is: AesonParse.hs
261183 ghc-options: -Wall -O2 -rtsopts
262184 build-depends:
@@ -268,7 +190,6 @@ executable aeson-benchmark-aeson-parse
268190 time
269191
270192executable aeson-benchmark-json-parse
271− default-language: Haskell2010
272193 main-is: JsonParse.hs
273194 ghc-options: -Wall -O2 -rtsopts
274195 build-depends:
@@ -279,7 +200,6 @@ executable aeson-benchmark-json-parse
279200 time
280201
281202executable aeson-benchmark-dates
282− default-language: Haskell2010
283203 main-is: Dates.hs
284204 ghc-options: -Wall -O2 -rtsopts
285205 build-depends:
@@ -291,14 +211,12 @@ executable aeson-benchmark-dates
291211 aeson-benchmarks,
292212 text,
293213 time
294− if impl(ghc >= 8.2)
295− ghc-options: -Wno-simplifiable-class-constraints
296214
297215executable aeson-benchmark-map
298− default-language: Haskell2010
299216 main-is: AesonMap.hs
300217 ghc-options: -Wall -O2 -rtsopts
301218 build-depends:
219+ aeson,
302220 aeson-benchmarks,
303221 base,
304222 base-compat,
@@ -312,10 +230,10 @@ executable aeson-benchmark-map
312230 unordered-containers
313231
314232executable aeson-benchmark-foldable
315− default-language: Haskell2010
316233 main-is: AesonFoldable.hs
317234 ghc-options: -Wall -O2 -rtsopts
318235 build-depends:
236+ aeson,
319237 aeson-benchmarks,
320238 base,
321239 base-compat,
@@ -328,18 +246,3 @@ executable aeson-benchmark-foldable
328246 text,
329247 unordered-containers,
330248 vector
331−
332−executable aeson-issue-673
333− default-language: Haskell2010
334− main-is: Issue673.hs
335− ghc-options: -Wall -O1 -rtsopts
336− build-depends:
337− aeson-benchmarks,
338− attoparsec,
339− base,
340− bytestring,
341− scientific,
342− base-compat,
343− criterion >= 1.0
344− if impl(ghc < 8.0)
345− build-depends: semigroups
cabal.project−3
@@ -1,5 +1,2 @@
11with-compiler: ghc
22packages: .
3−packages: attoparsec-iso8601
4−-- packages: benchmarks
5−tests: true
changelog.md+1−154
@@ -1,159 +1,6 @@
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
4−
5−* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− standard `{numerator=..., denumerator=...}` encoding.
7−
8−### 1.4.6.0
9−
10−* Provide a clearer error message when a required tagKey for a constructor is missing, thanks to Guru Devanla.
11− The error message now looks like this: `Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found`
12−
13−* Add `formatPath` and `formatRelativePath` functions to turn a `JSONPath` into a `String`, thanks to Robbie McMichael
14−
15−
16−### 1.4.5.0
17−
18−* Expose `(<?>)`, `JSONPath` and `JSONPathElement(..)` from `Data.Aeson.Types`. Previously only available through internal modules. Thanks to Luke Clifton.
19−
20−* Support for base-compat 0.11, thanks to Ryan Scott.
21−
22−* Travis build for GHC 8.8, thanks to Oleg Grenrus.
23−
24−### 1.4.4.0
25−
26−**New features**:
27−
28−* Adds a parameterized parser `jsonWith` that can be used to choose how to handle duplicate keys in objects, thanks to Xia Li-Yao.
29−
30−* Add generic implementations of `FromJSONKey` and `ToJSONKey`, thanks to Xia Li-Yao. Example:
31−
32−```haskell
33−data Foo = Bar
34− deriving Generic
35−
36−opts :: JSONKeyOptions
37−opts = defaultJSONKeyOptions { keyModifier = toLower }
38−
39−instance ToJSONKey Foo where
40− toJSONKey = genericToJSONKey opts
41−
42−instance FromJSONKey Foo where
43− fromJSONKey = genericFromJSONKey opts
44−```
45−
46−**Minor**:
47−* aeson now uses `time-compat` instead of `time-locale-compat`, thanks to Oleg Grenrus.
48−* Prepare for `MonadFail` breakages in GHC 8.8, thanks to Oleg Grenrus.
49−* Require `bytestring >= 0.10.8.1` for newer GHCs to avoid build failures, thanks to Oleg Grenrus.
50−* Support `primitive 0.7.*`, thanks to Adam Bergmark.
51−* Allow `semigroups 0.19.*` and `hashable 1.3.*`, thanks to Oleg Grenrus.
52−* Fix a typo in the error message when parsing `NonEmpty`, thanks to Colin Woodbury.
53−* Document surprising behavior when using `omitNothingFields` with type variables, thanks to Xia Li-Yao.
54−
55−**Internal changes**:
56−* Code cleanup by Oleg Grenrus
57−* Fix dependencies of the benchmarks on older GHC's, thanks to Xia Li-Yao.
58−
59−### 1.4.3.0
60−* Improve error messages for FromJSON in existing instances and GHC Generic implementation. Thanks to Xia Li-Yao & Igor Pashev.
61−* Tweak error-reporting combinators and their documentation. Thanks to Xia Li-Yao.
62− * `typeMismatch` is now about comparing JSON types (i.e., the expected and actual names of the Value constructor).
63− * `withObject` and other `with*` combinators now also mention the JSON types they expect
64− * New `unexpected` and `prependFailure` combinators.
65−* Add `Contravariant` `ToJSONKeyFunction` instance. Thanks to Oleg Grenrus.
66−* Add `KeyValue` instance for `Object`. Thanks to Robert Hensing.
67−* Improve performance when parsing certain large numbers, thanks to Oleg Grenrus.
68−* Add `Data.Aeson.QQ.Simple` - A limited version of aeson-qq. Thanks to Oleg Grenrus.
69−* Exposes internal helper functions like `<?>`, `JSONPath`, and `parseIndexedJSON` from `Data.Aeson` module. Thanks to Abid Uzair.
70−* Better error messages when there are syntax errors parsing objects and arrays. Thanks to Fintan Halpenny.
71−* Support building with `th-abstraction-0.3.0.0` or later. Thanks to Ryan Scott.
72−
73−### 1.4.2.0
74−
75−* Add `Data.Aeson.QQ.Simple` which is a simpler version of the `aeson-qq` package, it does not support interpolation, thanks to Oleg Grenrus.
76−* Add `Contravariant ToJSONKeyFunction` instance, thanks to Oleg Grenrus.
77−* Add `KeyValue Object` instance, thanks to Robert Hensing
78−* Improved performance when parsing large numbers, thanks to Oleg Grenrus.
79−
80−### 1.4.1.0
81−
82−* Optimizations of generics, thanks to Rémy Oudompheng, here are some numbers for GHC 8.4:
83− * Compilation time: G/BigProduct.hs is 25% faster, G/BigRecord.hs is 2x faster.
84− * Runtime performance: BigRecord/toJSON/generic and BigProduct/encode/generic are more than 2x faster.
85−* Added To/FromJSON instances for `Void` and Generics's `V1`, thanks to Will Yager
86−* Added To/FromJSON instances for `primitive`'s `Array`, `SmallArray`, `PrimArray` and `UnliftedArray`, thanks to Andrew Thad.
87−* Fixes handling of `UTCTime` wrt. leap seconds , thanks to Adam Schønemann
88−* Warning and documentation fixes thanks to tom-bop, Gabor Greif, Ian Jeffries, and Mateusz Curyło.
89−
90−## 1.4.0.0
91−
92−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.
93−
94−This means that trying to decode a number such as `1e1000000000` into an `Integer` will now fail instead of using a lot of memory. If you need to represent large numbers you can add a newtype (preferably over `Scientific`) and providing a parser using `withScientific`.
95−
96−The following instances are affected by this:
97−* `FromJSON Natural`
98−* `FromJSONKey Natural`
99−* `FromJSON Integer`
100−* `FromJSONKey Integer`
101−* `FromJSON NominalDiffTime`
102−
103−For the same reasons the following instances & functions have been removed:
104−* Remove `FromJSON Data.Attoparsec.Number` instance. Note that `Data.Attoparsec.Number` is deprecated.
105−* Remove deprecated `withNumber`, use `withScientific` instead.
106−
107−Finally, encoding integral values with large exponents now uses scientific notation, this saves space for large numbers.
108−
109−#### 1.3.1.1
110−
111−* Catch 0 denominators when parsing Ratio
112−
113−### 1.3.1.0
114−
115−* Fix bug in generically derived `FromJSON` instances that are using `unwrapUnaryRecords`, thanks to Xia Li-yao
116−* Allow base-compat 0.10.*, thanks to Oleg Grenrus
117−
118−## 1.3.0.0
119−
120−Breaking changes:
121−* `GKeyValue` has been renamed to `KeyValuePair`, thanks to Xia Li-yao
122−* Removed unused `FromJSON` constraint in `withEmbeddedJson`, thanks to Tristan Seligmann
123−
124−Other improvements:
125−* Optimizations of TH toEncoding, thanks to Xia Li-yao
126−* Optimizations of hex decoding when using the default/pure unescape implementation, thanks to Xia Li-yao
127−* Improved error message on `Day` parse failures, thanks to Gershom Bazerman
128−* Add `encodeFile` as well as `decodeFile*` variants, thanks to Markus Hauck
129−* Documentation fixes, thanks to Lennart Spitzner
130−* CPP cleanup, thanks to Ryan Scott
131−
132−### 1.2.4.0
133−
134−* Add `Ord` instance for `JSONPathElement`, thanks to Simon Hengel.
135−
136−
137−### 1.2.3.0
138−
139−* Added `withEmbeddedJSON` to help parse JSON embedded inside a JSON string, thanks to Jesse Kempf.
140−* Memory usage improvements to the default (pure) parser, thanks to Jonathan Paugh. Also thanks to Neil Mitchell & Oleg Grenrus for contributing a benchmark.
141−* `omitNothingFields` now works for the `Option` newtype, thanks to Xia Li-yao.
142−* Some documentation fixes, thanks to Jonathan Paug & Philippe Crama.
143−
144−### 1.2.2.0
145−
146−* Add `FromJSON` and `ToJSON` instances for
147− * `DiffTime`, thanks to Víctor López Juan.
148− * `CTime`, thanks to Daniel Díaz.
149−* Fix handling of fractions when parsing Natural, thanks to Yuriy Syrovetskiy.
150−* Change text in error messages for Integral types to make them follow the common pattern, thanks to Yuriy Syrovetskiy.
151−* Add missing `INCOHERENT` pragma for `RecordToPair`, thanks to Xia Li-yao.
152−* Everything related to `Options` is now exported from `Data.Aeson`, thanks to Xia Li-yao.
153−* Optimizations to not escape text in clear cases, thanks to Oleg Grenrus.
154−* Some documentation fixes, thanks to Phil de Joux & Xia Li-yao.
155−
156−### 1.2.1.0
3+## 1.2.1.0
1574
1585* Add `parserThrowError` and `parserCatchError` combinators, thanks to Oleg Grenrus.
1596
examples/Generic.hs+1−1
@@ -6,11 +6,11 @@
66-- automatically derive the Generic class for us.
77
88{-# LANGUAGE DeriveGeneric #-}
9−{-# LANGUAGE NoImplicitPrelude #-}
109{-# LANGUAGE OverloadedStrings #-}
1110
1211module Main (main) where
1312
13+import Prelude ()
1414import Prelude.Compat
1515
1616import Data.Aeson (FromJSON, ToJSON, decode, encode)
examples/Simplest.hs+1−1
@@ -1,9 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE OverloadedStrings #-}
32{-# LANGUAGE RecordWildCards #-}
43
54module Main (main) where
65
6+import Prelude ()
77import Prelude.Compat
88
99import Control.Applicative (empty)
examples/TemplateHaskell.hs+1−1
@@ -1,12 +1,12 @@
11-- We can use Template Haskell (TH) to generate instances of the
22-- FromJSON and ToJSON classes automatically. This is the fastest way
33-- to add JSON support for a type.
4−{-# LANGUAGE NoImplicitPrelude #-}
54{-# LANGUAGE OverloadedStrings #-}
65{-# LANGUAGE TemplateHaskell #-}
76
87module Main (main) where
98
9+import Prelude ()
1010import Prelude.Compat
1111
1212import Data.Aeson (decode, encode)
examples/Twitter.hs+2−1
@@ -10,7 +10,6 @@
1010
1111{-# LANGUAGE DeriveDataTypeable #-}
1212{-# LANGUAGE DeriveGeneric #-}
13−{-# LANGUAGE NoImplicitPrelude #-}
1413
1514module Twitter
1615 (
@@ -20,6 +19,7 @@ module Twitter
2019 , Result(..)
2120 ) where
2221
22+import Prelude ()
2323import Prelude.Compat
2424
2525import Control.DeepSeq
@@ -27,6 +27,7 @@ import Data.Data (Typeable, Data)
2727import Data.Int (Int64)
2828import Data.Text (Text)
2929import GHC.Generics (Generic)
30+import Prelude hiding (id)
3031
3132{-# ANN module "Hlint: ignore Use camelCase" #-}
3233{-# ANN module "Hlint: ignore Use newtype instead of data" #-}
examples/Twitter/Generic.hs+37−2
@@ -1,9 +1,12 @@
11-- Use GHC generics to automatically generate good instances.
22
3−{-# LANGUAGE NoImplicitPrelude #-}
4−
3+{-# LANGUAGE CPP #-}
54{-# OPTIONS_GHC -fno-warn-orphans #-}
65
6+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
7+{-# LANGUAGE PackageImports #-}
8+#endif
9+
710module Twitter.Generic
811 (
912 Metadata(..)
@@ -12,12 +15,18 @@ module Twitter.Generic
1215 , Result(..)
1316 ) where
1417
18+import Prelude ()
1519import Prelude.Compat ()
1620
1721import Twitter
1822import Twitter.Options
1923
24+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
2025import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
26+#else
27+import "aeson" Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
28+import qualified "aeson-benchmarks" Data.Aeson as B
29+#endif
2130
2231instance ToJSON Metadata where
2332 toJSON = genericToJSON twitterOptions
@@ -42,3 +51,29 @@ instance ToJSON Result where
4251 toEncoding = genericToEncoding twitterOptions
4352instance FromJSON Result where
4453 parseJSON = genericParseJSON twitterOptions
54+
55+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
56+instance B.ToJSON Metadata where
57+ toJSON = B.genericToJSON btwitterOptions
58+ toEncoding = B.genericToEncoding btwitterOptions
59+instance B.FromJSON Metadata where
60+ parseJSON = B.genericParseJSON btwitterOptions
61+
62+instance B.ToJSON Geo where
63+ toJSON = B.genericToJSON btwitterOptions
64+ toEncoding = B.genericToEncoding btwitterOptions
65+instance B.FromJSON Geo where
66+ parseJSON = B.genericParseJSON btwitterOptions
67+
68+instance B.ToJSON Story where
69+ toJSON = B.genericToJSON btwitterOptions
70+ toEncoding = B.genericToEncoding btwitterOptions
71+instance B.FromJSON Story where
72+ parseJSON = B.genericParseJSON btwitterOptions
73+
74+instance B.ToJSON Result where
75+ toJSON = B.genericToJSON btwitterOptions
76+ toEncoding = B.genericToEncoding btwitterOptions
77+instance B.FromJSON Result where
78+ parseJSON = B.genericParseJSON btwitterOptions
79+#endif
examples/Twitter/Manual.hs+139−3
@@ -1,10 +1,14 @@
11-- Manually write instances.
2−{-# LANGUAGE NoImplicitPrelude #-}
2+
3+{-# LANGUAGE CPP #-}
34{-# LANGUAGE OverloadedStrings #-}
45{-# LANGUAGE RecordWildCards #-}
5−
66{-# OPTIONS_GHC -fno-warn-orphans #-}
77
8+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
9+{-# LANGUAGE PackageImports #-}
10+#endif
11+
812module Twitter.Manual
913 (
1014 Metadata(..)
@@ -13,13 +17,20 @@ module Twitter.Manual
1317 , Result(..)
1418 ) where
1519
20+import Prelude ()
1621import Prelude.Compat
1722
1823import Control.Applicative
19−import Data.Semigroup ((<>))
24+import Data.Monoid ((<>))
25+import Prelude hiding (id)
2026import Twitter
2127
28+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
2229import Data.Aeson hiding (Result)
30+#else
31+import "aeson" Data.Aeson hiding (Result)
32+import qualified "aeson-benchmarks" Data.Aeson as B
33+#endif
2334
2435instance ToJSON Metadata where
2536
@@ -144,3 +155,128 @@ instance FromJSON Result where
144155 <*> v .: "max_id_str"
145156 <*> v .: "query"
146157 parseJSON _ = empty
158+
159+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
160+instance B.ToJSON Metadata where
161+ toJSON Metadata{..} = B.object [
162+ "result_type" B..= result_type
163+ ]
164+
165+ toEncoding Metadata{..} = B.pairs $
166+ "result_type" B..= result_type
167+
168+instance B.FromJSON Metadata where
169+ parseJSON (B.Object v) = Metadata <$> v B..: "result_type"
170+ parseJSON _ = empty
171+
172+instance B.ToJSON Geo where
173+ toJSON Geo{..} = B.object [
174+ "type_" B..= type_
175+ , "coordinates" B..= coordinates
176+ ]
177+
178+ toEncoding Geo{..} = B.pairs $
179+ "type_" B..= type_
180+ <> "coordinates" B..= coordinates
181+
182+instance B.FromJSON Geo where
183+ parseJSON (B.Object v) = Geo <$>
184+ v B..: "type_"
185+ <*> v B..: "coordinates"
186+ parseJSON _ = empty
187+
188+instance B.ToJSON Story where
189+ toJSON Story{..} = B.object [
190+ "from_user_id_str" B..= from_user_id_str
191+ , "profile_image_url" B..= profile_image_url
192+ , "created_at" B..= created_at
193+ , "from_user" B..= from_user
194+ , "id_str" B..= id_str
195+ , "metadata" B..= metadata
196+ , "to_user_id" B..= to_user_id
197+ , "text" B..= text
198+ , "id" B..= id_
199+ , "from_user_id" B..= from_user_id
200+ , "geo" B..= geo
201+ , "iso_language_code" B..= iso_language_code
202+ , "to_user_id_str" B..= to_user_id_str
203+ , "source" B..= source
204+ ]
205+
206+ toEncoding Story{..} = B.pairs $
207+ "from_user_id_str" B..= from_user_id_str
208+ <> "profile_image_url" B..= profile_image_url
209+ <> "created_at" B..= created_at
210+ <> "from_user" B..= from_user
211+ <> "id_str" B..= id_str
212+ <> "metadata" B..= metadata
213+ <> "to_user_id" B..= to_user_id
214+ <> "text" B..= text
215+ <> "id" B..= id_
216+ <> "from_user_id" B..= from_user_id
217+ <> "geo" B..= geo
218+ <> "iso_language_code" B..= iso_language_code
219+ <> "to_user_id_str" B..= to_user_id_str
220+ <> "source" B..= source
221+
222+instance B.FromJSON Story where
223+ parseJSON (B.Object v) = Story <$>
224+ v B..: "from_user_id_str"
225+ <*> v B..: "profile_image_url"
226+ <*> v B..: "created_at"
227+ <*> v B..: "from_user"
228+ <*> v B..: "id_str"
229+ <*> v B..: "metadata"
230+ <*> v B..: "to_user_id"
231+ <*> v B..: "text"
232+ <*> v B..: "id"
233+ <*> v B..: "from_user_id"
234+ <*> v B..: "geo"
235+ <*> v B..: "iso_language_code"
236+ <*> v B..: "to_user_id_str"
237+ <*> v B..: "source"
238+ parseJSON _ = empty
239+
240+instance B.ToJSON Result where
241+ toJSON Result{..} = B.object [
242+ "results" B..= results
243+ , "max_id" B..= max_id
244+ , "since_id" B..= since_id
245+ , "refresh_url" B..= refresh_url
246+ , "next_page" B..= next_page
247+ , "results_per_page" B..= results_per_page
248+ , "page" B..= page
249+ , "completed_in" B..= completed_in
250+ , "since_id_str" B..= since_id_str
251+ , "max_id_str" B..= max_id_str
252+ , "query" B..= query
253+ ]
254+
255+ toEncoding Result{..} = B.pairs $
256+ "results" B..= results
257+ <> "max_id" B..= max_id
258+ <> "since_id" B..= since_id
259+ <> "refresh_url" B..= refresh_url
260+ <> "next_page" B..= next_page
261+ <> "results_per_page" B..= results_per_page
262+ <> "page" B..= page
263+ <> "completed_in" B..= completed_in
264+ <> "since_id_str" B..= since_id_str
265+ <> "max_id_str" B..= max_id_str
266+ <> "query" B..= query
267+
268+instance B.FromJSON Result where
269+ parseJSON (B.Object v) = Result <$>
270+ v B..: "results"
271+ <*> v B..: "max_id"
272+ <*> v B..: "since_id"
273+ <*> v B..: "refresh_url"
274+ <*> v B..: "next_page"
275+ <*> v B..: "results_per_page"
276+ <*> v B..: "page"
277+ <*> v B..: "completed_in"
278+ <*> v B..: "since_id_str"
279+ <*> v B..: "max_id_str"
280+ <*> v B..: "query"
281+ parseJSON _ = empty
282+#endif
examples/Twitter/Options.hs+23
@@ -1,6 +1,20 @@
1+{-# LANGUAGE CPP #-}
2+
3+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
4+{-# LANGUAGE PackageImports #-}
5+#endif
6+
17module Twitter.Options (module Twitter.Options) where
28
9+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
310import Data.Aeson
11+import Data.Aeson.Types
12+#else
13+import "aeson" Data.Aeson
14+import "aeson" Data.Aeson.Types
15+import qualified "aeson-benchmarks" Data.Aeson as B
16+import qualified "aeson-benchmarks" Data.Aeson.Types as B
17+#endif
418
519twitterOptions :: Options
620twitterOptions = defaultOptions
@@ -8,3 +22,12 @@ twitterOptions = defaultOptions
822 "id_" -> "id"
923 _ -> x
1024 }
25+
26+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
27+btwitterOptions :: B.Options
28+btwitterOptions = B.defaultOptions
29+ { B.fieldLabelModifier = \x -> case x of
30+ "id_" -> "id"
31+ _ -> x
32+ }
33+#endif
examples/Twitter/TH.hs+18−2
@@ -1,10 +1,13 @@
11-- Use Template Haskell to generate good instances.
22
3−{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE CPP #-}
44{-# LANGUAGE TemplateHaskell #-}
5−
65{-# OPTIONS_GHC -fno-warn-orphans #-}
76
7+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
8+{-# LANGUAGE PackageImports #-}
9+#endif
10+
811module Twitter.TH
912 (
1013 Metadata(..)
@@ -13,13 +16,26 @@ module Twitter.TH
1316 , Result(..)
1417 ) where
1518
19+import Prelude ()
1620
1721import Twitter
1822import Twitter.Options
1923
24+#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
2025import Data.Aeson.TH
26+#else
27+import "aeson" Data.Aeson.TH
28+import qualified "aeson-benchmarks" Data.Aeson.TH as B
29+#endif
2130
2231$(deriveJSON twitterOptions ''Metadata)
2332$(deriveJSON twitterOptions ''Geo)
2433$(deriveJSON twitterOptions ''Story)
2534$(deriveJSON twitterOptions ''Result)
35+
36+#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
37+$(B.deriveJSON btwitterOptions ''Metadata)
38+$(B.deriveJSON btwitterOptions ''Geo)
39+$(B.deriveJSON btwitterOptions ''Story)
40+$(B.deriveJSON btwitterOptions ''Result)
41+#endif
ffi/Data/Aeson/Parser/UnescapeFFI.hs+1−1
@@ -10,7 +10,7 @@ module Data.Aeson.Parser.UnescapeFFI
1010import Control.Exception (evaluate, throw, try)
1111import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
1212import Data.ByteString as B
13−import Data.ByteString.Internal as B
13+import Data.ByteString.Internal as B hiding (c2w)
1414import Data.Text.Encoding.Error (UnicodeException (..))
1515import Data.Text.Internal (Text (..))
1616import Data.Text.Internal.Private (runText)
pure/Data/Aeson/Parser/UnescapePure.hs+36−13
@@ -119,18 +119,38 @@ decode UtfTail1 point word = case word of
119119 w | 0x80 <= w && w <= 0xbf -> (UtfGround, setByte1 point word)
120120 _ -> throwDecodeError
121121
122+{-# INLINE decode #-}
123+
122124decodeHex :: Word8 -> Word16
123−decodeHex x
124− | 48 <= x && x <= 57 = fromIntegral x - 48 -- 0-9
125− | 65 <= x && x <= 70 = fromIntegral x - 55 -- A-F
126− | 97 <= x && x <= 102 = fromIntegral x - 87 -- a-f
127− | otherwise = throwDecodeError
125+decodeHex 48 = 0 -- '0'
126+decodeHex 49 = 1 -- '1'
127+decodeHex 50 = 2 -- '2'
128+decodeHex 51 = 3 -- '3'
129+decodeHex 52 = 4 -- '4'
130+decodeHex 53 = 5 -- '5'
131+decodeHex 54 = 6 -- '6'
132+decodeHex 55 = 7 -- '7'
133+decodeHex 56 = 8 -- '8'
134+decodeHex 57 = 9 -- '9'
135+decodeHex 65 = 10 -- 'A'
136+decodeHex 97 = 10 -- 'a'
137+decodeHex 66 = 11 -- 'B'
138+decodeHex 98 = 11 -- 'b'
139+decodeHex 67 = 12 -- 'C'
140+decodeHex 99 = 12 -- 'c'
141+decodeHex 68 = 13 -- 'D'
142+decodeHex 100 = 13 -- 'd'
143+decodeHex 69 = 14 -- 'E'
144+decodeHex 101 = 14 -- 'e'
145+decodeHex 70 = 15 -- 'F'
146+decodeHex 102 = 15 -- 'f'
147+decodeHex _ = throwDecodeError
148+{-# INLINE decodeHex #-}
128149
129150unescapeText' :: ByteString -> Text
130151unescapeText' bs = runText $ \done -> do
131152 dest <- A.new len
132−
133− (pos, finalState) <- loop dest (0, StateNone) 0
153+ (pos, finalState) <- B.foldl' (f' dest) (return (0, StateNone)) bs
134154
135155 -- Check final state. Currently pos gets only increased over time, so this check should catch overflows.
136156 when ( finalState /= StateNone || pos > len)
@@ -152,12 +172,11 @@ unescapeText' bs = runText $ \done -> do
152172 (st', p) ->
153173 return (pos, StateUtf st' p)
154174
155− loop :: A.MArray s -> (Int, State) -> Int -> ST s (Int, State)
156− loop _ ps i | i >= len = return ps
157− loop dest ps i = do
158− let c = B.index bs i -- JP: We can use unsafe index once we prove bounds with Liquid Haskell.
159− ps' <- f dest ps c
160− loop dest ps' $ i+1
175+ {-# INLINE runUtf #-}
176+
177+ f' dest m c = m >>= \s -> f dest s c
178+
179+ {-# INLINE f' #-}
161180
162181 -- No pending state.
163182 f dest (pos, StateNone) c = runUtf dest pos UtfGround 0 c
@@ -234,6 +253,8 @@ unescapeText' bs = runText $ \done -> do
234253 else
235254 writeAndReturn dest pos u StateNone
236255
256+ {-# INLINE f #-}
257+
237258write :: A.MArray s -> Int -> Word16 -> ST s ()
238259write dest pos char =
239260 A.unsafeWrite dest pos char
@@ -249,6 +270,8 @@ throwDecodeError :: a
249270throwDecodeError =
250271 let desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream" in
251272 throw (DecodeError desc Nothing)
273+{-# INLINE throwDecodeError #-}
252274
253275unescapeText :: ByteString -> Either UnicodeException Text
254276unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
277+{-# INLINE unescapeText #-}
run-hlint.shdeleted−7
@@ -1,7 +0,0 @@
1−#!/bin/bash
2−
3−if hash hlint 2>/dev/null; then
4− hlint "$@"
5−else
6− ./hlint.sh "$@"
7−fi
stack-bench.yaml+5−2
@@ -1,4 +1,4 @@
1−resolver: nightly-2019-09-21
1+resolver: lts-8.5
22# We use aeson in the snapshot to
33# - avoid recompilation of criterion
44# - compare against it
@@ -9,4 +9,7 @@ resolver: nightly-2019-09-21
99work-dir: .stack-work-bench
1010packages:
1111- benchmarks
12−extra-deps: []
12+extra-deps:
13+- aeson-1.2.1.0
14+- quickcheck-instances-0.3.14
15+- th-abstraction-0.2.2.0
stack-ffi-unescape.yamldeleted−9
@@ -1,9 +0,0 @@
1−resolver: nightly-2019-09-21
2−packages:
3−- '.'
4−flags:
5− aeson:
6− fast: true
7− cffi: true
8−extra-deps:
9−- Diff-0.4.0
stack-lts12.yamldeleted−15
@@ -1,15 +0,0 @@
1−resolver: lts-12.26
2−packages:
3−- '.'
4−- attoparsec-iso8601
5−flags:
6− aeson:
7− fast: true
8− cffi: true
9−extra-deps:
10−- base-orphans-0.8.1
11−- hashable-time-0.2.0.1
12−- QuickCheck-2.13.1
13−- quickcheck-instances-0.3.21
14−- splitmix-0.0.2
15−- time-compat-1.9.2.2
stack-lts6.yamlnew file+20
@@ -0,0 +1,20 @@
1+resolver: lts-6.30
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- integer-logarithms-1
7+- QuickCheck-2.9.2
8+- quickcheck-instances-0.3.14
9+- semigroups-0.18.2
10+- tagged-0.8.5
11+- th-abstraction-0.2.2.0
12+- transformers-compat-0.5.1.4
13+flags:
14+flags:
15+ aeson:
16+ fast: true
17+ attoparsec-iso8601:
18+ fast: true
19+ semigroups:
20+ bytestring-builder: false
stack-lts7.yamlnew file+13
@@ -0,0 +1,13 @@
1+resolver: lts-7.19
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- integer-logarithms-1
7+- quickcheck-instances-0.3.14
8+- th-abstraction-0.2.2.0
9+flags:
10+ aeson:
11+ fast: true
12+ attoparsec-iso8601:
13+ fast: true
stack-lts8.yamlnew file+12
@@ -0,0 +1,12 @@
1+resolver: lts-8.5
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+extra-deps:
6+- quickcheck-instances-0.3.14
7+- th-abstraction-0.2.2.0
8+flags:
9+ aeson:
10+ fast: true
11+ attoparsec-iso8601:
12+ fast: true
stack-nightly.yaml+4−3
@@ -1,11 +1,12 @@
1−resolver: nightly-2019-10-14
1+resolver: nightly-2017-03-25
22packages:
33- '.'
44- attoparsec-iso8601
5+extra-deps:
6+- quickcheck-instances-0.3.14
7+- th-abstraction-0.2.2.0
58flags:
69 aeson:
710 fast: true
811 attoparsec-iso8601:
912 fast: true
10−extra-deps:
11−- generic-deriving-1.12.4
stack-pure-unescape.yamlnew file+10
@@ -0,0 +1,10 @@
1+resolver: lts-8.1
2+packages:
3+- '.'
4+extra-deps:
5+- quickcheck-instances-0.3.14
6+- th-abstraction-0.2.2.0
7+flags:
8+ aeson:
9+ fast: true
10+ cffi: false
tests/DataFamilies/Encoders.hs+1−1
@@ -1,10 +1,10 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
43{-# LANGUAGE TemplateHaskell #-}
54
65module DataFamilies.Encoders (module DataFamilies.Encoders) where
76
7+import Prelude ()
88import Prelude.Compat
99
1010import Data.Aeson.TH
tests/DataFamilies/Instances.hs+1−2
@@ -1,13 +1,12 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
43{-# LANGUAGE TemplateHaskell #-}
54{-# LANGUAGE TypeFamilies #-}
6−
75{-# OPTIONS_GHC -fno-warn-orphans #-}
86
97module DataFamilies.Instances () where
108
9+import Prelude ()
1110import Prelude.Compat
1211
1312import Data.Aeson.TH
tests/DataFamilies/Properties.hs+5−6
@@ -1,21 +1,20 @@
11{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
32
43module DataFamilies.Properties (tests) where
54
5+import Prelude ()
66import Prelude.Compat
77
88import DataFamilies.Encoders
99import DataFamilies.Instances ()
10−import PropUtils
10+import Properties hiding (tests)
1111
12−
13−import Test.Tasty (TestTree, testGroup)
14−import Test.Tasty.QuickCheck (testProperty)
12+import Test.Framework (Test, testGroup)
13+import Test.Framework.Providers.QuickCheck2 (testProperty)
1514
1615--------------------------------------------------------------------------------
1716
18−tests :: TestTree
17+tests :: Test
1918tests = testGroup "data families" [
2019 testGroup "template-haskell" [
2120 testGroup "toJSON" [
tests/DataFamilies/Types.hs+1−1
@@ -4,13 +4,13 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE GADTs #-}
66{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
87{-# LANGUAGE StandaloneDeriving #-}
98{-# LANGUAGE TemplateHaskell #-}
109{-# LANGUAGE TypeFamilies #-}
1110
1211module DataFamilies.Types (module DataFamilies.Types) where
1312
13+import Prelude ()
1414import Prelude.Compat
1515
1616import Generics.Deriving.TH (deriveAll0)
tests/Encoders.hs+7−76
@@ -1,12 +1,11 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
43{-# LANGUAGE TemplateHaskell #-}
54
65module Encoders (module Encoders) where
76
7+import Prelude ()
88import Prelude.Compat
9−import Data.Text (Text)
109
1110import Data.Aeson.TH
1211import Data.Aeson.Types
@@ -97,18 +96,6 @@ gNullaryToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingle
9796gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
9897gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
9998
100−keyOptions :: JSONKeyOptions
101−keyOptions = defaultJSONKeyOptions { keyModifier = ('k' :) }
102−
103−gNullaryToJSONKey :: Nullary -> Either String Text
104−gNullaryToJSONKey x = case genericToJSONKey keyOptions of
105− ToJSONKeyText p _ -> Right (p x)
106− _ -> Left "Should be a ToJSONKeyText"
107−
108−gNullaryFromJSONKey :: Text -> Parser Nullary
109−gNullaryFromJSONKey t = case genericFromJSONKey keyOptions of
110− FromJSONKeyTextParser p -> p t
111− _ -> fail "Not a TextParser"
11299
113100--------------------------------------------------------------------------------
114101-- SomeType encoders/decoders
@@ -188,6 +175,7 @@ gSomeTypeToEncoding2ElemArray = genericToEncoding opts2ElemArray
188175gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
189176gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
190177
178+#if __GLASGOW_HASKELL__ >= 706
191179gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
192180gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
193181
@@ -196,6 +184,7 @@ gSomeTypeLiftToJSON2ElemArray = genericLiftToJSON opts2ElemArray
196184
197185gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
198186gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
187+#endif
199188
200189
201190gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
@@ -207,6 +196,7 @@ gSomeTypeToEncodingTaggedObject = genericToEncoding optsTaggedObject
207196gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
208197gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
209198
199+#if __GLASGOW_HASKELL__ >= 706
210200gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
211201gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
212202
@@ -215,6 +205,7 @@ gSomeTypeLiftToJSONTaggedObject = genericLiftToJSON optsTaggedObject
215205
216206gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
217207gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
208+#endif
218209
219210
220211gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
@@ -226,6 +217,7 @@ gSomeTypeToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingl
226217gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
227218gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
228219
220+#if __GLASGOW_HASKELL__ >= 706
229221gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
230222gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
231223
@@ -234,6 +226,7 @@ gSomeTypeLiftToJSONObjectWithSingleField = genericLiftToJSON optsObjectWithSingl
234226
235227gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
236228gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
229+#endif
237230
238231
239232gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
@@ -242,58 +235,6 @@ gSomeTypeToJSONOmitNothingFields = genericToJSON optsOmitNothingFields
242235gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
243236gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
244237
245−
246−thSomeTypeParseJSONRejectUnknownFields :: Value -> Parser (SomeType Int)
247−thSomeTypeParseJSONRejectUnknownFields = $(mkParseJSON optsRejectUnknownFields ''SomeType)
248−
249−gSomeTypeParseJSONRejectUnknownFields :: Value -> Parser (SomeType Int)
250−gSomeTypeParseJSONRejectUnknownFields = genericParseJSON optsRejectUnknownFields
251−
252−
253−--------------------------------------------------------------------------------
254−-- Foo decoders
255−--------------------------------------------------------------------------------
256−
257−thFooParseJSONRejectUnknownFields :: Value -> Parser Foo
258−thFooParseJSONRejectUnknownFields = $(mkParseJSON optsRejectUnknownFields ''Foo)
259−
260−gFooParseJSONRejectUnknownFields :: Value -> Parser Foo
261−gFooParseJSONRejectUnknownFields = genericParseJSON optsRejectUnknownFields
262−
263−
264−thFooParseJSONRejectUnknownFieldsTagged :: Value -> Parser Foo
265−thFooParseJSONRejectUnknownFieldsTagged = $(mkParseJSON optsRejectUnknownFieldsTagged ''Foo)
266−
267−gFooParseJSONRejectUnknownFieldsTagged :: Value -> Parser Foo
268−gFooParseJSONRejectUnknownFieldsTagged = genericParseJSON optsRejectUnknownFieldsTagged
269−
270−
271−--------------------------------------------------------------------------------
272−-- Option fields
273−--------------------------------------------------------------------------------
274−
275−thOptionFieldToJSON :: OptionField -> Value
276−thOptionFieldToJSON = $(mkToJSON optsOptionField 'OptionField)
277−
278−thOptionFieldToEncoding :: OptionField -> Encoding
279−thOptionFieldToEncoding = $(mkToEncoding optsOptionField 'OptionField)
280−
281−thOptionFieldParseJSON :: Value -> Parser OptionField
282−thOptionFieldParseJSON = $(mkParseJSON optsOptionField 'OptionField)
283−
284−gOptionFieldToJSON :: OptionField -> Value
285−gOptionFieldToJSON = genericToJSON optsOptionField
286−
287−gOptionFieldToEncoding :: OptionField -> Encoding
288−gOptionFieldToEncoding = genericToEncoding optsOptionField
289−
290−gOptionFieldParseJSON :: Value -> Parser OptionField
291−gOptionFieldParseJSON = genericParseJSON optsOptionField
292−
293−thMaybeFieldToJSON :: MaybeField -> Value
294−thMaybeFieldToJSON = $(mkToJSON optsOptionField 'MaybeField)
295−
296−
297238--------------------------------------------------------------------------------
298239-- IncoherentInstancesNeeded
299240--------------------------------------------------------------------------------
@@ -446,13 +387,3 @@ gOneConstructorToEncodingTagged = genericToEncoding optsTagSingleConstructors
446387
447388gOneConstructorParseJSONTagged :: Value -> Parser OneConstructor
448389gOneConstructorParseJSONTagged = genericParseJSON optsTagSingleConstructors
449−
450−--------------------------------------------------------------------------------
451−-- Product2 encoders/decoders
452−--------------------------------------------------------------------------------
453−
454−thProduct2ParseJSON :: Value -> Parser (Product2 Int Bool)
455−thProduct2ParseJSON = $(mkParseJSON defaultOptions ''Product2)
456−
457−gProduct2ParseJSON :: Value -> Parser (Product2 Int Bool)
458−gProduct2ParseJSON = genericParseJSON defaultOptions
tests/ErrorMessages.hs+41−208
@@ -1,4 +1,3 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
21{-# LANGUAGE OverloadedStrings #-}
32{-# LANGUAGE ScopedTypeVariables #-}
43
@@ -7,218 +6,52 @@ module ErrorMessages
76 tests
87 ) where
98
9+import Prelude ()
1010import Prelude.Compat
1111
12−import Data.Aeson (FromJSON(..), Value, json)
13−import Data.Aeson.Types (Parser)
14−import Data.Aeson.Parser (eitherDecodeWith)
15−import Data.Aeson.Internal (formatError, iparse)
16−import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff)
12+import Data.Aeson (FromJSON(..), eitherDecode)
1713import Data.Proxy (Proxy(..))
18−import Data.Semigroup ((<>))
19−import Data.Sequence (Seq)
2014import Instances ()
21−import Numeric.Natural (Natural)
22−import Test.Tasty (TestTree, TestName)
23−import Test.Tasty.Golden.Advanced (goldenTest)
15+import Test.Framework (Test)
16+import Test.Framework.Providers.HUnit (testCase)
17+import Test.HUnit (Assertion, assertFailure, assertEqual)
2418import qualified Data.ByteString.Lazy.Char8 as L
2519import qualified Data.HashMap.Strict as HM
2620
27−import Encoders
28−import Types
29−
30−tests :: [TestTree]
21+tests :: [Test]
3122tests =
32− [ aesonGoldenTest "simple" "tests/golden/simple.expected" output
33− , aesonGoldenTest "generic" "tests/golden/generic.expected" (outputGeneric G)
34− ]
35−
36−output :: Output
37−output = concat
38− [ testFor "Int" (Proxy :: Proxy Int)
39− [ "\"\""
40− , "[]"
41− , "{}"
42− , "null"
43− ]
44−
45− , testFor "Integer" (Proxy :: Proxy Integer)
46− [ "44.44"
47− ]
48−
49− , testFor "Natural" (Proxy :: Proxy Natural)
50− [ "44.44"
51− , "-50"
52− ]
53−
54− , testFor "String" (Proxy :: Proxy String)
55− [ "1"
56− , "[]"
57− , "{}"
58− , "null"
59− ]
60−
61− , testFor "HashMap" (Proxy :: Proxy (HM.HashMap String Int))
62− [ "\"\""
63− , "[]"
64− ]
65−
66− -- issue #356
67− , testFor "Either" (Proxy :: Proxy (Int, Either (Int, Bool) ()))
68− [ "[1,{\"Left\":[2,3]}]"
69− ]
70−
71− -- issue #358
72− , testFor "Seq" (Proxy :: Proxy (Seq Int))
73− [ "[0,1,true]"
74− ]
75− ]
76−
77−data Choice = TH | G
78−
79−outputGeneric :: Choice -> Output
80−outputGeneric choice = concat
81− [ testWith "OneConstructor"
82− (select
83− thOneConstructorParseJSONDefault
84− gOneConstructorParseJSONDefault)
85− [ "\"X\""
86− , "[0]"
87− ]
88−
89− , testWith "Nullary"
90− (select
91− thNullaryParseJSONString
92− gNullaryParseJSONString)
93− [ "\"X\""
94− , "[]"
95− ]
96−
97− , testWithSomeType "SomeType (tagged)"
98− (select
99− thSomeTypeParseJSONTaggedObject
100− gSomeTypeParseJSONTaggedObject)
101− [ "{\"tag\": \"unary\", \"contents\": true}"
102− , "{\"tag\": \"unary\"}"
103− , "{\"tag\": \"record\"}"
104− , "{\"tag\": \"record\", \"testone\": true, \"testtwo\": null, \"testthree\": null}"
105− , "{\"tag\": \"X\"}"
106− , "{}"
107− , "[]"
108− ]
109−
110− , testWithSomeType "SomeType (single-field)"
111− (select
112− thSomeTypeParseJSONObjectWithSingleField
113− gSomeTypeParseJSONObjectWithSingleField)
114− [ "{\"unary\": {}}"
115− , "{\"unary\": []}"
116− , "{\"X\": []}"
117− , "{\"record\": {}, \"W\":{}}"
118− , "{}"
119− , "[]"
120− , "{\"unary\""
121− , "{\"unary\":"
122− , "{\"unary\":1"
123− ]
124−
125− , testWithSomeType "SomeType (two-element array)"
126− (select
127− thSomeTypeParseJSON2ElemArray
128− gSomeTypeParseJSON2ElemArray)
129− [ "[\"unary\", true]"
130− , "[\"record\", null]"
131− , "[\"X\", 0]"
132− , "[null, 0]"
133− , "[]"
134− , "{}"
135− , "[1"
136− , "[1,"
137− ]
138−
139− , testWithSomeType "SomeType (reject unknown fields)"
140− (select
141− thSomeTypeParseJSONRejectUnknownFields
142− gSomeTypeParseJSONRejectUnknownFields)
143− [ "{\"tag\": \"record\", \"testOne\": 1.0, \"testZero\": 1}"
144− , "{\"testZero\": 1}"
145− , "{\"tag\": \"record\", \"testone\": true, \"testtwo\": null, \"testthree\": null}"
146− ]
147−
148− , testWithFoo "Foo (reject unknown fields)"
149− (select
150− thFooParseJSONRejectUnknownFields
151− gFooParseJSONRejectUnknownFields)
152− [ "{\"tag\": \"foo\"}"
153− ]
154−
155− , testWithFoo "Foo (reject unknown fields, tagged single)"
156− (select
157− thFooParseJSONRejectUnknownFieldsTagged
158− gFooParseJSONRejectUnknownFieldsTagged)
159− [ "{\"tag\": \"foo\", \"unknownField\": 0}"
160− ]
161−
162− , testWith "EitherTextInt"
163− (select
164− thEitherTextIntParseJSONUntaggedValue
165− gEitherTextIntParseJSONUntaggedValue)
166− [ "\"X\""
167− , "[]"
168− ]
169−
170− , testWith "Product2 Int Bool"
171− (select
172− thProduct2ParseJSON
173− gProduct2ParseJSON)
174− [ "[1, null]"
175− , "[]"
176− , "{}"
177− ]
178− ]
179− where
180− select a b = case choice of
181− TH -> a
182− G -> b
183−
184−-- Test infrastructure
185−
186−type Output = [String]
187−
188−outputLine :: String -> Output
189−outputLine = pure
190−
191−aesonGoldenTest :: TestName -> FilePath -> Output -> TestTree
192−aesonGoldenTest name ref out = goldenTest name (L.readFile ref) act cmp upd
193− where
194− act = pure (L.pack (unlines out))
195− upd = L.writeFile ref
196− cmp x y | x == y = return Nothing
197− cmp x y = return $ Just $ unlines $
198− concatMap f (getGroupedDiff (L.lines x) (L.lines y))
199− where
200− f (First xs) = map (cons3 '-' . L.unpack) xs
201− f (Second ys) = map (cons3 '+' . L.unpack) ys
202− -- we print unchanged lines too. It shouldn't be a problem while we have
203− -- reasonably small examples
204− f (Both xs _) = map (cons3 ' ' . L.unpack) xs
205− -- we add three characters, so the changed lines are easier to spot
206− cons3 c cs = c : c : c : ' ' : cs
207−
208−testWith :: Show a => String -> (Value -> Parser a) -> [L.ByteString] -> Output
209−testWith name parser ts =
210− outputLine name <>
211− foldMap (\s ->
212− case eitherDecodeWith json (iparse parser) s of
213− Left err -> outputLine $ uncurry formatError err
214− Right a -> outputLine $ show a) ts
215−
216−testFor :: forall a proxy. (FromJSON a, Show a)
217− => String -> proxy a -> [L.ByteString] -> Output
218−testFor name _ = testWith name (parseJSON :: Value -> Parser a)
219−
220−testWithSomeType :: String -> (Value -> Parser (SomeType Int)) -> [L.ByteString] -> Output
221−testWithSomeType = testWith
222−
223−testWithFoo :: String -> (Value -> Parser Foo) -> [L.ByteString] -> Output
224−testWithFoo = testWith
23+ [
24+ testCase "Int" int
25+ , testCase "String" string
26+ , testCase "HashMap" hashMap
27+ ]
28+
29+int :: Assertion
30+int = do
31+ let t = test (Proxy :: Proxy Int)
32+ t "\"\"" $ expected "Int" "String"
33+ t "[]" $ expected "Int" "Array"
34+ t "{}" $ expected "Int" "Object"
35+ t "null" $ expected "Int" "Null"
36+
37+string :: Assertion
38+string = do
39+ let t = test (Proxy :: Proxy String)
40+ t "1" $ expected "String" "Number"
41+ t "[]" $ expected "String" "Array"
42+ t "{}" $ expected "String" "Object"
43+ t "null" $ expected "String" "Null"
44+
45+hashMap :: Assertion
46+hashMap = do
47+ let t = test (Proxy :: Proxy (HM.HashMap String Int))
48+ t "\"\"" $ expected "HashMap k v" "String"
49+ t "[]" $ expected "HashMap k v" "Array"
50+
51+expected :: String -> String -> String
52+expected ex enc = "Error in $: expected " ++ ex ++ ", encountered " ++ enc
53+
54+test :: forall a proxy . (FromJSON a, Show a) => proxy a -> L.ByteString -> String -> Assertion
55+test _ v msg = case eitherDecode v of
56+ Left e -> assertEqual "Invalid error message" msg e
57+ Right (x :: a) -> assertFailure $ "Expected parsing to fail but it suceeded with: " ++ show x
tests/Functions.hs+1−2
@@ -1,11 +1,10 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Functions
42 (
53 approxEq
64 , approxEqWith
75 ) where
86
7+import Prelude ()
98import Prelude.Compat
109
1110approxEq :: (Fractional a, Ord a) => a -> a -> Bool
tests/Instances.hs+62−7
@@ -1,13 +1,13 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE NoImplicitPrelude #-}
44{-# LANGUAGE OverloadedStrings #-}
55{-# LANGUAGE RecordWildCards #-}
6−
76{-# OPTIONS_GHC -fno-warn-orphans #-}
87
98module Instances () where
109
10+import Prelude ()
1111import Prelude.Compat
1212
1313import Control.Applicative (empty)
@@ -22,14 +22,35 @@ import Types
2222import qualified Data.DList as DList
2323import qualified Data.HashMap.Strict as HM
2424
25+#if !MIN_VERSION_QuickCheck(2,9,0)
26+import Control.Applicative (Const(..))
27+import Data.Functor.Identity (Identity (..))
28+import Data.List.NonEmpty (NonEmpty(..))
29+import Data.Version
30+import Test.QuickCheck (getNonNegative, listOf1, resize)
31+#endif
32+
33+#if !MIN_VERSION_QuickCheck(2,10,0)
34+import Data.Functor.Compose (Compose (..))
35+import Data.Proxy (Proxy(..))
36+#endif
37+
2538import Data.Orphans ()
2639import Test.QuickCheck.Instances ()
40+#if MIN_VERSION_base(4,7,0)
2741import Data.Hashable.Time ()
42+#endif
43+
44+#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
45+import Numeric.Natural
46+#endif
47+
48+{-# ANN module ("HLint: ignore Use fewer imports"::String) #-}
2849
2950-- "System" types.
3051
3152instance Arbitrary DotNetTime where
32− arbitrary = DotNetTime `fmap` arbitrary
53+ arbitrary = DotNetTime `liftM` arbitrary
3354 shrink = map DotNetTime . shrink . fromDotNetTime
3455
3556-- | Compare timezone part only on 'timeZoneMinutes'
@@ -153,15 +174,49 @@ instance Arbitrary EitherTextInt where
153174instance Arbitrary (GADT String) where
154175 arbitrary = GADT <$> arbitrary
155176
156−instance Arbitrary OptionField where
157− arbitrary = OptionField <$> arbitrary
158−
159−
160177instance ApproxEq Char where
161178 (=~) = (==)
162179
163180instance (ApproxEq a) => ApproxEq [a] where
164181 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
165182
183+-- Version tags are deprecated, so we avoid using them in the Arbitrary
184+-- instance. However, the recommended constructor 'makeVersion' is not
185+-- exported by "Data.Version" until base-4.8.0.0. For previous versions,
186+-- a definition is given below.
187+
188+
189+#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
190+instance Arbitrary Natural where
191+ arbitrary = fromInteger . abs <$> arbitrary
192+#endif
193+
194+#if !MIN_VERSION_QuickCheck(2,10,0)
195+instance Arbitrary (Proxy a) where
196+ arbitrary = pure Proxy
197+
198+instance Arbitrary (f (g a)) => Arbitrary (Compose f g a) where
199+ arbitrary = Compose <$> arbitrary
200+#endif
201+
166202instance Arbitrary a => Arbitrary (DList.DList a) where
167203 arbitrary = DList.fromList <$> arbitrary
204+
205+#if !MIN_VERSION_QuickCheck(2,9,0)
206+instance Arbitrary a => Arbitrary (Const a b) where
207+ arbitrary = Const <$> arbitrary
208+
209+instance Arbitrary a => Arbitrary (NonEmpty a) where
210+ arbitrary = (:|) <$> arbitrary <*> arbitrary
211+
212+instance Arbitrary Version where
213+ arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
214+
215+#if !MIN_VERSION_base(4,8,0)
216+makeVersion :: [Int] -> Version
217+makeVersion b = Version b []
218+#endif
219+
220+instance Arbitrary a => Arbitrary (Identity a) where
221+ arbitrary = Identity <$> arbitrary
222+#endif
tests/JSONTestSuite/parsers/test_haskell-aeson/testaeson.cabal+3−5
@@ -1,8 +1,6 @@
1−-- This file has been generated from package.yaml by hpack version 0.28.2.
1+-- This file has been generated from package.yaml by hpack version 0.14.0.
22--
33-- see: https://github.com/sol/hpack
4−--
5−-- hash: 716ef4c3e64849526f097b85ed05015e0072c2e2afcc1f91e58d8bea68b123b2
64
75name: testaeson
86version: 0.0.0
@@ -14,8 +12,8 @@ executable testaeson
1412 hs-source-dirs:
1513 src
1614 build-depends:
17− aeson
18− , base >=4.7 && <5
15+ base >= 4.7 && < 5
16+ , aeson
1917 , base-prelude
2018 , bytestring
2119 default-language: Haskell2010
tests/Options.hs+1−19
@@ -1,7 +1,6 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Options (module Options) where
42
3+import Prelude ()
54import Prelude.Compat
65
76import Data.Aeson.Types
@@ -49,20 +48,3 @@ optsTagSingleConstructors = optsDefault
4948 { tagSingleConstructors = True
5049 , allNullaryToStringTag = False
5150 }
52−
53−optsOptionField :: Options
54−optsOptionField = optsDefault
55− { fieldLabelModifier = const "field"
56− , omitNothingFields = True
57− }
58−
59−optsRejectUnknownFields :: Options
60−optsRejectUnknownFields = optsDefault
61− { rejectUnknownFields = True
62− }
63−
64−optsRejectUnknownFieldsTagged :: Options
65−optsRejectUnknownFieldsTagged = optsDefault
66− { rejectUnknownFields = True
67− , tagSingleConstructors = True
68− }
tests/PropUtils.hsdeleted−208
@@ -1,208 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE RankNTypes #-}
4−
5−module PropUtils (module PropUtils) where
6−
7−import Prelude.Compat
8−
9−import Data.Aeson (eitherDecode, encode)
10−import Data.Aeson.Encoding (encodingToLazyByteString)
11−import Data.Aeson.Internal (IResult(..), formatError, ifromJSON, iparse)
12−import qualified Data.Aeson.Internal as I
13−import Data.Aeson.Parser (value)
14−import Data.Aeson.Types
15−import Data.HashMap.Strict (HashMap)
16−import Data.Hashable (Hashable)
17−import Data.Int (Int8)
18−import Data.Map (Map)
19−import Data.Time (ZonedTime)
20−import Encoders
21−import Instances ()
22−import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample)
23−import Types
24−import qualified Data.Attoparsec.Lazy as L
25−import qualified Data.ByteString.Lazy.Char8 as L
26−import qualified Data.HashMap.Strict as H
27−import qualified Data.Map as Map
28−import qualified Data.Text as T
29−import qualified Data.Vector as V
30−
31−
32−encodeDouble :: Double -> Double -> Property
33−encodeDouble num denom
34− | isInfinite d || isNaN d = encode d === "null"
35− | otherwise = (read . L.unpack . encode) d === d
36− where d = num / denom
37−
38−encodeInteger :: Integer -> Property
39−encodeInteger i = encode i === L.pack (show i)
40−
41−toParseJSON :: (Eq a, Show a) =>
42− (Value -> Parser a) -> (a -> Value) -> a -> Property
43−toParseJSON parsejson tojson x =
44− case iparse parsejson . tojson $ x of
45− IError path msg -> failure "parse" (formatError path msg) x
46− ISuccess x' -> x === x'
47−
48−toParseJSON1
49− :: (Eq (f Int), Show (f Int))
50− => (forall a. LiftParseJSON f a)
51− -> (forall a. LiftToJSON f a)
52− -> f Int
53− -> Property
54−toParseJSON1 parsejson1 tojson1 = toParseJSON parsejson tojson
55− where
56− parsejson = parsejson1 parseJSON (listParser parseJSON)
57− tojson = tojson1 toJSON (listValue toJSON)
58−
59−roundTripEnc :: (FromJSON a, ToJSON a, Show a) =>
60− (a -> a -> Property) -> a -> a -> Property
61−roundTripEnc eq _ i =
62− case fmap ifromJSON . L.parse value . encode $ i of
63− L.Done _ (ISuccess v) -> v `eq` i
64− L.Done _ (IError path err) -> failure "fromJSON" (formatError path err) i
65− L.Fail _ _ err -> failure "parse" err i
66−
67−roundTripNoEnc :: (FromJSON a, ToJSON a, Show a) =>
68− (a -> a -> Property) -> a -> a -> Property
69−roundTripNoEnc eq _ i =
70− case ifromJSON . toJSON $ i of
71− (ISuccess v) -> v `eq` i
72− (IError path err) -> failure "fromJSON" (formatError path err) i
73−
74−roundTripEq :: (Eq a, FromJSON a, ToJSON a, Show a) => a -> a -> Property
75−roundTripEq x y = roundTripEnc (===) x y .&&. roundTripNoEnc (===) x y
76−
77−-- We test keys by encoding HashMap and Map with it
78−roundTripKey
79− :: (Ord a, Hashable a, FromJSONKey a, ToJSONKey a, Show a)
80− => a -> HashMap a Int -> Map a Int -> Property
81−roundTripKey _ h m = roundTripEq h h .&&. roundTripEq m m
82−
83−infix 4 ==~
84−(==~) :: (ApproxEq a, Show a) => a -> a -> Property
85−x ==~ y =
86− counterexample (show x ++ " /= " ++ show y) (x =~ y)
87−
88−toFromJSON :: (Arbitrary a, Eq a, FromJSON a, ToJSON a, Show a) => a -> Property
89−toFromJSON x = case ifromJSON (toJSON x) of
90− IError path err -> failure "fromJSON" (formatError path err) x
91− ISuccess x' -> x === x'
92−
93−modifyFailureProp :: String -> String -> Bool
94−modifyFailureProp orig added =
95− result == Error (added ++ orig)
96− where
97− parser = const $ modifyFailure (added ++) $ fail orig
98− result :: Result ()
99− result = parse parser ()
100−
101−parserThrowErrorProp :: String -> Property
102−parserThrowErrorProp msg =
103− result === Error msg
104− where
105− parser = const $ parserThrowError [] msg
106− result :: Result ()
107− result = parse parser ()
108−
109−-- | Tests (also) that we catch the JSONPath and it has elements in the right order.
110−parserCatchErrorProp :: [String] -> String -> Property
111−parserCatchErrorProp path msg =
112− result === Success ([I.Key "outer", I.Key "inner"] ++ jsonPath, msg)
113− where
114− parser = parserCatchError outer (curry pure)
115−
116− outer = inner I.<?> I.Key "outer"
117− inner = parserThrowError jsonPath msg I.<?> I.Key "inner"
118−
119− result :: Result (I.JSONPath, String)
120− result = parse (const parser) ()
121−
122− jsonPath = map (I.Key . T.pack) path
123−
124−-- | Perform a structural comparison of the results of two encoding
125−-- methods. Compares decoded values to account for HashMap-driven
126−-- variation in JSON object key ordering.
127−sameAs :: (a -> Value) -> (a -> Encoding) -> a -> Property
128−sameAs toVal toEnc v =
129− counterexample (show s) $
130− eitherDecode s === Right (toVal v)
131− where
132− s = encodingToLazyByteString (toEnc v)
133−
134−sameAs1
135− :: (forall a. LiftToJSON f a)
136− -> (forall a. LiftToEncoding f a)
137− -> f Int
138− -> Property
139−sameAs1 toVal1 toEnc1 v = lhs === rhs
140− where
141− rhs = Right $ toVal1 toJSON (listValue toJSON) v
142− lhs = eitherDecode . encodingToLazyByteString $
143− toEnc1 toEncoding (listEncoding toEncoding) v
144−
145−sameAs1Agree
146− :: ToJSON a
147− => (f a -> Encoding)
148− -> (forall b. LiftToEncoding f b)
149− -> f a
150− -> Property
151−sameAs1Agree toEnc toEnc1 v = rhs === lhs
152− where
153− rhs = encodingToLazyByteString $ toEnc v
154− lhs = encodingToLazyByteString $ toEnc1 toEncoding (listEncoding toEncoding) v
155−
156−type P6 = Product6 Int Bool String (Approx Double) (Int, Approx Double) ()
157−type S4 = Sum4 Int8 ZonedTime T.Text (Map.Map String Int)
158−
159−--------------------------------------------------------------------------------
160−-- Value properties
161−--------------------------------------------------------------------------------
162−
163−-- | Add the formatted @Value@ to the printed counterexample when the property
164−-- fails.
165−checkValue :: Testable a => (Value -> a) -> Value -> Property
166−checkValue prop v = counterexample (L.unpack (encode v)) (prop v)
167−
168−isString :: Value -> Bool
169−isString (String _) = True
170−isString _ = False
171−
172−is2ElemArray :: Value -> Bool
173−is2ElemArray (Array v) = V.length v == 2 && isString (V.head v)
174−is2ElemArray _ = False
175−
176−isTaggedObjectValue :: Value -> Bool
177−isTaggedObjectValue (Object obj) = "tag" `H.member` obj &&
178− "contents" `H.member` obj
179−isTaggedObjectValue _ = False
180−
181−isNullaryTaggedObject :: Value -> Bool
182−isNullaryTaggedObject obj = isTaggedObject' obj && isObjectWithSingleField obj
183−
184−isTaggedObject :: Value -> Property
185−isTaggedObject = checkValue isTaggedObject'
186−
187−isTaggedObject' :: Value -> Bool
188−isTaggedObject' (Object obj) = "tag" `H.member` obj
189−isTaggedObject' _ = False
190−
191−isObjectWithSingleField :: Value -> Bool
192−isObjectWithSingleField (Object obj) = H.size obj == 1
193−isObjectWithSingleField _ = False
194−
195−-- | is untaggedValue of EitherTextInt
196−isUntaggedValueETI :: Value -> Bool
197−isUntaggedValueETI (String s)
198− | s == "nonenullary" = True
199−isUntaggedValueETI (Bool _) = True
200−isUntaggedValueETI (Number _) = True
201−isUntaggedValueETI (Array a) = length a == 2
202−isUntaggedValueETI _ = False
203−
204−isEmptyArray :: Value -> Property
205−isEmptyArray = checkValue isEmptyArray'
206−
207−isEmptyArray' :: Value -> Bool
208−isEmptyArray' = (Array mempty ==)
tests/Properties.hs+511−14
@@ -1,28 +1,326 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-} -- For arbitrary Compose
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
26
37module Properties (module Properties) where
48
9+import Prelude ()
510import Prelude.Compat
611
12+import Control.Applicative (Const)
13+import Data.Aeson (eitherDecode, encode)
14+import Data.Aeson.Encoding (encodingToLazyByteString)
15+import Data.Aeson.Internal (IResult(..), formatError, ifromJSON, iparse)
16+import qualified Data.Aeson.Internal as I
17+import Data.Aeson.Parser (value)
18+import Data.Aeson.Types
19+import Data.DList (DList)
20+import Data.Functor.Compose (Compose (..))
21+import Data.HashMap.Strict (HashMap)
22+import Data.Hashable (Hashable)
23+import Data.Int (Int8)
24+import Data.List.NonEmpty (NonEmpty)
25+import Data.Map (Map)
26+import Data.Proxy (Proxy)
27+import Data.Ratio (Ratio)
28+import Data.Sequence (Seq)
29+import Data.Tagged (Tagged)
30+import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
31+import Data.Version (Version)
32+import Encoders
733import Instances ()
8−import Test.Tasty (TestTree, testGroup)
9−import Test.Tasty.QuickCheck (testProperty)
10−import Test.QuickCheck (Property)
11−import PropUtils
12−import PropertyGeneric
13−import PropertyKeys
14−import PropertyRoundTrip
15−import PropertyTH
34+import Numeric.Natural (Natural)
35+import Test.Framework (Test, testGroup)
36+import Test.Framework.Providers.QuickCheck2 (testProperty)
37+import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample)
38+import Types
39+import qualified Data.Attoparsec.Lazy as L
40+import qualified Data.ByteString.Lazy.Char8 as L
41+import qualified Data.HashMap.Strict as H
42+import qualified Data.Map as Map
43+import qualified Data.Text as T
44+import qualified Data.Text.Lazy as LT
45+import qualified Data.UUID.Types as UUID
46+import qualified Data.Vector as V
1647
48+encodeDouble :: Double -> Double -> Property
49+encodeDouble num denom
50+ | isInfinite d || isNaN d = encode d === "null"
51+ | otherwise = (read . L.unpack . encode) d === d
52+ where d = num / denom
1753
18−tests :: TestTree
54+encodeInteger :: Integer -> Property
55+encodeInteger i = encode i === L.pack (show i)
56+
57+toParseJSON :: (Eq a, Show a) =>
58+ (Value -> Parser a) -> (a -> Value) -> a -> Property
59+toParseJSON parsejson tojson x =
60+ case iparse parsejson . tojson $ x of
61+ IError path msg -> failure "parse" (formatError path msg) x
62+ ISuccess x' -> x === x'
63+
64+toParseJSON1
65+ :: (Eq (f Int), Show (f Int))
66+ => (forall a. LiftParseJSON f a)
67+ -> (forall a. LiftToJSON f a)
68+ -> f Int
69+ -> Property
70+toParseJSON1 parsejson1 tojson1 = toParseJSON parsejson tojson
71+ where
72+ parsejson = parsejson1 parseJSON (listParser parseJSON)
73+ tojson = tojson1 toJSON (listValue toJSON)
74+
75+roundTripEnc :: (FromJSON a, ToJSON a, Show a) =>
76+ (a -> a -> Property) -> a -> a -> Property
77+roundTripEnc eq _ i =
78+ case fmap ifromJSON . L.parse value . encode $ i of
79+ L.Done _ (ISuccess v) -> v `eq` i
80+ L.Done _ (IError path err) -> failure "fromJSON" (formatError path err) i
81+ L.Fail _ _ err -> failure "parse" err i
82+
83+roundTripNoEnc :: (FromJSON a, ToJSON a, Show a) =>
84+ (a -> a -> Property) -> a -> a -> Property
85+roundTripNoEnc eq _ i =
86+ case ifromJSON . toJSON $ i of
87+ (ISuccess v) -> v `eq` i
88+ (IError path err) -> failure "fromJSON" (formatError path err) i
89+
90+roundTripEq :: (Eq a, FromJSON a, ToJSON a, Show a) => a -> a -> Property
91+roundTripEq x y = roundTripEnc (===) x y .&&. roundTripNoEnc (===) x y
92+
93+-- We test keys by encoding HashMap and Map with it
94+roundTripKey
95+ :: (Ord a, Hashable a, FromJSONKey a, ToJSONKey a, Show a)
96+ => a -> HashMap a Int -> Map a Int -> Property
97+roundTripKey _ h m = roundTripEq h h .&&. roundTripEq m m
98+
99+infix 4 ==~
100+(==~) :: (ApproxEq a, Show a) => a -> a -> Property
101+x ==~ y =
102+ counterexample (show x ++ " /= " ++ show y) (x =~ y)
103+
104+toFromJSON :: (Arbitrary a, Eq a, FromJSON a, ToJSON a, Show a) => a -> Property
105+toFromJSON x = case ifromJSON (toJSON x) of
106+ IError path err -> failure "fromJSON" (formatError path err) x
107+ ISuccess x' -> x === x'
108+
109+modifyFailureProp :: String -> String -> Bool
110+modifyFailureProp orig added =
111+ result == Error (added ++ orig)
112+ where
113+ parser = const $ modifyFailure (added ++) $ fail orig
114+ result :: Result ()
115+ result = parse parser ()
116+
117+parserThrowErrorProp :: String -> Property
118+parserThrowErrorProp msg =
119+ result === Error msg
120+ where
121+ parser = const $ parserThrowError [] msg
122+ result :: Result ()
123+ result = parse parser ()
124+
125+-- | Tests (also) that we catch the JSONPath and it has elements in the right order.
126+parserCatchErrorProp :: [String] -> String -> Property
127+parserCatchErrorProp path msg =
128+ result === Success ([I.Key "outer", I.Key "inner"] ++ jsonPath, msg)
129+ where
130+ parser = parserCatchError outer (curry pure)
131+
132+ outer = inner I.<?> I.Key "outer"
133+ inner = parserThrowError jsonPath msg I.<?> I.Key "inner"
134+
135+ result :: Result (I.JSONPath, String)
136+ result = parse (const parser) ()
137+
138+ jsonPath = map (I.Key . T.pack) path
139+
140+-- | Perform a structural comparison of the results of two encoding
141+-- methods. Compares decoded values to account for HashMap-driven
142+-- variation in JSON object key ordering.
143+sameAs :: (a -> Value) -> (a -> Encoding) -> a -> Property
144+sameAs toVal toEnc v =
145+ eitherDecode (encodingToLazyByteString (toEnc v)) === Right (toVal v)
146+
147+sameAs1
148+ :: (forall a. LiftToJSON f a)
149+ -> (forall a. LiftToEncoding f a)
150+ -> f Int
151+ -> Property
152+sameAs1 toVal1 toEnc1 v = lhs === rhs
153+ where
154+ rhs = Right $ toVal1 toJSON (listValue toJSON) v
155+ lhs = eitherDecode . encodingToLazyByteString $
156+ toEnc1 toEncoding (listEncoding toEncoding) v
157+
158+sameAs1Agree
159+ :: ToJSON a
160+ => (f a -> Encoding)
161+ -> (forall b. LiftToEncoding f b)
162+ -> f a
163+ -> Property
164+sameAs1Agree toEnc toEnc1 v = rhs === lhs
165+ where
166+ rhs = encodingToLazyByteString $ toEnc v
167+ lhs = encodingToLazyByteString $ toEnc1 toEncoding (listEncoding toEncoding) v
168+
169+type P6 = Product6 Int Bool String (Approx Double) (Int, Approx Double) ()
170+type S4 = Sum4 Int8 ZonedTime T.Text (Map.Map String Int)
171+
172+--------------------------------------------------------------------------------
173+-- Value properties
174+--------------------------------------------------------------------------------
175+
176+-- | Add the formatted @Value@ to the printed counterexample when the property
177+-- fails.
178+checkValue :: Testable a => (Value -> a) -> Value -> Property
179+checkValue prop v = counterexample (L.unpack (encode v)) (prop v)
180+
181+isString :: Value -> Bool
182+isString (String _) = True
183+isString _ = False
184+
185+is2ElemArray :: Value -> Bool
186+is2ElemArray (Array v) = V.length v == 2 && isString (V.head v)
187+is2ElemArray _ = False
188+
189+isTaggedObjectValue :: Value -> Bool
190+isTaggedObjectValue (Object obj) = "tag" `H.member` obj &&
191+ "contents" `H.member` obj
192+isTaggedObjectValue _ = False
193+
194+isNullaryTaggedObject :: Value -> Bool
195+isNullaryTaggedObject obj = isTaggedObject' obj && isObjectWithSingleField obj
196+
197+isTaggedObject :: Value -> Property
198+isTaggedObject = checkValue isTaggedObject'
199+
200+isTaggedObject' :: Value -> Bool
201+isTaggedObject' (Object obj) = "tag" `H.member` obj
202+isTaggedObject' _ = False
203+
204+isObjectWithSingleField :: Value -> Bool
205+isObjectWithSingleField (Object obj) = H.size obj == 1
206+isObjectWithSingleField _ = False
207+
208+-- | is untaggedValue of EitherTextInt
209+isUntaggedValueETI :: Value -> Bool
210+isUntaggedValueETI (String s)
211+ | s == "nonenullary" = True
212+isUntaggedValueETI (Bool _) = True
213+isUntaggedValueETI (Number _) = True
214+isUntaggedValueETI (Array a) = length a == 2
215+isUntaggedValueETI _ = False
216+
217+isEmptyArray :: Value -> Property
218+isEmptyArray = checkValue isEmptyArray'
219+
220+isEmptyArray' :: Value -> Bool
221+isEmptyArray' = (Array mempty ==)
222+
223+
224+--------------------------------------------------------------------------------
225+
226+
227+tests :: Test
19228tests = testGroup "properties" [
20229 testGroup "encode" [
21230 testProperty "encodeDouble" encodeDouble
22231 , testProperty "encodeInteger" encodeInteger
23232 ]
24− , roundTripTests
25− , keysTests
233+ , testGroup "roundTrip" [
234+ testProperty "Bool" $ roundTripEq True
235+ , testProperty "Double" $ roundTripEq (1 :: Approx Double)
236+ , testProperty "Int" $ roundTripEq (1 :: Int)
237+ , testProperty "NonEmpty Char" $ roundTripEq (undefined :: NonEmpty Char)
238+ , testProperty "Integer" $ roundTripEq (1 :: Integer)
239+ , testProperty "String" $ roundTripEq ("" :: String)
240+ , testProperty "Text" $ roundTripEq T.empty
241+ , testProperty "Lazy Text" $ roundTripEq LT.empty
242+ , testProperty "Foo" $ roundTripEq (undefined :: Foo)
243+ , testProperty "Day" $ roundTripEq (undefined :: Day)
244+ , testProperty "BCE Day" $ roundTripEq (undefined :: BCEDay)
245+ , testProperty "DotNetTime" $ roundTripEq (undefined :: Approx DotNetTime)
246+ , testProperty "LocalTime" $ roundTripEq (undefined :: LocalTime)
247+ , testProperty "TimeOfDay" $ roundTripEq (undefined :: TimeOfDay)
248+ , testProperty "UTCTime" $ roundTripEq (undefined :: UTCTime)
249+ , testProperty "ZonedTime" $ roundTripEq (undefined :: ZonedTime)
250+ , testProperty "NominalDiffTime" $ roundTripEq (undefined :: NominalDiffTime)
251+ , testProperty "Version" $ roundTripEq (undefined :: Version)
252+ , testProperty "Natural" $ roundTripEq (undefined :: Natural)
253+ , testProperty "Proxy" $ roundTripEq (undefined :: Proxy Int)
254+ , testProperty "Tagged" $ roundTripEq (undefined :: Tagged Int Char)
255+ , testProperty "Const" $ roundTripEq (undefined :: Const Int Char)
256+ , testProperty "DList" $ roundTripEq (undefined :: DList Int)
257+ , testProperty "Seq" $ roundTripEq (undefined :: Seq Int)
258+ , testProperty "Rational" $ roundTripEq (undefined :: Rational)
259+ , testProperty "Ratio Int" $ roundTripEq (undefined :: Ratio Int)
260+ , testProperty "UUID" $ roundTripEq UUID.nil
261+ , testGroup "functors"
262+ [ testProperty "Identity Char" $ roundTripEq (undefined :: I Int)
263+
264+ , testProperty "Identity Char" $ roundTripEq (undefined :: I Char)
265+ , testProperty "Identity [Char]" $ roundTripEq (undefined :: I String)
266+ , testProperty "[Identity Char]" $ roundTripEq (undefined :: [I Char])
267+
268+ , testProperty "Compose I I Int" $ roundTripEq (undefined :: LogScaled (Compose I I Int))
269+ , testProperty "Compose [] I Int" $ roundTripEq (undefined :: LogScaled (Compose [] I Int))
270+ , testProperty "Compose I [] Int" $ roundTripEq (undefined :: LogScaled (Compose I [] Int))
271+ , testProperty "Compose [] [] Int" $ roundTripEq (undefined :: LogScaled (Compose [] [] Int))
272+
273+ , testProperty "Compose I I Char" $ roundTripEq (undefined :: LogScaled (Compose I I Char))
274+ , testProperty "Compose [] I Char" $ roundTripEq (undefined :: LogScaled (Compose [] I Char))
275+ , testProperty "Compose I [] Char" $ roundTripEq (undefined :: LogScaled (Compose I [] Char))
276+ , testProperty "Compose [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose [] [] Char))
277+
278+ , testProperty "Compose3 I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I I Char))
279+ , testProperty "Compose3 I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] I Char))
280+ , testProperty "Compose3 I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I [] Char))
281+ , testProperty "Compose3 I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] [] Char))
282+ , testProperty "Compose3 [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I I Char))
283+ , testProperty "Compose3 [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] I Char))
284+ , testProperty "Compose3 [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I [] Char))
285+ , testProperty "Compose3 [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] [] Char))
286+
287+ , testProperty "Compose3' I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I I Char))
288+ , testProperty "Compose3' I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] I Char))
289+ , testProperty "Compose3' I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I [] Char))
290+ , testProperty "Compose3' I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] [] Char))
291+ , testProperty "Compose3' [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I I Char))
292+ , testProperty "Compose3' [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] I Char))
293+ , testProperty "Compose3' [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I [] Char))
294+ , testProperty "Compose3' [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] [] Char))
295+ ]
296+ , testGroup "ghcGenerics" [
297+ testProperty "OneConstructor" $ roundTripEq OneConstructor
298+ , testProperty "Product2" $ roundTripEq (undefined :: Product2 Int Bool)
299+ , testProperty "Product6" $ roundTripEq (undefined :: P6)
300+ , testProperty "Sum4" $ roundTripEq (undefined :: S4)
301+ ]
302+ ]
303+ , testGroup "roundTrip Key"
304+ [ testProperty "Bool" $ roundTripKey True
305+ , testProperty "Text" $ roundTripKey (undefined :: T.Text)
306+ , testProperty "String" $ roundTripKey (undefined :: String)
307+ , testProperty "Int" $ roundTripKey (undefined :: Int)
308+ , testProperty "[Text]" $ roundTripKey (undefined :: LogScaled [T.Text])
309+ , testProperty "(Int,Char)" $ roundTripKey (undefined :: (Int,Char))
310+ , testProperty "Integer" $ roundTripKey (undefined :: Integer)
311+ , testProperty "Natural" $ roundTripKey (undefined :: Natural)
312+ , testProperty "Float" $ roundTripKey (undefined :: Float)
313+ , testProperty "Double" $ roundTripKey (undefined :: Double)
314+#if MIN_VERSION_base(4,7,0)
315+ , testProperty "Day" $ roundTripKey (undefined :: Day)
316+ , testProperty "LocalTime" $ roundTripKey (undefined :: LocalTime)
317+ , testProperty "TimeOfDay" $ roundTripKey (undefined :: TimeOfDay)
318+ , testProperty "UTCTime" $ roundTripKey (undefined :: UTCTime)
319+#endif
320+ , testProperty "Version" $ roundTripKey (undefined :: Version)
321+ , testProperty "Lazy Text" $ roundTripKey (undefined :: LT.Text)
322+ , testProperty "UUID" $ roundTripKey UUID.nil
323+ ]
26324 , testGroup "toFromJSON" [
27325 testProperty "Integer" (toFromJSON :: Integer -> Property)
28326 , testProperty "Double" (toFromJSON :: Double -> Property)
@@ -35,6 +333,205 @@ tests = testGroup "properties" [
35333 , testProperty "parserThrowError" parserThrowErrorProp
36334 , testProperty "parserCatchError" parserCatchErrorProp
37335 ]
38− , genericTests
39− , templateHaskellTests
336+ , testGroup "generic" [
337+ testGroup "toJSON" [
338+ testGroup "Nullary" [
339+ testProperty "string" (isString . gNullaryToJSONString)
340+ , testProperty "2ElemArray" (is2ElemArray . gNullaryToJSON2ElemArray)
341+ , testProperty "TaggedObject" (isNullaryTaggedObject . gNullaryToJSONTaggedObject)
342+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gNullaryToJSONObjectWithSingleField)
343+ , testGroup "roundTrip" [
344+ testProperty "string" (toParseJSON gNullaryParseJSONString gNullaryToJSONString)
345+ , testProperty "2ElemArray" (toParseJSON gNullaryParseJSON2ElemArray gNullaryToJSON2ElemArray)
346+ , testProperty "TaggedObject" (toParseJSON gNullaryParseJSONTaggedObject gNullaryToJSONTaggedObject)
347+ , testProperty "ObjectWithSingleField" (toParseJSON gNullaryParseJSONObjectWithSingleField gNullaryToJSONObjectWithSingleField)
348+ ]
349+ ]
350+ , testGroup "EitherTextInt" [
351+ testProperty "UntaggedValue" (isUntaggedValueETI . gEitherTextIntToJSONUntaggedValue)
352+ , testProperty "roundtrip" (toParseJSON gEitherTextIntParseJSONUntaggedValue gEitherTextIntToJSONUntaggedValue)
353+ ]
354+ , testGroup "SomeType" [
355+ testProperty "2ElemArray" (is2ElemArray . gSomeTypeToJSON2ElemArray)
356+ , testProperty "TaggedObject" (isTaggedObject . gSomeTypeToJSONTaggedObject)
357+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gSomeTypeToJSONObjectWithSingleField)
358+ , testGroup "roundTrip" [
359+ testProperty "2ElemArray" (toParseJSON gSomeTypeParseJSON2ElemArray gSomeTypeToJSON2ElemArray)
360+ , testProperty "TaggedObject" (toParseJSON gSomeTypeParseJSONTaggedObject gSomeTypeToJSONTaggedObject)
361+ , testProperty "ObjectWithSingleField" (toParseJSON gSomeTypeParseJSONObjectWithSingleField gSomeTypeToJSONObjectWithSingleField)
362+
363+#if __GLASGOW_HASKELL__ >= 706
364+ , testProperty "2ElemArray unary" (toParseJSON1 gSomeTypeLiftParseJSON2ElemArray gSomeTypeLiftToJSON2ElemArray)
365+ , testProperty "TaggedObject unary" (toParseJSON1 gSomeTypeLiftParseJSONTaggedObject gSomeTypeLiftToJSONTaggedObject)
366+ , testProperty "ObjectWithSingleField unary" (toParseJSON1 gSomeTypeLiftParseJSONObjectWithSingleField gSomeTypeLiftToJSONObjectWithSingleField)
367+#endif
368+ ]
369+ ]
370+ , testGroup "OneConstructor" [
371+ testProperty "default" (isEmptyArray . gOneConstructorToJSONDefault)
372+ , testProperty "Tagged" (isTaggedObject . gOneConstructorToJSONTagged)
373+ , testGroup "roundTrip" [
374+ testProperty "default" (toParseJSON gOneConstructorParseJSONDefault gOneConstructorToJSONDefault)
375+ , testProperty "Tagged" (toParseJSON gOneConstructorParseJSONTagged gOneConstructorToJSONTagged)
376+ ]
377+ ]
378+ ]
379+ , testGroup "toEncoding" [
380+ testProperty "NullaryString" $
381+ gNullaryToJSONString `sameAs` gNullaryToEncodingString
382+ , testProperty "Nullary2ElemArray" $
383+ gNullaryToJSON2ElemArray `sameAs` gNullaryToEncoding2ElemArray
384+ , testProperty "NullaryTaggedObject" $
385+ gNullaryToJSONTaggedObject `sameAs` gNullaryToEncodingTaggedObject
386+ , testProperty "NullaryObjectWithSingleField" $
387+ gNullaryToJSONObjectWithSingleField `sameAs`
388+ gNullaryToEncodingObjectWithSingleField
389+ -- , testProperty "ApproxUnwrap" $
390+ -- gApproxToJSONUnwrap `sameAs` gApproxToEncodingUnwrap
391+ , testProperty "ApproxDefault" $
392+ gApproxToJSONDefault `sameAs` gApproxToEncodingDefault
393+
394+ , testProperty "EitherTextInt UntaggedValue" $
395+ gEitherTextIntToJSONUntaggedValue `sameAs` gEitherTextIntToEncodingUntaggedValue
396+
397+ , testProperty "SomeType2ElemArray" $
398+ gSomeTypeToJSON2ElemArray `sameAs` gSomeTypeToEncoding2ElemArray
399+#if __GLASGOW_HASKELL__ >= 706
400+ , testProperty "SomeType2ElemArray unary" $
401+ gSomeTypeLiftToJSON2ElemArray `sameAs1` gSomeTypeLiftToEncoding2ElemArray
402+ , testProperty "SomeType2ElemArray unary agree" $
403+ gSomeTypeToEncoding2ElemArray `sameAs1Agree` gSomeTypeLiftToEncoding2ElemArray
404+#endif
405+
406+ , testProperty "SomeTypeTaggedObject" $
407+ gSomeTypeToJSONTaggedObject `sameAs` gSomeTypeToEncodingTaggedObject
408+#if __GLASGOW_HASKELL__ >= 706
409+ , testProperty "SomeTypeTaggedObject unary" $
410+ gSomeTypeLiftToJSONTaggedObject `sameAs1` gSomeTypeLiftToEncodingTaggedObject
411+ , testProperty "SomeTypeTaggedObject unary agree" $
412+ gSomeTypeToEncodingTaggedObject `sameAs1Agree` gSomeTypeLiftToEncodingTaggedObject
413+#endif
414+
415+ , testProperty "SomeTypeObjectWithSingleField" $
416+ gSomeTypeToJSONObjectWithSingleField `sameAs` gSomeTypeToEncodingObjectWithSingleField
417+#if __GLASGOW_HASKELL__ >= 706
418+ , testProperty "SomeTypeObjectWithSingleField unary" $
419+ gSomeTypeLiftToJSONObjectWithSingleField `sameAs1` gSomeTypeLiftToEncodingObjectWithSingleField
420+ , testProperty "SomeTypeObjectWithSingleField unary agree" $
421+ gSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` gSomeTypeLiftToEncodingObjectWithSingleField
422+#endif
423+
424+ , testProperty "SomeTypeOmitNothingFields" $
425+ gSomeTypeToJSONOmitNothingFields `sameAs` gSomeTypeToEncodingOmitNothingFields
426+
427+ , testProperty "OneConstructorDefault" $
428+ gOneConstructorToJSONDefault `sameAs` gOneConstructorToEncodingDefault
429+ , testProperty "OneConstructorTagged" $
430+ gOneConstructorToJSONTagged `sameAs` gOneConstructorToEncodingTagged
431+ ]
432+ ]
433+ , testGroup "template-haskell" [
434+ testGroup "toJSON" [
435+ testGroup "Nullary" [
436+ testProperty "string" (isString . thNullaryToJSONString)
437+ , testProperty "2ElemArray" (is2ElemArray . thNullaryToJSON2ElemArray)
438+ , testProperty "TaggedObject" (isNullaryTaggedObject . thNullaryToJSONTaggedObject)
439+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thNullaryToJSONObjectWithSingleField)
440+
441+ , testGroup "roundTrip" [
442+ testProperty "string" (toParseJSON thNullaryParseJSONString thNullaryToJSONString)
443+ , testProperty "2ElemArray" (toParseJSON thNullaryParseJSON2ElemArray thNullaryToJSON2ElemArray)
444+ , testProperty "TaggedObject" (toParseJSON thNullaryParseJSONTaggedObject thNullaryToJSONTaggedObject)
445+ , testProperty "ObjectWithSingleField" (toParseJSON thNullaryParseJSONObjectWithSingleField thNullaryToJSONObjectWithSingleField)
446+ ]
447+ ]
448+ , testGroup "EitherTextInt" [
449+ testProperty "UntaggedValue" (isUntaggedValueETI . thEitherTextIntToJSONUntaggedValue)
450+ , testProperty "roundtrip" (toParseJSON thEitherTextIntParseJSONUntaggedValue thEitherTextIntToJSONUntaggedValue)
451+ ]
452+ , testGroup "SomeType" [
453+ testProperty "2ElemArray" (is2ElemArray . thSomeTypeToJSON2ElemArray)
454+ , testProperty "TaggedObject" (isTaggedObject . thSomeTypeToJSONTaggedObject)
455+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thSomeTypeToJSONObjectWithSingleField)
456+ , testGroup "roundTrip" [
457+ testProperty "2ElemArray" (toParseJSON thSomeTypeParseJSON2ElemArray thSomeTypeToJSON2ElemArray)
458+ , testProperty "TaggedObject" (toParseJSON thSomeTypeParseJSONTaggedObject thSomeTypeToJSONTaggedObject)
459+ , testProperty "ObjectWithSingleField" (toParseJSON thSomeTypeParseJSONObjectWithSingleField thSomeTypeToJSONObjectWithSingleField)
460+
461+ , testProperty "2ElemArray unary" (toParseJSON1 thSomeTypeLiftParseJSON2ElemArray thSomeTypeLiftToJSON2ElemArray)
462+ , testProperty "TaggedObject unary" (toParseJSON1 thSomeTypeLiftParseJSONTaggedObject thSomeTypeLiftToJSONTaggedObject)
463+ , testProperty "ObjectWithSingleField unary" (toParseJSON1 thSomeTypeLiftParseJSONObjectWithSingleField thSomeTypeLiftToJSONObjectWithSingleField)
464+
465+ ]
466+ ]
467+ , testGroup "Approx" [
468+ testProperty "string" (isString . thApproxToJSONUnwrap)
469+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thApproxToJSONDefault)
470+ , testGroup "roundTrip" [
471+ testProperty "string" (toParseJSON thApproxParseJSONUnwrap thApproxToJSONUnwrap)
472+ , testProperty "ObjectWithSingleField" (toParseJSON thApproxParseJSONDefault thApproxToJSONDefault)
473+ ]
474+ ]
475+ , testGroup "GADT" [
476+ testProperty "string" (isString . thGADTToJSONUnwrap)
477+ , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thGADTToJSONDefault)
478+ , testGroup "roundTrip" [
479+ testProperty "string" (toParseJSON thGADTParseJSONUnwrap thGADTToJSONUnwrap)
480+ , testProperty "ObjectWithSingleField" (toParseJSON thGADTParseJSONDefault thGADTToJSONDefault)
481+ ]
482+ ]
483+ , testGroup "OneConstructor" [
484+ testProperty "default" (isEmptyArray . thOneConstructorToJSONDefault)
485+ , testProperty "Tagged" (isTaggedObject . thOneConstructorToJSONTagged)
486+ , testGroup "roundTrip" [
487+ testProperty "default" (toParseJSON thOneConstructorParseJSONDefault thOneConstructorToJSONDefault)
488+ , testProperty "Tagged" (toParseJSON thOneConstructorParseJSONTagged thOneConstructorToJSONTagged)
489+ ]
490+ ]
491+ ]
492+ , testGroup "toEncoding" [
493+ testProperty "NullaryString" $
494+ thNullaryToJSONString `sameAs` thNullaryToEncodingString
495+ , testProperty "Nullary2ElemArray" $
496+ thNullaryToJSON2ElemArray `sameAs` thNullaryToEncoding2ElemArray
497+ , testProperty "NullaryTaggedObject" $
498+ thNullaryToJSONTaggedObject `sameAs` thNullaryToEncodingTaggedObject
499+ , testProperty "NullaryObjectWithSingleField" $
500+ thNullaryToJSONObjectWithSingleField `sameAs`
501+ thNullaryToEncodingObjectWithSingleField
502+ , testProperty "ApproxUnwrap" $
503+ thApproxToJSONUnwrap `sameAs` thApproxToEncodingUnwrap
504+ , testProperty "ApproxDefault" $
505+ thApproxToJSONDefault `sameAs` thApproxToEncodingDefault
506+
507+ , testProperty "EitherTextInt UntaggedValue" $
508+ thEitherTextIntToJSONUntaggedValue `sameAs` thEitherTextIntToEncodingUntaggedValue
509+
510+ , testProperty "SomeType2ElemArray" $
511+ thSomeTypeToJSON2ElemArray `sameAs` thSomeTypeToEncoding2ElemArray
512+ , testProperty "SomeType2ElemArray unary" $
513+ thSomeTypeLiftToJSON2ElemArray `sameAs1` thSomeTypeLiftToEncoding2ElemArray
514+ , testProperty "SomeType2ElemArray unary agree" $
515+ thSomeTypeToEncoding2ElemArray `sameAs1Agree` thSomeTypeLiftToEncoding2ElemArray
516+
517+ , testProperty "SomeTypeTaggedObject" $
518+ thSomeTypeToJSONTaggedObject `sameAs` thSomeTypeToEncodingTaggedObject
519+ , testProperty "SomeTypeTaggedObject unary" $
520+ thSomeTypeLiftToJSONTaggedObject `sameAs1` thSomeTypeLiftToEncodingTaggedObject
521+ , testProperty "SomeTypeTaggedObject unary agree" $
522+ thSomeTypeToEncodingTaggedObject `sameAs1Agree` thSomeTypeLiftToEncodingTaggedObject
523+
524+ , testProperty "SomeTypeObjectWithSingleField" $
525+ thSomeTypeToJSONObjectWithSingleField `sameAs` thSomeTypeToEncodingObjectWithSingleField
526+ , testProperty "SomeTypeObjectWithSingleField unary" $
527+ thSomeTypeLiftToJSONObjectWithSingleField `sameAs1` thSomeTypeLiftToEncodingObjectWithSingleField
528+ , testProperty "SomeTypeObjectWithSingleField unary agree" $
529+ thSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` thSomeTypeLiftToEncodingObjectWithSingleField
530+
531+ , testProperty "OneConstructorDefault" $
532+ thOneConstructorToJSONDefault `sameAs` thOneConstructorToEncodingDefault
533+ , testProperty "OneConstructorTagged" $
534+ thOneConstructorToJSONTagged `sameAs` thOneConstructorToEncodingTagged
535+ ]
536+ ]
40537 ]
tests/PropertyGeneric.hsdeleted−116
@@ -1,116 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE NoImplicitPrelude #-}
3−
4−module PropertyGeneric ( genericTests ) where
5−
6−import Prelude.Compat
7−
8−import Data.Semigroup (Option(..))
9−import Encoders
10−import Instances ()
11−import Test.Tasty (TestTree, testGroup)
12−import Test.Tasty.QuickCheck (testProperty)
13−import Test.QuickCheck ( (===) )
14−import Types
15−import PropUtils
16−
17−
18−genericTests :: TestTree
19−genericTests =
20− testGroup "generic" [
21− testGroup "toJSON" [
22− testGroup "Nullary" [
23− testProperty "string" (isString . gNullaryToJSONString)
24− , testProperty "2ElemArray" (is2ElemArray . gNullaryToJSON2ElemArray)
25− , testProperty "TaggedObject" (isNullaryTaggedObject . gNullaryToJSONTaggedObject)
26− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gNullaryToJSONObjectWithSingleField)
27− , testGroup "roundTrip" [
28− testProperty "string" (toParseJSON gNullaryParseJSONString gNullaryToJSONString)
29− , testProperty "2ElemArray" (toParseJSON gNullaryParseJSON2ElemArray gNullaryToJSON2ElemArray)
30− , testProperty "TaggedObject" (toParseJSON gNullaryParseJSONTaggedObject gNullaryToJSONTaggedObject)
31− , testProperty "ObjectWithSingleField" (toParseJSON gNullaryParseJSONObjectWithSingleField gNullaryToJSONObjectWithSingleField)
32− ]
33− ]
34− , testGroup "EitherTextInt" [
35− testProperty "UntaggedValue" (isUntaggedValueETI . gEitherTextIntToJSONUntaggedValue)
36− , testProperty "roundtrip" (toParseJSON gEitherTextIntParseJSONUntaggedValue gEitherTextIntToJSONUntaggedValue)
37− ]
38− , testGroup "SomeType" [
39− testProperty "2ElemArray" (is2ElemArray . gSomeTypeToJSON2ElemArray)
40− , testProperty "TaggedObject" (isTaggedObject . gSomeTypeToJSONTaggedObject)
41− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . gSomeTypeToJSONObjectWithSingleField)
42− , testGroup "roundTrip" [
43− testProperty "2ElemArray" (toParseJSON gSomeTypeParseJSON2ElemArray gSomeTypeToJSON2ElemArray)
44− , testProperty "TaggedObject" (toParseJSON gSomeTypeParseJSONTaggedObject gSomeTypeToJSONTaggedObject)
45− , testProperty "ObjectWithSingleField" (toParseJSON gSomeTypeParseJSONObjectWithSingleField gSomeTypeToJSONObjectWithSingleField)
46−
47− , testProperty "2ElemArray unary" (toParseJSON1 gSomeTypeLiftParseJSON2ElemArray gSomeTypeLiftToJSON2ElemArray)
48− , testProperty "TaggedObject unary" (toParseJSON1 gSomeTypeLiftParseJSONTaggedObject gSomeTypeLiftToJSONTaggedObject)
49− , testProperty "ObjectWithSingleField unary" (toParseJSON1 gSomeTypeLiftParseJSONObjectWithSingleField gSomeTypeLiftToJSONObjectWithSingleField)
50− ]
51− ]
52− , testGroup "OneConstructor" [
53− testProperty "default" (isEmptyArray . gOneConstructorToJSONDefault)
54− , testProperty "Tagged" (isTaggedObject . gOneConstructorToJSONTagged)
55− , testGroup "roundTrip" [
56− testProperty "default" (toParseJSON gOneConstructorParseJSONDefault gOneConstructorToJSONDefault)
57− , testProperty "Tagged" (toParseJSON gOneConstructorParseJSONTagged gOneConstructorToJSONTagged)
58− ]
59− ]
60− , testGroup "OptionField" [
61− testProperty "like Maybe" $
62− \x -> gOptionFieldToJSON (OptionField (Option x)) === thMaybeFieldToJSON (MaybeField x)
63− , testProperty "roundTrip" (toParseJSON gOptionFieldParseJSON gOptionFieldToJSON)
64− ]
65− ]
66− , testGroup "toEncoding" [
67− testProperty "NullaryString" $
68− gNullaryToJSONString `sameAs` gNullaryToEncodingString
69− , testProperty "Nullary2ElemArray" $
70− gNullaryToJSON2ElemArray `sameAs` gNullaryToEncoding2ElemArray
71− , testProperty "NullaryTaggedObject" $
72− gNullaryToJSONTaggedObject `sameAs` gNullaryToEncodingTaggedObject
73− , testProperty "NullaryObjectWithSingleField" $
74− gNullaryToJSONObjectWithSingleField `sameAs`
75− gNullaryToEncodingObjectWithSingleField
76− -- , testProperty "ApproxUnwrap" $
77− -- gApproxToJSONUnwrap `sameAs` gApproxToEncodingUnwrap
78− , testProperty "ApproxDefault" $
79− gApproxToJSONDefault `sameAs` gApproxToEncodingDefault
80−
81− , testProperty "EitherTextInt UntaggedValue" $
82− gEitherTextIntToJSONUntaggedValue `sameAs` gEitherTextIntToEncodingUntaggedValue
83−
84− , testProperty "SomeType2ElemArray" $
85− gSomeTypeToJSON2ElemArray `sameAs` gSomeTypeToEncoding2ElemArray
86− , testProperty "SomeType2ElemArray unary" $
87− gSomeTypeLiftToJSON2ElemArray `sameAs1` gSomeTypeLiftToEncoding2ElemArray
88− , testProperty "SomeType2ElemArray unary agree" $
89− gSomeTypeToEncoding2ElemArray `sameAs1Agree` gSomeTypeLiftToEncoding2ElemArray
90−
91− , testProperty "SomeTypeTaggedObject" $
92− gSomeTypeToJSONTaggedObject `sameAs` gSomeTypeToEncodingTaggedObject
93− , testProperty "SomeTypeTaggedObject unary" $
94− gSomeTypeLiftToJSONTaggedObject `sameAs1` gSomeTypeLiftToEncodingTaggedObject
95− , testProperty "SomeTypeTaggedObject unary agree" $
96− gSomeTypeToEncodingTaggedObject `sameAs1Agree` gSomeTypeLiftToEncodingTaggedObject
97−
98− , testProperty "SomeTypeObjectWithSingleField" $
99− gSomeTypeToJSONObjectWithSingleField `sameAs` gSomeTypeToEncodingObjectWithSingleField
100− , testProperty "SomeTypeObjectWithSingleField unary" $
101− gSomeTypeLiftToJSONObjectWithSingleField `sameAs1` gSomeTypeLiftToEncodingObjectWithSingleField
102− , testProperty "SomeTypeObjectWithSingleField unary agree" $
103− gSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` gSomeTypeLiftToEncodingObjectWithSingleField
104−
105− , testProperty "SomeTypeOmitNothingFields" $
106− gSomeTypeToJSONOmitNothingFields `sameAs` gSomeTypeToEncodingOmitNothingFields
107−
108− , testProperty "OneConstructorDefault" $
109− gOneConstructorToJSONDefault `sameAs` gOneConstructorToEncodingDefault
110− , testProperty "OneConstructorTagged" $
111− gOneConstructorToJSONTagged `sameAs` gOneConstructorToEncodingTagged
112−
113− , testProperty "OptionField" $
114− gOptionFieldToJSON `sameAs` gOptionFieldToEncoding
115− ]
116− ]
tests/PropertyKeys.hsdeleted−40
@@ -1,40 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyKeys ( keysTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Time (Day, LocalTime, TimeOfDay, UTCTime)
8−import Data.Version (Version)
9−import Instances ()
10−import Numeric.Natural (Natural)
11−import Test.Tasty (TestTree, testGroup)
12−import Test.Tasty.QuickCheck (testProperty)
13−import Types
14−import qualified Data.Text as T
15−import qualified Data.Text.Lazy as LT
16−import qualified Data.UUID.Types as UUID
17−import PropUtils
18−
19−
20−keysTests :: TestTree
21−keysTests =
22− testGroup "roundTrip Key"
23− [ testProperty "Bool" $ roundTripKey True
24− , testProperty "Text" $ roundTripKey (undefined :: T.Text)
25− , testProperty "String" $ roundTripKey (undefined :: String)
26− , testProperty "Int" $ roundTripKey (undefined :: Int)
27− , testProperty "[Text]" $ roundTripKey (undefined :: LogScaled [T.Text])
28− , testProperty "(Int,Char)" $ roundTripKey (undefined :: (Int,Char))
29− , testProperty "Integer" $ roundTripKey (undefined :: Integer)
30− , testProperty "Natural" $ roundTripKey (undefined :: Natural)
31− , testProperty "Float" $ roundTripKey (undefined :: Float)
32− , testProperty "Double" $ roundTripKey (undefined :: Double)
33− , testProperty "Day" $ roundTripKey (undefined :: Day)
34− , testProperty "LocalTime" $ roundTripKey (undefined :: LocalTime)
35− , testProperty "TimeOfDay" $ roundTripKey (undefined :: TimeOfDay)
36− , testProperty "UTCTime" $ roundTripKey (undefined :: UTCTime)
37− , testProperty "Version" $ roundTripKey (undefined :: Version)
38− , testProperty "Lazy Text" $ roundTripKey (undefined :: LT.Text)
39− , testProperty "UUID" $ roundTripKey UUID.nil
40− ]
tests/PropertyRTFunctors.hsdeleted−51
@@ -1,51 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyRTFunctors ( roundTripFunctorsTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Functor.Compose (Compose (..))
8−import Instances ()
9−import Test.Tasty (TestTree, testGroup)
10−import Test.Tasty.QuickCheck (testProperty)
11−import Types
12−import PropUtils
13−
14−
15−roundTripFunctorsTests :: TestTree
16−roundTripFunctorsTests =
17− testGroup "functors"
18− [ testProperty "Identity Char" $ roundTripEq (undefined :: I Int)
19−
20− , testProperty "Identity Char" $ roundTripEq (undefined :: I Char)
21− , testProperty "Identity [Char]" $ roundTripEq (undefined :: I String)
22− , testProperty "[Identity Char]" $ roundTripEq (undefined :: [I Char])
23−
24− , testProperty "Compose I I Int" $ roundTripEq (undefined :: LogScaled (Compose I I Int))
25− , testProperty "Compose [] I Int" $ roundTripEq (undefined :: LogScaled (Compose [] I Int))
26− , testProperty "Compose I [] Int" $ roundTripEq (undefined :: LogScaled (Compose I [] Int))
27− , testProperty "Compose [] [] Int" $ roundTripEq (undefined :: LogScaled (Compose [] [] Int))
28−
29− , testProperty "Compose I I Char" $ roundTripEq (undefined :: LogScaled (Compose I I Char))
30− , testProperty "Compose [] I Char" $ roundTripEq (undefined :: LogScaled (Compose [] I Char))
31− , testProperty "Compose I [] Char" $ roundTripEq (undefined :: LogScaled (Compose I [] Char))
32− , testProperty "Compose [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose [] [] Char))
33−
34− , testProperty "Compose3 I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I I Char))
35− , testProperty "Compose3 I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] I Char))
36− , testProperty "Compose3 I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I I [] Char))
37− , testProperty "Compose3 I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 I [] [] Char))
38− , testProperty "Compose3 [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I I Char))
39− , testProperty "Compose3 [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] I Char))
40− , testProperty "Compose3 [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] I [] Char))
41− , testProperty "Compose3 [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3 [] [] [] Char))
42−
43− , testProperty "Compose3' I I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I I Char))
44− , testProperty "Compose3' I [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] I Char))
45− , testProperty "Compose3' I I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I I [] Char))
46− , testProperty "Compose3' I [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' I [] [] Char))
47− , testProperty "Compose3' [] I I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I I Char))
48− , testProperty "Compose3' [] [] I Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] I Char))
49− , testProperty "Compose3' [] I [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] I [] Char))
50− , testProperty "Compose3' [] [] [] Char" $ roundTripEq (undefined :: LogScaled (Compose3' [] [] [] Char))
51− ]
tests/PropertyRoundTrip.hsdeleted−74
@@ -1,74 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyRoundTrip ( roundTripTests ) where
4−
5−import Prelude.Compat
6−
7−import Control.Applicative (Const)
8−import Data.Aeson.Types
9−import Data.DList (DList)
10−import Data.List.NonEmpty (NonEmpty)
11−import Data.Proxy (Proxy)
12−import Data.Ratio (Ratio)
13−import Data.Sequence (Seq)
14−import Data.Tagged (Tagged)
15−import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
16−import Data.Version (Version)
17−import Data.Time.Calendar.Compat (CalendarDiffDays, DayOfWeek)
18−import Data.Time.LocalTime.Compat (CalendarDiffTime)
19−import Data.Time.Clock.System.Compat (SystemTime)
20−import Instances ()
21−import Numeric.Natural (Natural)
22−import Test.Tasty (TestTree, testGroup)
23−import Test.Tasty.QuickCheck (testProperty)
24−import Types
25−import qualified Data.Text as T
26−import qualified Data.Text.Lazy as LT
27−import qualified Data.UUID.Types as UUID
28−import PropUtils
29−import PropertyRTFunctors
30−
31−
32−roundTripTests :: TestTree
33−roundTripTests =
34− testGroup "roundTrip" [
35− testProperty "Bool" $ roundTripEq True
36− , testProperty "Double" $ roundTripEq (1 :: Approx Double)
37− , testProperty "Int" $ roundTripEq (1 :: Int)
38− , testProperty "NonEmpty Char" $ roundTripEq (undefined :: NonEmpty Char)
39− , testProperty "Integer" $ roundTripEq (1 :: Integer)
40− , testProperty "String" $ roundTripEq ("" :: String)
41− , testProperty "Text" $ roundTripEq T.empty
42− , testProperty "Lazy Text" $ roundTripEq LT.empty
43− , testProperty "Foo" $ roundTripEq (undefined :: Foo)
44− , testProperty "Day" $ roundTripEq (undefined :: Day)
45− , testProperty "BCE Day" $ roundTripEq (undefined :: BCEDay)
46− , testProperty "DotNetTime" $ roundTripEq (undefined :: Approx DotNetTime)
47− , testProperty "LocalTime" $ roundTripEq (undefined :: LocalTime)
48− , testProperty "TimeOfDay" $ roundTripEq (undefined :: TimeOfDay)
49− , testProperty "UTCTime" $ roundTripEq (undefined :: UTCTime)
50− , testProperty "ZonedTime" $ roundTripEq (undefined :: ZonedTime)
51− , testProperty "NominalDiffTime" $ roundTripEq (undefined :: NominalDiffTime)
52− , testProperty "DiffTime" $ roundTripEq (undefined :: DiffTime)
53− , testProperty "DayOfWeek" $ roundTripEq (undefined :: DayOfWeek)
54− , testProperty "SystemTime" $ roundTripEq (undefined :: SystemTime)
55− , testProperty "CalendarDiffTime" $ roundTripEq (undefined :: CalendarDiffTime)
56− , testProperty "CalendarDiffDays" $ roundTripEq (undefined :: CalendarDiffDays)
57− , testProperty "Version" $ roundTripEq (undefined :: Version)
58− , testProperty "Natural" $ roundTripEq (undefined :: Natural)
59− , testProperty "Proxy" $ roundTripEq (undefined :: Proxy Int)
60− , testProperty "Tagged" $ roundTripEq (undefined :: Tagged Int Char)
61− , testProperty "Const" $ roundTripEq (undefined :: Const Int Char)
62− , testProperty "DList" $ roundTripEq (undefined :: DList Int)
63− , testProperty "Seq" $ roundTripEq (undefined :: Seq Int)
64− , testProperty "Rational" $ roundTripEq (undefined :: Rational)
65− , testProperty "Ratio Int" $ roundTripEq (undefined :: Ratio Int)
66− , testProperty "UUID" $ roundTripEq UUID.nil
67− , roundTripFunctorsTests
68− , testGroup "ghcGenerics" [
69− testProperty "OneConstructor" $ roundTripEq OneConstructor
70− , testProperty "Product2" $ roundTripEq (undefined :: Product2 Int Bool)
71− , testProperty "Product6" $ roundTripEq (undefined :: P6)
72− , testProperty "Sum4" $ roundTripEq (undefined :: S4)
73− ]
74− ]
tests/PropertyTH.hsdeleted−130
@@ -1,130 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−module PropertyTH ( templateHaskellTests ) where
4−
5−import Prelude.Compat
6−
7−import Data.Semigroup (Option(..))
8−import Encoders
9−import Instances ()
10−import Test.Tasty (TestTree, testGroup)
11−import Test.Tasty.QuickCheck (testProperty)
12−import Test.QuickCheck ( (===) )
13−import Types
14−import PropUtils
15−
16−
17−templateHaskellTests :: TestTree
18−templateHaskellTests =
19− testGroup "template-haskell" [
20− testGroup "toJSON" [
21− testGroup "Nullary" [
22− testProperty "string" (isString . thNullaryToJSONString)
23− , testProperty "2ElemArray" (is2ElemArray . thNullaryToJSON2ElemArray)
24− , testProperty "TaggedObject" (isNullaryTaggedObject . thNullaryToJSONTaggedObject)
25− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thNullaryToJSONObjectWithSingleField)
26−
27− , testGroup "roundTrip" [
28− testProperty "string" (toParseJSON thNullaryParseJSONString thNullaryToJSONString)
29− , testProperty "2ElemArray" (toParseJSON thNullaryParseJSON2ElemArray thNullaryToJSON2ElemArray)
30− , testProperty "TaggedObject" (toParseJSON thNullaryParseJSONTaggedObject thNullaryToJSONTaggedObject)
31− , testProperty "ObjectWithSingleField" (toParseJSON thNullaryParseJSONObjectWithSingleField thNullaryToJSONObjectWithSingleField)
32− ]
33− ]
34− , testGroup "EitherTextInt" [
35− testProperty "UntaggedValue" (isUntaggedValueETI . thEitherTextIntToJSONUntaggedValue)
36− , testProperty "roundtrip" (toParseJSON thEitherTextIntParseJSONUntaggedValue thEitherTextIntToJSONUntaggedValue)
37− ]
38− , testGroup "SomeType" [
39− testProperty "2ElemArray" (is2ElemArray . thSomeTypeToJSON2ElemArray)
40− , testProperty "TaggedObject" (isTaggedObject . thSomeTypeToJSONTaggedObject)
41− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thSomeTypeToJSONObjectWithSingleField)
42− , testGroup "roundTrip" [
43− testProperty "2ElemArray" (toParseJSON thSomeTypeParseJSON2ElemArray thSomeTypeToJSON2ElemArray)
44− , testProperty "TaggedObject" (toParseJSON thSomeTypeParseJSONTaggedObject thSomeTypeToJSONTaggedObject)
45− , testProperty "ObjectWithSingleField" (toParseJSON thSomeTypeParseJSONObjectWithSingleField thSomeTypeToJSONObjectWithSingleField)
46−
47− , testProperty "2ElemArray unary" (toParseJSON1 thSomeTypeLiftParseJSON2ElemArray thSomeTypeLiftToJSON2ElemArray)
48− , testProperty "TaggedObject unary" (toParseJSON1 thSomeTypeLiftParseJSONTaggedObject thSomeTypeLiftToJSONTaggedObject)
49− , testProperty "ObjectWithSingleField unary" (toParseJSON1 thSomeTypeLiftParseJSONObjectWithSingleField thSomeTypeLiftToJSONObjectWithSingleField)
50−
51− ]
52− ]
53− , testGroup "Approx" [
54− testProperty "string" (isString . thApproxToJSONUnwrap)
55− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thApproxToJSONDefault)
56− , testGroup "roundTrip" [
57− testProperty "string" (toParseJSON thApproxParseJSONUnwrap thApproxToJSONUnwrap)
58− , testProperty "ObjectWithSingleField" (toParseJSON thApproxParseJSONDefault thApproxToJSONDefault)
59− ]
60− ]
61− , testGroup "GADT" [
62− testProperty "string" (isString . thGADTToJSONUnwrap)
63− , testProperty "ObjectWithSingleField" (isObjectWithSingleField . thGADTToJSONDefault)
64− , testGroup "roundTrip" [
65− testProperty "string" (toParseJSON thGADTParseJSONUnwrap thGADTToJSONUnwrap)
66− , testProperty "ObjectWithSingleField" (toParseJSON thGADTParseJSONDefault thGADTToJSONDefault)
67− ]
68− ]
69− , testGroup "OneConstructor" [
70− testProperty "default" (isEmptyArray . thOneConstructorToJSONDefault)
71− , testProperty "Tagged" (isTaggedObject . thOneConstructorToJSONTagged)
72− , testGroup "roundTrip" [
73− testProperty "default" (toParseJSON thOneConstructorParseJSONDefault thOneConstructorToJSONDefault)
74− , testProperty "Tagged" (toParseJSON thOneConstructorParseJSONTagged thOneConstructorToJSONTagged)
75− ]
76− ]
77− , testGroup "OptionField" [
78− testProperty "like Maybe" $
79− \x -> thOptionFieldToJSON (OptionField (Option x)) === thMaybeFieldToJSON (MaybeField x)
80− , testProperty "roundTrip" (toParseJSON thOptionFieldParseJSON thOptionFieldToJSON)
81− ]
82− ]
83− , testGroup "toEncoding" [
84− testProperty "NullaryString" $
85− thNullaryToJSONString `sameAs` thNullaryToEncodingString
86− , testProperty "Nullary2ElemArray" $
87− thNullaryToJSON2ElemArray `sameAs` thNullaryToEncoding2ElemArray
88− , testProperty "NullaryTaggedObject" $
89− thNullaryToJSONTaggedObject `sameAs` thNullaryToEncodingTaggedObject
90− , testProperty "NullaryObjectWithSingleField" $
91− thNullaryToJSONObjectWithSingleField `sameAs`
92− thNullaryToEncodingObjectWithSingleField
93− , testProperty "ApproxUnwrap" $
94− thApproxToJSONUnwrap `sameAs` thApproxToEncodingUnwrap
95− , testProperty "ApproxDefault" $
96− thApproxToJSONDefault `sameAs` thApproxToEncodingDefault
97−
98− , testProperty "EitherTextInt UntaggedValue" $
99− thEitherTextIntToJSONUntaggedValue `sameAs` thEitherTextIntToEncodingUntaggedValue
100−
101− , testProperty "SomeType2ElemArray" $
102− thSomeTypeToJSON2ElemArray `sameAs` thSomeTypeToEncoding2ElemArray
103− , testProperty "SomeType2ElemArray unary" $
104− thSomeTypeLiftToJSON2ElemArray `sameAs1` thSomeTypeLiftToEncoding2ElemArray
105− , testProperty "SomeType2ElemArray unary agree" $
106− thSomeTypeToEncoding2ElemArray `sameAs1Agree` thSomeTypeLiftToEncoding2ElemArray
107−
108− , testProperty "SomeTypeTaggedObject" $
109− thSomeTypeToJSONTaggedObject `sameAs` thSomeTypeToEncodingTaggedObject
110− , testProperty "SomeTypeTaggedObject unary" $
111− thSomeTypeLiftToJSONTaggedObject `sameAs1` thSomeTypeLiftToEncodingTaggedObject
112− , testProperty "SomeTypeTaggedObject unary agree" $
113− thSomeTypeToEncodingTaggedObject `sameAs1Agree` thSomeTypeLiftToEncodingTaggedObject
114−
115− , testProperty "SomeTypeObjectWithSingleField" $
116− thSomeTypeToJSONObjectWithSingleField `sameAs` thSomeTypeToEncodingObjectWithSingleField
117− , testProperty "SomeTypeObjectWithSingleField unary" $
118− thSomeTypeLiftToJSONObjectWithSingleField `sameAs1` thSomeTypeLiftToEncodingObjectWithSingleField
119− , testProperty "SomeTypeObjectWithSingleField unary agree" $
120− thSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` thSomeTypeLiftToEncodingObjectWithSingleField
121−
122− , testProperty "OneConstructorDefault" $
123− thOneConstructorToJSONDefault `sameAs` thOneConstructorToEncodingDefault
124− , testProperty "OneConstructorTagged" $
125− thOneConstructorToJSONTagged `sameAs` thOneConstructorToEncodingTagged
126−
127− , testProperty "OptionField" $
128− thOptionFieldToJSON `sameAs` thOptionFieldToEncoding
129− ]
130− ]
tests/SerializationFormatSpec.hs+30−53
@@ -1,9 +1,10 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DeriveGeneric #-}
33{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
54{-# LANGUAGE OverloadedStrings #-}
5+#if __GLASGOW_HASKELL__ >= 708
66{-# LANGUAGE DataKinds #-}
7+#endif
78
89------------------------------------------------------------------------------
910-- These tests assert that the JSON serialization doesn't change by accident.
@@ -14,6 +15,7 @@ module SerializationFormatSpec
1415 tests
1516 ) where
1617
18+import Prelude ()
1719import Prelude.Compat
1820
1921import Control.Applicative (Const(..))
@@ -30,14 +32,12 @@ import Data.Proxy (Proxy(..))
3032import Data.Scientific (Scientific)
3133import Data.Tagged (Tagged(..))
3234import Data.Time (fromGregorian)
33−import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
34−import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
35−import Data.Time.Clock.System.Compat (SystemTime (..))
3635import Data.Word (Word8)
3736import GHC.Generics (Generic)
3837import Instances ()
39−import Test.Tasty (TestTree, testGroup)
40−import Test.Tasty.HUnit (assertFailure, assertEqual, testCase)
38+import Test.Framework (Test, testGroup)
39+import Test.Framework.Providers.HUnit (testCase)
40+import Test.HUnit (assertFailure, assertEqual)
4141import Types (Approx(..), Compose3, Compose3', I)
4242import qualified Data.ByteString.Lazy.Char8 as L
4343import qualified Data.DList as DList
@@ -54,7 +54,7 @@ import qualified Data.Tree as Tree
5454import qualified Data.UUID.Types as UUID
5555import qualified Data.Vector as Vector
5656
57−tests :: [TestTree]
57+tests :: [Test]
5858tests =
5959 [
6060 testGroup "To JSON representation" $ fmap assertJsonEncodingExample jsonEncodingExamples
@@ -69,14 +69,13 @@ jsonExamples =
6969 example "Either Left" "{\"Left\":1}" (Left 1 :: Either Int Int)
7070 , example "Either Right" "{\"Right\":1}" (Right 1 :: Either Int Int)
7171 , example "Nothing" "null" (Nothing :: Maybe Int)
72− -- Maybe serialising is lossy
73− -- https://github.com/bos/aeson/issues/376
74− , Example "Just Nothing" ["null"] (Just Nothing :: Maybe (Maybe Int)) Nothing
7572 , example "Just" "1" (Just 1 :: Maybe Int)
7673 , example "Proxy Int" "null" (Proxy :: Proxy Int)
7774 , example "Tagged Char Int" "1" (Tagged 1 :: Tagged Char Int)
75+#if __GLASGOW_HASKELL__ >= 708
7876 -- Test Tagged instance is polykinded
7977 , example "Tagged 123 Int" "1" (Tagged 1 :: Tagged 123 Int)
78+#endif
8079 , example "Const Char Int" "\"c\"" (Const 'c' :: Const Char Int)
8180 , example "Tuple" "[1,2]" ((1, 2) :: (Int, Int))
8281 , example "NonEmpty" "[1,2,3]" (1 :| [2, 3] :: NonEmpty Int)
@@ -84,22 +83,22 @@ jsonExamples =
8483 , example "DList" "[1,2,3]" (DList.fromList [1, 2, 3] :: DList.DList Int)
8584 , example "()" "[]" ()
8685
87− , ndExample "HashMap Int Int"
86+ , Example "HashMap Int Int"
8887 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
8988 (HM.fromList [(0,1),(2,3)] :: HM.HashMap Int Int)
90− , ndExample "Map Int Int"
89+ , Example "Map Int Int"
9190 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
9291 (M.fromList [(0,1),(2,3)] :: M.Map Int Int)
93− , ndExample "Map (Tagged Int Int) Int"
92+ , Example "Map (Tagged Int Int) Int"
9493 [ "{\"0\":1,\"2\":3}", "{\"2\":3,\"0\":1}"]
9594 (M.fromList [(Tagged 0,1),(Tagged 2,3)] :: M.Map (Tagged Int Int) Int)
9695 , example "Map [Int] Int"
9796 "[[[0],1],[[2],3]]"
9897 (M.fromList [([0],1),([2],3)] :: M.Map [Int] Int)
99− , ndExample "Map [Char] Int"
98+ , Example "Map [Char] Int"
10099 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
101100 (M.fromList [("ab",1),("cd",3)] :: M.Map String Int)
102− , ndExample "Map [I Char] Int"
101+ , Example "Map [I Char] Int"
103102 [ "{\"ab\":1,\"cd\":3}", "{\"cd\":3,\"ab\":1}" ]
104103 (M.fromList [(map pure "ab",1),(map pure "cd",3)] :: M.Map [I Char] Int)
105104
@@ -195,27 +194,16 @@ jsonExamples =
195194 , example "Semigroup.WrappedMonoid Int" "2" (Semigroup.WrapMonoid 2 :: Semigroup.WrappedMonoid Int)
196195 , example "Semigroup.Option Just" "2" (pure 2 :: Semigroup.Option Int)
197196 , example "Semigroup.Option Nothing" "null" (Semigroup.Option (Nothing :: Maybe Bool))
198−
199− -- time 1.9
200− , example "SystemTime" "123.123456789" (MkSystemTime 123 123456789)
201− , Example "SystemTime" ["124.23456789"]
202− (MkSystemTime 123 1234567890)
203− (MkSystemTime 124 234567890)
204− , ndExample "CalendarDiffTime"
205− [ "{\"months\":12,\"time\":456.789}", "{\"time\":456.789,\"months\":12}" ]
206− (CalendarDiffTime 12 456.789)
207− , ndExample "CalendarDiffDays"
208− [ "{\"months\":12,\"days\":20}", "{\"days\":20,\"months\":12}" ]
209− (CalendarDiffDays 12 20)
210− , example "DayOfWeek" "\"monday\"" Monday
211197 ]
212198
213199jsonEncodingExamples :: [Example]
214200jsonEncodingExamples =
215201 [
202+ -- Maybe serialising is lossy
203+ -- https://github.com/bos/aeson/issues/376
204+ example "Just Nothing" "null" (Just Nothing :: Maybe (Maybe Int))
216205 -- infinities cannot be recovered, null is decoded as NaN
217− example "inf :: Double" "null" (Approx $ 1/0 :: Approx Double)
218− , example "-inf :: Double" "null" (Approx $ -1/0 :: Approx Double)
206+ , example "inf :: Double" "null" (Approx $ 1/0 :: Approx Double)
219207 ]
220208
221209jsonDecodingExamples :: [Example]
@@ -237,27 +225,16 @@ jsonDecodingExamples = [
237225 ]
238226
239227data Example where
240− Example
241− :: (Eq a, Show a, ToJSON a, FromJSON a)
242− => String -- name
243− -> [L.ByteString] -- encoded variants
244− -> a -- input
245− -> a -- decoded
246− -> Example -- empty bytestring will fail, any p [] == False
247−
248− MaybeExample
249− :: (Eq a, Show a, FromJSON a)
250− => String -> L.ByteString -> Maybe a -> Example
228+ Example
229+ :: (Eq a, Show a, ToJSON a, FromJSON a)
230+ => String -> [L.ByteString] -> a -> Example -- empty bytestring will fail, any p [] == False
231+ MaybeExample
232+ :: (Eq a, Show a, FromJSON a)
233+ => String -> L.ByteString -> Maybe a -> Example
251234
252235example :: (Eq a, Show a, ToJSON a, FromJSON a)
253236 => String -> L.ByteString -> a -> Example
254−example n bs x = Example n [bs] x x
255−
256−-- | Non-deterministic example, input encodes to some of bytestrings.
257−ndExample :: (Eq a, Show a, ToJSON a, FromJSON a)
258− => String -> [L.ByteString] -> a -> Example
259−ndExample n bss x = Example n bss x x
260−
237+example n bs x = Example n [bs] x
261238
262239data MyEither a b = MyLeft a | MyRight b
263240 deriving (Generic, Show, Eq)
@@ -269,17 +246,17 @@ instance (ToJSON a, ToJSON b) => ToJSON (MyEither a b) where
269246instance (FromJSON a, FromJSON b) => FromJSON (MyEither a b) where
270247 parseJSON = genericParseJSON defaultOptions { sumEncoding = UntaggedValue }
271248
272−assertJsonExample :: Example -> TestTree
273−assertJsonExample (Example name bss val val') = testCase name $ do
249+assertJsonExample :: Example -> Test
250+assertJsonExample (Example name bss val) = testCase name $ do
274251 assertSomeEqual "encode" bss (encode val)
275252 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
276253 for_ bss $ \bs ->
277− assertEqual "decode" (Just val') (decode bs)
254+ assertEqual "decode" (Just val) (decode bs)
278255assertJsonExample (MaybeExample name bs mval) = testCase name $
279256 assertEqual "decode" mval (decode bs)
280257
281−assertJsonEncodingExample :: Example -> TestTree
282−assertJsonEncodingExample (Example name bss val _) = testCase name $ do
258+assertJsonEncodingExample :: Example -> Test
259+assertJsonEncodingExample (Example name bss val) = testCase name $ do
283260 assertSomeEqual "encode" bss (encode val)
284261 assertSomeEqual "encode/via value" bss (encode $ toJSON val)
285262assertJsonEncodingExample (MaybeExample name _ _) = testCase name $
tests/Tests.hs+3−5
@@ -1,10 +1,9 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
31module Main (main) where
42
3+import Prelude ()
54import Prelude.Compat
65
7−import Test.Tasty (defaultMain, testGroup)
6+import Test.Framework (defaultMain)
87import qualified DataFamilies.Properties as DF
98import qualified Properties
109import qualified UnitTests
@@ -12,5 +11,4 @@ import qualified UnitTests
1211main :: IO ()
1312main = do
1413 ioTests <- UnitTests.ioTests
15− let allTests = DF.tests : Properties.tests : UnitTests.tests : ioTests
16− defaultMain (testGroup "tests" allTests)
14+ defaultMain (DF.tests : Properties.tests : UnitTests.tests : ioTests)
tests/Types.hs+5−10
@@ -4,21 +4,19 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE GADTs #-}
66{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE NoImplicitPrelude #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
97{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
109
1110module Types (module Types) where
1211
12+import Prelude ()
1313import Prelude.Compat
1414
1515import Math.NumberTheory.Logarithms (intLog2)
16−import Control.Applicative ((<$>))
1716import Data.Data
1817import Data.Functor.Compose (Compose (..))
1918import Data.Functor.Identity (Identity (..))
2019import Data.Hashable (Hashable (..))
21−import Data.Semigroup (Option)
2220import Data.Text
2321import Data.Time (Day (..), fromGregorian)
2422import GHC.Generics
@@ -106,10 +104,6 @@ deriving instance Data (GADT String)
106104deriving instance Eq (GADT a)
107105deriving instance Show (GADT a)
108106
109−newtype MaybeField = MaybeField { maybeField :: Maybe Int }
110−newtype OptionField = OptionField { optionField :: Option Int }
111− deriving (Eq, Show)
112−
113107deriving instance Generic Foo
114108deriving instance Generic UFoo
115109deriving instance Generic OneConstructor
@@ -119,8 +113,9 @@ deriving instance Generic (Sum4 a b c d)
119113deriving instance Generic (Approx a)
120114deriving instance Generic Nullary
121115deriving instance Generic (SomeType a)
116+#if __GLASGOW_HASKELL__ >= 706
122117deriving instance Generic1 SomeType
123−deriving instance Generic OptionField
118+#endif
124119deriving instance Generic EitherTextInt
125120
126121failure :: Show a => String -> String -> a -> Property
@@ -151,7 +146,7 @@ instance Hashable a => Hashable (LogScaled a) where
151146 hashWithSalt salt (LogScaled a) = hashWithSalt salt a
152147
153148instance Arbitrary a => Arbitrary (LogScaled a) where
154− arbitrary = LogScaled <$> scale (\x -> intLog2 $ x + 1) arbitrary
149+ arbitrary = fmap LogScaled $ scale (\x -> intLog2 $ x + 1) arbitrary
155150 shrink = fmap LogScaled . shrink . getLogScaled
156151
157152instance ToJSON a => ToJSON (LogScaled a) where
tests/UnitTests.hs+39−276
@@ -1,72 +1,55 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DeriveGeneric #-}
33{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE NoImplicitPrelude #-}
54{-# LANGUAGE OverloadedStrings #-}
65{-# LANGUAGE ScopedTypeVariables #-}
76{-# LANGUAGE TemplateHaskell #-}
8−{-# LANGUAGE QuasiQuotes #-}
97
108-- For Data.Aeson.Types.camelTo
119{-# OPTIONS_GHC -fno-warn-deprecations #-}
1210
13−#if MIN_VERSION_base(4,9,0)
14−{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
15−#endif
16−
1711module UnitTests
1812 (
1913 ioTests
2014 , tests
21− , withEmbeddedJSONTest
2215 ) where
2316
17+import Prelude ()
2418import Prelude.Compat
2519
2620import Control.Monad (forM, forM_)
27−import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject, withEmbeddedJSON)
21+import Data.Aeson ((.=), (.:), (.:?), (.:!), FromJSON(..), FromJSONKeyFunction(..), FromJSONKey(..), ToJSON1(..), decode, eitherDecode, encode, fromJSON, genericParseJSON, genericToEncoding, genericToJSON, object, withObject)
2822import Data.Aeson.Internal (JSONPathElement(..), formatError)
29−import Data.Aeson.QQ.Simple (aesonQQ)
3023import Data.Aeson.TH (deriveJSON, deriveToJSON, deriveToJSON1)
3124import Data.Aeson.Text (encodeToTextBuilder)
32−import Data.Aeson.Parser
33− ( json, jsonLast, jsonAccum, jsonNoDup
34− , json', jsonLast', jsonAccum', jsonNoDup')
35−import Data.Aeson.Types
36− ( Options(..), Result(Success, Error), ToJSON(..)
37− , Value(Array, Bool, Null, Number, Object, String), camelTo, camelTo2
38− , defaultOptions, formatPath, formatRelativePath, omitNothingFields, parse)
39−import Data.Attoparsec.ByteString (Parser, parseOnly)
25+import Data.Aeson.Types (Options(..), Result(Success), ToJSON(..), Value(Null), camelTo, camelTo2, defaultOptions, omitNothingFields, parse)
4026import Data.Char (toUpper)
4127import Data.Either.Compat (isLeft, isRight)
4228import Data.Hashable (hash)
43−import Data.HashMap.Strict (HashMap)
44−import Data.List (sort, isSuffixOf)
29+import Data.List (sort)
4530import Data.Maybe (fromMaybe)
46−import Data.Scientific (Scientific, scientific)
31+import Data.Sequence (Seq)
4732import Data.Tagged (Tagged(..))
4833import Data.Text (Text)
4934import Data.Time (UTCTime)
50−import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
35+import Data.Time.Format (parseTime)
36+import Data.Time.Locale.Compat (defaultTimeLocale)
5137import GHC.Generics (Generic)
5238import Instances ()
53−import Numeric.Natural (Natural)
5439import System.Directory (getDirectoryContents)
5540import System.FilePath ((</>), takeExtension, takeFileName)
56−import Test.Tasty (TestTree, testGroup)
57−import Test.Tasty.HUnit (Assertion, assertBool, assertFailure, assertEqual, testCase, (@?=))
41+import Test.Framework (Test, testGroup)
42+import Test.Framework.Providers.HUnit (testCase)
43+import Test.HUnit (Assertion, assertBool, assertFailure, assertEqual)
5844import Text.Printf (printf)
5945import UnitTests.NullaryConstructors (nullaryConstructors)
60−import qualified Data.ByteString as S
6146import qualified Data.ByteString.Base16.Lazy as LBase16
6247import qualified Data.ByteString.Lazy.Char8 as L
6348import qualified Data.HashSet as HashSet
64−import qualified Data.HashMap.Lazy as HashMap
6549import qualified Data.Text.Lazy as LT
6650import qualified Data.Text.Lazy.Builder as TLB
6751import qualified Data.Text.Lazy.Encoding as LT
6852import qualified Data.Text.Lazy.Encoding as TLE
69−import qualified Data.Vector as Vector
7053import qualified ErrorMessages
7154import qualified SerializationFormatSpec
7255
@@ -74,7 +57,7 @@ import qualified SerializationFormatSpec
7457import Data.Aeson.Parser.UnescapeFFI ()
7558import Data.Aeson.Parser.UnescapePure ()
7659
77−tests :: TestTree
60+tests :: Test
7861tests = testGroup "unit" [
7962 testGroup "SerializationFormatSpec" SerializationFormatSpec.tests
8063 , testGroup "ErrorMessages" ErrorMessages.tests
@@ -101,8 +84,8 @@ tests = testGroup "unit" [
10184 testCase "example 1" formatErrorExample
10285 ]
10386 , testGroup ".:, .:?, .:!" $ fmap (testCase "-") dotColonMark
87+ , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
10488 , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
105− , testGroup "Object construction" $ fmap (testCase "-") objectConstruction
10689 , testGroup "Issue #351" $ fmap (testCase "-") issue351
10790 , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors
10891 , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions
@@ -110,22 +93,6 @@ tests = testGroup "unit" [
11093 , testCase "Unescape string (PR #477)" unescapeString
11194 , testCase "Show Options" showOptions
11295 , testGroup "SingleMaybeField" singleMaybeField
113− , testCase "withEmbeddedJSON" withEmbeddedJSONTest
114− , testCase "SingleFieldCon" singleFieldCon
115− , testGroup "UnknownFields" unknownFields
116− , testGroup "Ordering of object keys" keyOrdering
117− , testCase "Ratio with denominator 0" ratioDenominator0
118− , testCase "Rational parses number" rationalNumber
119− , testCase "Big rational" bigRationalDecoding
120− , testCase "Small rational" smallRationalDecoding
121− , testCase "Big scientific exponent" bigScientificExponent
122− , testCase "Big integer decoding" bigIntegerDecoding
123− , testCase "Big natural decading" bigNaturalDecoding
124− , testCase "Big integer key decoding" bigIntegerKeyDecoding
125− , testGroup "QQ.Simple"
126− [ testCase "example" $
127− assertEqual "" (object ["foo" .= True]) [aesonQQ| {"foo": true } |]
128− ]
12996 ]
13097
13198roundTripCamel :: String -> Assertion
@@ -218,18 +185,10 @@ utcTimeGood = do
218185 assertEqual "utctime" (parseWithRead "%F %H:%MZ" ts11) t11
219186 assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-01-01T14:30:00Z") t12
220187
221− -- leap seconds are included correctly
222− let ts13 = "2015-08-23T23:59:60.128+00" :: LT.Text
223− let (Just (t13 :: UTCTime)) = parseWithAeson ts13
224− assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.128Z") t13
225− let ts14 = "2015-08-23T23:59:60.999999999999+00" :: LT.Text
226− let (Just (t14 :: UTCTime)) = parseWithAeson ts14
227− assertEqual "utctime" (parseWithRead "%FT%T%QZ" "2015-08-23T23:59:60.999999999999Z") t14
228−
229188 where
230189 parseWithRead :: String -> LT.Text -> UTCTime
231190 parseWithRead f s =
232− fromMaybe (error "parseTime input malformed") . parseTimeM True defaultTimeLocale f . LT.unpack $ s
191+ fromMaybe (error "parseTime input malformed") . parseTime defaultTimeLocale f . LT.unpack $ s
233192 parseWithAeson :: LT.Text -> Maybe UTCTime
234193 parseWithAeson s = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""]
235194
@@ -245,7 +204,6 @@ utcTimeBad = do
245204 verifyFailParse "2015-01-01T12:30:00.00+00:00Z" -- no Zulu if offset given
246205 verifyFailParse "2015-01-03 12:13:00.Z" -- decimal at the end but no digits
247206 verifyFailParse "2015-01-03 12:13.000Z" -- decimal at the end, but no seconds
248− verifyFailParse "2015-01-03 23:59:61Z" -- exceeds allowed seconds per day
249207 where
250208 verifyFailParse (s :: LT.Text) =
251209 let (dec :: Maybe UTCTime) = decode . LT.encodeUtf8 $ LT.concat ["\"", s, "\""] in
@@ -258,18 +216,6 @@ formatErrorExample =
258216 lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
259217 in assertEqual "formatError example" lhs rhs
260218
261−formatPathExample :: Assertion
262−formatPathExample =
263− let rhs = formatPath [Key "x", Index 0]
264− lhs = "$.x[0]"
265− in assertEqual "formatPath example" lhs rhs
266−
267−formatRelativePathExample :: Assertion
268−formatRelativePathExample =
269− let rhs = formatPath [Key "x", Index 0]
270− lhs = ".x[0]"
271− in assertEqual "formatRelativePath example" lhs rhs
272−
273219------------------------------------------------------------------------------
274220-- Comparison (.:?) and (.:!)
275221------------------------------------------------------------------------------
@@ -300,6 +246,27 @@ dotColonMark = [
300246 ex2 = "{\"value\": 42 }"
301247 ex3 = "{\"value\": null }"
302248
249+------------------------------------------------------------------------------
250+-- These tests check that JSONPath is tracked correctly
251+-----------------------------------------------------------------------------
252+
253+jsonPath :: [Assertion]
254+jsonPath = [
255+ -- issue #356
256+ assertEqual "Either"
257+ (Left "Error in $[1].Left[1]: expected Bool, encountered Number")
258+ (eitherDecode "[1,{\"Left\":[2,3]}]"
259+ :: Either String (Int, Either (Int, Bool) ()))
260+ -- issue #358
261+ , assertEqual "Seq a"
262+ (Left "Error in $[2]: expected Int, encountered Boolean")
263+ (eitherDecode "[0,1,true]" :: Either String (Seq Int))
264+ , assertEqual "Wibble"
265+ (Left "Error in $.wibbleInt: expected Int, encountered Boolean")
266+ (eitherDecode "{\"wibbleString\":\"\",\"wibbleInt\":true}"
267+ :: Either String Wibble)
268+ ]
269+
303270------------------------------------------------------------------------------
304271-- Check that the hashes of two equal Value are the same
305272------------------------------------------------------------------------------
@@ -312,18 +279,6 @@ hashableLaws = [
312279 a = object ["223" .= False, "807882556" .= True]
313280 b = object ["807882556" .= True, "223" .= False]
314281
315−------------------------------------------------------------------------------
316−-- Check that an alternative way to construct objects works
317−------------------------------------------------------------------------------
318−
319−objectConstruction :: [Assertion]
320−objectConstruction = [
321− assertEqual "Equal objects constructed differently" recommended notRecommended
322− ]
323− where
324− recommended = object ["foo" .= True, "bar" .= (-1 :: Int)]
325− notRecommended = Object (mconcat ["foo" .= True, "bar" .= (-1 :: Int)])
326−
327282-------------------------------------------------------------------------------
328283-- ToJSONKey
329284-------------------------------------------------------------------------------
@@ -384,13 +339,13 @@ issue351 = [
384339-- Comparison between bytestring and text encoders
385340------------------------------------------------------------------------------
386341
387−ioTests :: IO [TestTree]
342+ioTests :: IO [Test]
388343ioTests = do
389344 enc <- encoderComparisonTests
390345 js <- jsonTestSuite
391346 return [enc, js]
392347
393−encoderComparisonTests :: IO TestTree
348+encoderComparisonTests :: IO Test
394349encoderComparisonTests = do
395350 encoderTests <- forM testFiles $ \file0 -> do
396351 let file = "benchmarks/json-data/" ++ file0
@@ -450,7 +405,7 @@ unescapeString = do
450405
451406-- JSONTestSuite
452407
453−jsonTestSuiteTest :: FilePath -> TestTree
408+jsonTestSuiteTest :: FilePath -> Test
454409jsonTestSuiteTest path = testCase fileName $ do
455410 payload <- L.readFile path
456411 let result = eitherDecode payload :: Either String Value
@@ -465,7 +420,7 @@ jsonTestSuiteTest path = testCase fileName $ do
465420-- Build a collection of tests based on the current contents of the
466421-- JSONTestSuite test directories.
467422
468−jsonTestSuite :: IO TestTree
423+jsonTestSuite :: IO Test
469424jsonTestSuite = do
470425 let suitePath = "tests/JSONTestSuite"
471426 let suites = ["test_parsing", "test_transform"]
@@ -540,14 +495,13 @@ showOptions =
540495 ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}"
541496 ++ ", unwrapUnaryRecords = False"
542497 ++ ", tagSingleConstructors = False"
543− ++ ", rejectUnknownFields = False"
544498 ++ "}")
545499 (show defaultOptions)
546500
547501newtype SingleMaybeField = SingleMaybeField { smf :: Maybe Int }
548502 deriving (Eq, Show, Generic)
549503
550−singleMaybeField :: [TestTree]
504+singleMaybeField :: [Test]
551505singleMaybeField = do
552506 (gName, gToJSON, gToEncoding, gFromJSON) <-
553507 [ ("generic", genericToJSON opts, genericToEncoding opts, parse (genericParseJSON opts))
@@ -561,200 +515,9 @@ singleMaybeField = do
561515 v = SingleMaybeField Nothing
562516 opts = defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True}
563517
564−
565−newtype EmbeddedJSONTest = EmbeddedJSONTest Int
566− deriving (Eq, Show)
567−
568−instance FromJSON EmbeddedJSONTest where
569− parseJSON =
570− withObject "Object" $ \o ->
571− EmbeddedJSONTest <$> (o .: "prop" >>= withEmbeddedJSON "Quoted Int" parseJSON)
572−
573−withEmbeddedJSONTest :: Assertion
574−withEmbeddedJSONTest =
575− assertEqual "Unquote embedded JSON" (Right $ EmbeddedJSONTest 1) (eitherDecode "{\"prop\":\"1\"}")
576−
577−-- Regression test for https://github.com/bos/aeson/issues/627
578−newtype SingleFieldCon = SingleFieldCon Int deriving (Eq, Show, Generic)
579−
580−instance FromJSON SingleFieldCon where
581− parseJSON = genericParseJSON defaultOptions{unwrapUnaryRecords=True}
582− -- This option should have no effect on this type
583−
584−singleFieldCon :: Assertion
585−singleFieldCon =
586− assertEqual "fromJSON" (Right (SingleFieldCon 0)) (eitherDecode "0")
587−
588−newtype UnknownFields = UnknownFields { knownField :: Int }
589− deriving (Eq, Show, Generic)
590−newtype UnknownFieldsTag = UnknownFieldsTag { tag :: Int }
591− deriving (Eq, Show, Generic)
592−newtype UnknownFieldsUnaryTagged = UnknownFieldsUnaryTagged { knownFieldUnaryTagged :: Int }
593− deriving (Eq, Show, Generic)
594−data UnknownFieldsSum
595− = UnknownFields1 { knownField1 :: Int }
596− | UnknownFields2 { knownField2 :: Int }
597− deriving (Eq, Show, Generic)
598−
599−unknownFields :: [TestTree]
600−unknownFields = concat
601− [ testsUnary
602− "unary-unknown"
603− (object [("knownField", Number 1), ("unknownField", Number 1)])
604− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFields)
605− , testsUnary
606− "unary-unknown-tag"
607− (object [("knownField", Number 1), ("tag", String "UnknownFields")])
608− (Error "nknown fields: [\"tag\"]" :: Result UnknownFields)
609− , testsUnaryTag
610− "unary-explicit-tag"
611− (object [("tag", Number 1)])
612− (Success $ UnknownFieldsTag 1)
613− , testsSum
614− "sum-tag"
615− (object [("knownField1", Number 1), ("tag", String "UnknownFields1")])
616− (Success $ UnknownFields1 1)
617− , testsSum
618− "sum-unknown-in-branch"
619− (object [("knownField1", Number 1), ("knownField2", Number 1), ("tag", String "UnknownFields1")])
620− (Error "nknown fields: [\"knownField2\"]" :: Result UnknownFieldsSum)
621− , testsSum
622− "sum-unknown"
623− (object [("knownField1", Number 1), ("unknownField", Number 1), ("tag", String "UnknownFields1")])
624− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsSum)
625− , testsTagged
626− "unary-tagged"
627− (object [("knownFieldUnaryTagged", Number 1), ("tag", String "UnknownFieldsUnaryTagged")])
628− (Success $ UnknownFieldsUnaryTagged 1)
629− , -- Just a case to verify that the tag isn't optional, this is likely already tested by other unit tests
630− testsTagged
631− "unary-tagged-notag"
632− (object [("knownFieldUnaryTagged", Number 1)])
633− (Error "key \"tag\" not found" :: Result UnknownFieldsUnaryTagged)
634− , testsTagged
635− "unary-tagged-unknown"
636− (object [ ("knownFieldUnaryTagged", Number 1), ("unknownField", Number 1)
637− , ("tag", String "UnknownFieldsUnaryTagged")])
638− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsUnaryTagged)
639− ]
640− where
641− opts = defaultOptions{rejectUnknownFields=True}
642− taggedOpts = opts{tagSingleConstructors=True}
643− assertApprox :: (Show a, Eq a) => Result a -> Result a -> IO ()
644− assertApprox (Error expected) (Error actual) | expected `isSuffixOf` actual = return ()
645− assertApprox expected actual = assertEqual "fromJSON" expected actual
646− testsBase :: (Show a, Eq a) => (Value -> Result a) -> (Value -> Result a)
647− -> String -> Value -> Result a -> [TestTree]
648− testsBase th g name value expected =
649− [ testCase (name ++ "-th") $ assertApprox expected (th value)
650− , testCase (name ++ "-generic") $ assertApprox expected (g value)
651− ]
652− testsUnary :: String -> Value -> Result UnknownFields -> [TestTree]
653− testsUnary = testsBase fromJSON (parse (genericParseJSON opts))
654− testsUnaryTag :: String -> Value -> Result UnknownFieldsTag -> [TestTree]
655− testsUnaryTag = testsBase fromJSON (parse (genericParseJSON opts))
656− testsSum :: String -> Value -> Result UnknownFieldsSum -> [TestTree]
657− testsSum = testsBase fromJSON (parse (genericParseJSON opts))
658− testsTagged :: String -> Value -> Result UnknownFieldsUnaryTagged -> [TestTree]
659− testsTagged = testsBase fromJSON (parse (genericParseJSON taggedOpts))
660−
661−testParser :: (Eq a, Show a)
662− => String -> Parser a -> S.ByteString -> Either String a -> TestTree
663−testParser name json_ s expected =
664− testCase name (parseOnly json_ s @?= expected)
665−
666−keyOrdering :: [TestTree]
667−keyOrdering =
668− [ testParser "json" json
669− "{\"k\":true,\"k\":false}" $
670− Right (Object (HashMap.fromList [("k", Bool True)]))
671− , testParser "jsonLast" jsonLast
672− "{\"k\":true,\"k\":false}" $
673− Right (Object (HashMap.fromList [("k", Bool False)]))
674− , testParser "jsonAccum" jsonAccum
675− "{\"k\":true,\"k\":false}" $
676− Right (Object (HashMap.fromList [("k", Array (Vector.fromList [Bool True, Bool False]))]))
677− , testParser "jsonNoDup" jsonNoDup
678− "{\"k\":true,\"k\":false}" $
679− Left "Failed reading: found duplicate key: \"k\""
680−
681− , testParser "json'" json'
682− "{\"k\":true,\"k\":false}" $
683− Right (Object (HashMap.fromList [("k", Bool True)]))
684− , testParser "jsonLast'" jsonLast'
685− "{\"k\":true,\"k\":false}" $
686− Right (Object (HashMap.fromList [("k", Bool False)]))
687− , testParser "jsonAccum'" jsonAccum'
688− "{\"k\":true,\"k\":false}" $
689− Right (Object (HashMap.fromList [("k", Array (Vector.fromList [Bool True, Bool False]))]))
690− , testParser "jsonNoDup'" jsonNoDup'
691− "{\"k\":true,\"k\":false}" $
692− Left "Failed reading: found duplicate key: \"k\""
693− ]
694−
695−ratioDenominator0 :: Assertion
696−ratioDenominator0 =
697− assertEqual "Ratio with denominator 0"
698− (Left "Error in $: Ratio denominator was 0")
699− (eitherDecode "{ \"numerator\": 1, \"denominator\": 0 }" :: Either String Rational)
700−
701−rationalNumber :: Assertion
702−rationalNumber =
703− assertEqual "Ratio with denominator 0"
704− (Right 1.37)
705− (eitherDecode "1.37" :: Either String Rational)
706−
707−bigRationalDecoding :: Assertion
708−bigRationalDecoding =
709− assertEqual "Decoding an Integer with a large exponent should fail"
710− (Left "Error in $: parsing Ratio failed, found a number with exponent 2000, but it must not be greater than 1024 or less than -1024")
711− ((eitherDecode :: L.ByteString -> Either String Rational) "1e2000")
712−
713−smallRationalDecoding :: Assertion
714−smallRationalDecoding =
715− assertEqual "Decoding an Integer with a large exponent should fail"
716− (Left "Error in $: parsing Ratio failed, found a number with exponent -2000, but it must not be greater than 1024 or less than -1024")
717− ((eitherDecode :: L.ByteString -> Either String Rational) "1e-2000")
718−
719−
720−bigScientificExponent :: Assertion
721−bigScientificExponent =
722− assertEqual "Encoding an integral scientific with a large exponent should normalize it"
723− "1.0e2000"
724− (encode (scientific 1 2000 :: Scientific))
725−
726−bigIntegerDecoding :: Assertion
727−bigIntegerDecoding =
728− assertEqual "Decoding an Integer with a large exponent should fail"
729− (Left "Error in $: parsing Integer failed, found a number with exponent 2000, but it must not be greater than 1024")
730− ((eitherDecode :: L.ByteString -> Either String Integer) "1e2000")
731−
732−bigNaturalDecoding :: Assertion
733−bigNaturalDecoding =
734− assertEqual "Decoding a Natural with a large exponent should fail"
735− (Left "Error in $: parsing Natural failed, found a number with exponent 2000, but it must not be greater than 1024")
736− ((eitherDecode :: L.ByteString -> Either String Natural) "1e2000")
737−
738−bigIntegerKeyDecoding :: Assertion
739−bigIntegerKeyDecoding =
740− assertEqual "Decoding an Integer key with a large exponent should fail"
741− (Left "Error in $['1e2000']: parsing Integer failed, found a number with exponent 2000, but it must not be greater than 1024")
742− ((eitherDecode :: L.ByteString -> Either String (HashMap Integer Value)) "{ \"1e2000\": null }")
743−
744−bigNaturalKeyDecoding :: Assertion
745−bigNaturalKeyDecoding =
746− assertEqual "Decoding an Integer key with a large exponent should fail"
747− (Left "Error in $['1e2000']: found a number with exponent 2000, but it must not be greater than 1024")
748− ((eitherDecode :: L.ByteString -> Either String (HashMap Natural Value)) "{ \"1e2000\": null }")
749−
750518deriveJSON defaultOptions{omitNothingFields=True} ''MyRecord
751519
752520deriveToJSON defaultOptions ''Foo
753521deriveToJSON1 defaultOptions ''Foo
754522
755523deriveJSON defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True} ''SingleMaybeField
756−
757−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
758−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
759−deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
760−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
tests/UnitTests/NullaryConstructors.hs+3−8
@@ -1,5 +1,4 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE OverloadedStrings #-}
32
43{-# OPTIONS_GHC -fno-warn-deprecations #-}
54
@@ -8,16 +7,16 @@ module UnitTests.NullaryConstructors
87 nullaryConstructors
98 ) where
109
10+import Prelude ()
1111import Prelude.Compat
1212
1313import Data.Aeson (decode, eitherDecode, fromEncoding, Value)
1414import Data.Aeson.Internal (IResult (..), iparse)
1515import Data.Aeson.Types (Parser)
1616import Data.ByteString.Builder (toLazyByteString)
17−import Data.Foldable (for_)
1817import Data.Maybe (fromJust)
1918import Encoders
20−import Test.Tasty.HUnit ((@=?), Assertion)
19+import Test.HUnit ((@=?), Assertion)
2120import Types
2221import qualified Data.ByteString.Lazy.Char8 as L
2322
@@ -53,10 +52,6 @@ nullaryConstructors =
5352 -- Make sure that the old `"contents" : []' is still allowed
5453 , ISuccess C1 @=? parse thNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
5554 , ISuccess C1 @=? parse gNullaryParseJSONTaggedObject (dec "{\"tag\":\"c1\",\"contents\":[]}")
56−
57− , for_ [("kC1", C1), ("kC2", C2), ("kC3", C3)] $ \(jkey, key) -> do
58− Right jkey @=? gNullaryToJSONKey key
59− ISuccess key @=? parse gNullaryFromJSONKey jkey
6055 ]
6156 where
6257 enc = eitherDecode . toLazyByteString . fromEncoding
tests/golden/generic.expecteddeleted−48
@@ -1,48 +0,0 @@
1−OneConstructor
2−Error in $: parsing Types.OneConstructor(OneConstructor) failed, expected Array, but encountered String
3−Error in $: parsing Types.OneConstructor(OneConstructor) failed, expected an empty Array, but encountered an Array of length 1
4−Nullary
5−Error in $: parsing Types.Nullary failed, expected one of the tags ["C1","C2","C3"], but found tag "X"
6−Error in $: parsing Types.Nullary failed, expected String, but encountered Array
7−SomeType (tagged)
8−Error in $.contents: parsing Int failed, expected Number, but encountered Boolean
9−Error in $: parsing Types.SomeType(Unary) failed, key "contents" not found
10−Error in $: parsing Types.SomeType(Record) failed, key "testone" not found
11−Error in $.testone: parsing Double failed, unexpected Boolean
12−Error in $.tag: parsing Types.SomeType failed, expected tag field to be one of ["nullary","unary","product","record","list"], but found tag "X"
13−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
14−Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
15−SomeType (single-field)
16−Error in $.unary: parsing Int failed, expected Number, but encountered Object
17−Error in $.unary: parsing Int failed, expected Number, but encountered Array
18−Error in $: parsing Types.SomeType failed, expected an Object with a single pair where the tag is one of ["nullary","unary","product","record","list"], but found tag "X"
19−Error in $: parsing Types.SomeType failed, expected an Object with a single pair, but found 2 pairs
20−Error in $: parsing Types.SomeType failed, expected an Object with a single pair, but found 0 pairs
21−Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
22−Error in $: not enough input. Expecting ':'
23−Error in $: not enough input. Expecting object value
24−Error in $: not enough input. Expecting ',' or '}'
25−SomeType (two-element array)
26−Error in $[1]: parsing Int failed, expected Number, but encountered Boolean
27−Error in $[1]: parsing Types.SomeType(Record) failed, expected Object, but encountered Null
28−Error in $[0]: parsing Types.SomeType failed, expected tag of the 2-element Array to be one of ["nullary","unary","product","record","list"], but found tag "X"
29−Error in $[0]: parsing Types.SomeType failed, tag element is not a String
30−Error in $: parsing Types.SomeType failed, expected a 2-element Array, but encountered an Array of length 0
31−Error in $: parsing Types.SomeType failed, expected Array, but encountered Object
32−Error in $: not enough input. Expecting ',' or ']'
33−Error in $: not enough input. Expecting json list value
34−SomeType (reject unknown fields)
35−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testZero"]
36−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
37−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testtwo","testone","testthree"]
38−Foo (reject unknown fields)
39−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["tag"]
40−Foo (reject unknown fields, tagged single)
41−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["unknownField"]
42−EitherTextInt
43−Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected tag "nonenullary", but found tag "X"
44−Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected String, but encountered Array
45−Product2 Int Bool
46−Error in $[1]: expected Bool, but encountered Null
47−Error in $: parsing Types.Product2(Product2) failed, expected an Array of length 2, but encountered an Array of length 0
48−Error in $: parsing Types.Product2(Product2) failed, expected Array, but encountered Object
tests/golden/simple.expecteddeleted−22
@@ -1,22 +0,0 @@
1−Int
2−Error in $: parsing Int failed, expected Number, but encountered String
3−Error in $: parsing Int failed, expected Number, but encountered Array
4−Error in $: parsing Int failed, expected Number, but encountered Object
5−Error in $: parsing Int failed, expected Number, but encountered Null
6−Integer
7−Error in $: parsing Integer failed, unexpected floating number 44.44
8−Natural
9−Error in $: parsing Natural failed, unexpected floating number 44.44
10−Error in $: parsing Natural failed, unexpected negative number -50
11−String
12−Error in $: expected String, but encountered Number
13−Error in $: expected String, but encountered Array
14−Error in $: expected String, but encountered Object
15−Error in $: expected String, but encountered Null
16−HashMap
17−Error in $: parsing HashMap failed, expected Object, but encountered String
18−Error in $: parsing HashMap failed, expected Object, but encountered Array
19−Either
20−Error in $[1].Left[1]: expected Bool, but encountered Number
21−Seq
22−Error in $[2]: parsing Int failed, expected Number, but encountered Boolean
travis/cabal_macros.hdeleted−448
@@ -1,448 +0,0 @@
1−/* DO NOT EDIT: This file is automatically generated by Cabal */
2−
3−/* package aeson-1.4.4.0 */
4−#ifndef VERSION_aeson
5−#define VERSION_aeson "1.4.4.0"
6−#endif /* VERSION_aeson */
7−#ifndef MIN_VERSION_aeson
8−#define MIN_VERSION_aeson(major1,major2,minor) (\
9− (major1) < 1 || \
10− (major1) == 1 && (major2) < 4 || \
11− (major1) == 1 && (major2) == 4 && (minor) <= 4)
12−#endif /* MIN_VERSION_aeson */
13−
14−/* package Diff-0.3.4 */
15−#ifndef VERSION_Diff
16−#define VERSION_Diff "0.3.4"
17−#endif /* VERSION_Diff */
18−#ifndef MIN_VERSION_Diff
19−#define MIN_VERSION_Diff(major1,major2,minor) (\
20− (major1) < 0 || \
21− (major1) == 0 && (major2) < 3 || \
22− (major1) == 0 && (major2) == 3 && (minor) <= 4)
23−#endif /* MIN_VERSION_Diff */
24−
25−/* package QuickCheck-2.13.1 */
26−#ifndef VERSION_QuickCheck
27−#define VERSION_QuickCheck "2.13.1"
28−#endif /* VERSION_QuickCheck */
29−#ifndef MIN_VERSION_QuickCheck
30−#define MIN_VERSION_QuickCheck(major1,major2,minor) (\
31− (major1) < 2 || \
32− (major1) == 2 && (major2) < 13 || \
33− (major1) == 2 && (major2) == 13 && (minor) <= 1)
34−#endif /* MIN_VERSION_QuickCheck */
35−
36−/* package aeson-1.4.4.0 */
37−#ifndef VERSION_aeson
38−#define VERSION_aeson "1.4.4.0"
39−#endif /* VERSION_aeson */
40−#ifndef MIN_VERSION_aeson
41−#define MIN_VERSION_aeson(major1,major2,minor) (\
42− (major1) < 1 || \
43− (major1) == 1 && (major2) < 4 || \
44− (major1) == 1 && (major2) == 4 && (minor) <= 4)
45−#endif /* MIN_VERSION_aeson */
46−
47−/* package attoparsec-0.13.2.2 */
48−#ifndef VERSION_attoparsec
49−#define VERSION_attoparsec "0.13.2.2"
50−#endif /* VERSION_attoparsec */
51−#ifndef MIN_VERSION_attoparsec
52−#define MIN_VERSION_attoparsec(major1,major2,minor) (\
53− (major1) < 0 || \
54− (major1) == 0 && (major2) < 13 || \
55− (major1) == 0 && (major2) == 13 && (minor) <= 2)
56−#endif /* MIN_VERSION_attoparsec */
57−
58−/* package base-4.12.0.0 */
59−#ifndef VERSION_base
60−#define VERSION_base "4.12.0.0"
61−#endif /* VERSION_base */
62−#ifndef MIN_VERSION_base
63−#define MIN_VERSION_base(major1,major2,minor) (\
64− (major1) < 4 || \
65− (major1) == 4 && (major2) < 12 || \
66− (major1) == 4 && (major2) == 12 && (minor) <= 0)
67−#endif /* MIN_VERSION_base */
68−
69−/* package base-compat-0.10.5 */
70−#ifndef VERSION_base_compat
71−#define VERSION_base_compat "0.10.5"
72−#endif /* VERSION_base_compat */
73−#ifndef MIN_VERSION_base_compat
74−#define MIN_VERSION_base_compat(major1,major2,minor) (\
75− (major1) < 0 || \
76− (major1) == 0 && (major2) < 10 || \
77− (major1) == 0 && (major2) == 10 && (minor) <= 5)
78−#endif /* MIN_VERSION_base_compat */
79−
80−/* package base-orphans-0.8.1 */
81−#ifndef VERSION_base_orphans
82−#define VERSION_base_orphans "0.8.1"
83−#endif /* VERSION_base_orphans */
84−#ifndef MIN_VERSION_base_orphans
85−#define MIN_VERSION_base_orphans(major1,major2,minor) (\
86− (major1) < 0 || \
87− (major1) == 0 && (major2) < 8 || \
88− (major1) == 0 && (major2) == 8 && (minor) <= 1)
89−#endif /* MIN_VERSION_base_orphans */
90−
91−/* package base16-bytestring-0.1.1.6 */
92−#ifndef VERSION_base16_bytestring
93−#define VERSION_base16_bytestring "0.1.1.6"
94−#endif /* VERSION_base16_bytestring */
95−#ifndef MIN_VERSION_base16_bytestring
96−#define MIN_VERSION_base16_bytestring(major1,major2,minor) (\
97− (major1) < 0 || \
98− (major1) == 0 && (major2) < 1 || \
99− (major1) == 0 && (major2) == 1 && (minor) <= 1)
100−#endif /* MIN_VERSION_base16_bytestring */
101−
102−/* package bytestring-0.10.8.2 */
103−#ifndef VERSION_bytestring
104−#define VERSION_bytestring "0.10.8.2"
105−#endif /* VERSION_bytestring */
106−#ifndef MIN_VERSION_bytestring
107−#define MIN_VERSION_bytestring(major1,major2,minor) (\
108− (major1) < 0 || \
109− (major1) == 0 && (major2) < 10 || \
110− (major1) == 0 && (major2) == 10 && (minor) <= 8)
111−#endif /* MIN_VERSION_bytestring */
112−
113−/* package containers-0.6.0.1 */
114−#ifndef VERSION_containers
115−#define VERSION_containers "0.6.0.1"
116−#endif /* VERSION_containers */
117−#ifndef MIN_VERSION_containers
118−#define MIN_VERSION_containers(major1,major2,minor) (\
119− (major1) < 0 || \
120− (major1) == 0 && (major2) < 6 || \
121− (major1) == 0 && (major2) == 6 && (minor) <= 0)
122−#endif /* MIN_VERSION_containers */
123−
124−/* package directory-1.3.3.0 */
125−#ifndef VERSION_directory
126−#define VERSION_directory "1.3.3.0"
127−#endif /* VERSION_directory */
128−#ifndef MIN_VERSION_directory
129−#define MIN_VERSION_directory(major1,major2,minor) (\
130− (major1) < 1 || \
131− (major1) == 1 && (major2) < 3 || \
132− (major1) == 1 && (major2) == 3 && (minor) <= 3)
133−#endif /* MIN_VERSION_directory */
134−
135−/* package dlist-0.8.0.6 */
136−#ifndef VERSION_dlist
137−#define VERSION_dlist "0.8.0.6"
138−#endif /* VERSION_dlist */
139−#ifndef MIN_VERSION_dlist
140−#define MIN_VERSION_dlist(major1,major2,minor) (\
141− (major1) < 0 || \
142− (major1) == 0 && (major2) < 8 || \
143− (major1) == 0 && (major2) == 8 && (minor) <= 0)
144−#endif /* MIN_VERSION_dlist */
145−
146−/* package filepath-1.4.2.1 */
147−#ifndef VERSION_filepath
148−#define VERSION_filepath "1.4.2.1"
149−#endif /* VERSION_filepath */
150−#ifndef MIN_VERSION_filepath
151−#define MIN_VERSION_filepath(major1,major2,minor) (\
152− (major1) < 1 || \
153− (major1) == 1 && (major2) < 4 || \
154− (major1) == 1 && (major2) == 4 && (minor) <= 2)
155−#endif /* MIN_VERSION_filepath */
156−
157−/* package generic-deriving-1.12.4 */
158−#ifndef VERSION_generic_deriving
159−#define VERSION_generic_deriving "1.12.4"
160−#endif /* VERSION_generic_deriving */
161−#ifndef MIN_VERSION_generic_deriving
162−#define MIN_VERSION_generic_deriving(major1,major2,minor) (\
163− (major1) < 1 || \
164− (major1) == 1 && (major2) < 12 || \
165− (major1) == 1 && (major2) == 12 && (minor) <= 4)
166−#endif /* MIN_VERSION_generic_deriving */
167−
168−/* package ghc-prim-0.5.3 */
169−#ifndef VERSION_ghc_prim
170−#define VERSION_ghc_prim "0.5.3"
171−#endif /* VERSION_ghc_prim */
172−#ifndef MIN_VERSION_ghc_prim
173−#define MIN_VERSION_ghc_prim(major1,major2,minor) (\
174− (major1) < 0 || \
175− (major1) == 0 && (major2) < 5 || \
176− (major1) == 0 && (major2) == 5 && (minor) <= 3)
177−#endif /* MIN_VERSION_ghc_prim */
178−
179−/* package hashable-1.3.0.0 */
180−#ifndef VERSION_hashable
181−#define VERSION_hashable "1.3.0.0"
182−#endif /* VERSION_hashable */
183−#ifndef MIN_VERSION_hashable
184−#define MIN_VERSION_hashable(major1,major2,minor) (\
185− (major1) < 1 || \
186− (major1) == 1 && (major2) < 3 || \
187− (major1) == 1 && (major2) == 3 && (minor) <= 0)
188−#endif /* MIN_VERSION_hashable */
189−
190−/* package hashable-time-0.2.0.2 */
191−#ifndef VERSION_hashable_time
192−#define VERSION_hashable_time "0.2.0.2"
193−#endif /* VERSION_hashable_time */
194−#ifndef MIN_VERSION_hashable_time
195−#define MIN_VERSION_hashable_time(major1,major2,minor) (\
196− (major1) < 0 || \
197− (major1) == 0 && (major2) < 2 || \
198− (major1) == 0 && (major2) == 2 && (minor) <= 0)
199−#endif /* MIN_VERSION_hashable_time */
200−
201−/* package integer-logarithms-1.0.3 */
202−#ifndef VERSION_integer_logarithms
203−#define VERSION_integer_logarithms "1.0.3"
204−#endif /* VERSION_integer_logarithms */
205−#ifndef MIN_VERSION_integer_logarithms
206−#define MIN_VERSION_integer_logarithms(major1,major2,minor) (\
207− (major1) < 1 || \
208− (major1) == 1 && (major2) < 0 || \
209− (major1) == 1 && (major2) == 0 && (minor) <= 3)
210−#endif /* MIN_VERSION_integer_logarithms */
211−
212−/* package quickcheck-instances-0.3.21 */
213−#ifndef VERSION_quickcheck_instances
214−#define VERSION_quickcheck_instances "0.3.21"
215−#endif /* VERSION_quickcheck_instances */
216−#ifndef MIN_VERSION_quickcheck_instances
217−#define MIN_VERSION_quickcheck_instances(major1,major2,minor) (\
218− (major1) < 0 || \
219− (major1) == 0 && (major2) < 3 || \
220− (major1) == 0 && (major2) == 3 && (minor) <= 21)
221−#endif /* MIN_VERSION_quickcheck_instances */
222−
223−/* package scientific-0.3.6.2 */
224−#ifndef VERSION_scientific
225−#define VERSION_scientific "0.3.6.2"
226−#endif /* VERSION_scientific */
227−#ifndef MIN_VERSION_scientific
228−#define MIN_VERSION_scientific(major1,major2,minor) (\
229− (major1) < 0 || \
230− (major1) == 0 && (major2) < 3 || \
231− (major1) == 0 && (major2) == 3 && (minor) <= 6)
232−#endif /* MIN_VERSION_scientific */
233−
234−/* package tagged-0.8.6 */
235−#ifndef VERSION_tagged
236−#define VERSION_tagged "0.8.6"
237−#endif /* VERSION_tagged */
238−#ifndef MIN_VERSION_tagged
239−#define MIN_VERSION_tagged(major1,major2,minor) (\
240− (major1) < 0 || \
241− (major1) == 0 && (major2) < 8 || \
242− (major1) == 0 && (major2) == 8 && (minor) <= 6)
243−#endif /* MIN_VERSION_tagged */
244−
245−/* package tasty-1.2.3 */
246−#ifndef VERSION_tasty
247−#define VERSION_tasty "1.2.3"
248−#endif /* VERSION_tasty */
249−#ifndef MIN_VERSION_tasty
250−#define MIN_VERSION_tasty(major1,major2,minor) (\
251− (major1) < 1 || \
252− (major1) == 1 && (major2) < 2 || \
253− (major1) == 1 && (major2) == 2 && (minor) <= 3)
254−#endif /* MIN_VERSION_tasty */
255−
256−/* package tasty-golden-2.3.2 */
257−#ifndef VERSION_tasty_golden
258−#define VERSION_tasty_golden "2.3.2"
259−#endif /* VERSION_tasty_golden */
260−#ifndef MIN_VERSION_tasty_golden
261−#define MIN_VERSION_tasty_golden(major1,major2,minor) (\
262− (major1) < 2 || \
263− (major1) == 2 && (major2) < 3 || \
264− (major1) == 2 && (major2) == 3 && (minor) <= 2)
265−#endif /* MIN_VERSION_tasty_golden */
266−
267−/* package tasty-hunit-0.10.0.2 */
268−#ifndef VERSION_tasty_hunit
269−#define VERSION_tasty_hunit "0.10.0.2"
270−#endif /* VERSION_tasty_hunit */
271−#ifndef MIN_VERSION_tasty_hunit
272−#define MIN_VERSION_tasty_hunit(major1,major2,minor) (\
273− (major1) < 0 || \
274− (major1) == 0 && (major2) < 10 || \
275− (major1) == 0 && (major2) == 10 && (minor) <= 0)
276−#endif /* MIN_VERSION_tasty_hunit */
277−
278−/* package tasty-quickcheck-0.10.1 */
279−#ifndef VERSION_tasty_quickcheck
280−#define VERSION_tasty_quickcheck "0.10.1"
281−#endif /* VERSION_tasty_quickcheck */
282−#ifndef MIN_VERSION_tasty_quickcheck
283−#define MIN_VERSION_tasty_quickcheck(major1,major2,minor) (\
284− (major1) < 0 || \
285− (major1) == 0 && (major2) < 10 || \
286− (major1) == 0 && (major2) == 10 && (minor) <= 1)
287−#endif /* MIN_VERSION_tasty_quickcheck */
288−
289−/* package template-haskell-2.14.0.0 */
290−#ifndef VERSION_template_haskell
291−#define VERSION_template_haskell "2.14.0.0"
292−#endif /* VERSION_template_haskell */
293−#ifndef MIN_VERSION_template_haskell
294−#define MIN_VERSION_template_haskell(major1,major2,minor) (\
295− (major1) < 2 || \
296− (major1) == 2 && (major2) < 14 || \
297− (major1) == 2 && (major2) == 14 && (minor) <= 0)
298−#endif /* MIN_VERSION_template_haskell */
299−
300−/* package text-1.2.3.1 */
301−#ifndef VERSION_text
302−#define VERSION_text "1.2.3.1"
303−#endif /* VERSION_text */
304−#ifndef MIN_VERSION_text
305−#define MIN_VERSION_text(major1,major2,minor) (\
306− (major1) < 1 || \
307− (major1) == 1 && (major2) < 2 || \
308− (major1) == 1 && (major2) == 2 && (minor) <= 3)
309−#endif /* MIN_VERSION_text */
310−
311−/* package time-1.8.0.2 */
312−#ifndef VERSION_time
313−#define VERSION_time "1.8.0.2"
314−#endif /* VERSION_time */
315−#ifndef MIN_VERSION_time
316−#define MIN_VERSION_time(major1,major2,minor) (\
317− (major1) < 1 || \
318− (major1) == 1 && (major2) < 8 || \
319− (major1) == 1 && (major2) == 8 && (minor) <= 0)
320−#endif /* MIN_VERSION_time */
321−
322−/* package time-compat-1.9.2.2 */
323−#ifndef VERSION_time_compat
324−#define VERSION_time_compat "1.9.2.2"
325−#endif /* VERSION_time_compat */
326−#ifndef MIN_VERSION_time_compat
327−#define MIN_VERSION_time_compat(major1,major2,minor) (\
328− (major1) < 1 || \
329− (major1) == 1 && (major2) < 9 || \
330− (major1) == 1 && (major2) == 9 && (minor) <= 2)
331−#endif /* MIN_VERSION_time_compat */
332−
333−/* package unordered-containers-0.2.10.0 */
334−#ifndef VERSION_unordered_containers
335−#define VERSION_unordered_containers "0.2.10.0"
336−#endif /* VERSION_unordered_containers */
337−#ifndef MIN_VERSION_unordered_containers
338−#define MIN_VERSION_unordered_containers(major1,major2,minor) (\
339− (major1) < 0 || \
340− (major1) == 0 && (major2) < 2 || \
341− (major1) == 0 && (major2) == 2 && (minor) <= 10)
342−#endif /* MIN_VERSION_unordered_containers */
343−
344−/* package uuid-types-1.0.3 */
345−#ifndef VERSION_uuid_types
346−#define VERSION_uuid_types "1.0.3"
347−#endif /* VERSION_uuid_types */
348−#ifndef MIN_VERSION_uuid_types
349−#define MIN_VERSION_uuid_types(major1,major2,minor) (\
350− (major1) < 1 || \
351− (major1) == 1 && (major2) < 0 || \
352− (major1) == 1 && (major2) == 0 && (minor) <= 3)
353−#endif /* MIN_VERSION_uuid_types */
354−
355−/* package vector-0.12.0.3 */
356−#ifndef VERSION_vector
357−#define VERSION_vector "0.12.0.3"
358−#endif /* VERSION_vector */
359−#ifndef MIN_VERSION_vector
360−#define MIN_VERSION_vector(major1,major2,minor) (\
361− (major1) < 0 || \
362− (major1) == 0 && (major2) < 12 || \
363− (major1) == 0 && (major2) == 12 && (minor) <= 0)
364−#endif /* MIN_VERSION_vector */
365−
366−/* tool gcc-4.2.1 */
367−#ifndef TOOL_VERSION_gcc
368−#define TOOL_VERSION_gcc "4.2.1"
369−#endif /* TOOL_VERSION_gcc */
370−#ifndef MIN_TOOL_VERSION_gcc
371−#define MIN_TOOL_VERSION_gcc(major1,major2,minor) (\
372− (major1) < 4 || \
373− (major1) == 4 && (major2) < 2 || \
374− (major1) == 4 && (major2) == 2 && (minor) <= 1)
375−#endif /* MIN_TOOL_VERSION_gcc */
376−
377−/* tool ghc-8.6.5 */
378−#ifndef TOOL_VERSION_ghc
379−#define TOOL_VERSION_ghc "8.6.5"
380−#endif /* TOOL_VERSION_ghc */
381−#ifndef MIN_TOOL_VERSION_ghc
382−#define MIN_TOOL_VERSION_ghc(major1,major2,minor) (\
383− (major1) < 8 || \
384− (major1) == 8 && (major2) < 6 || \
385− (major1) == 8 && (major2) == 6 && (minor) <= 5)
386−#endif /* MIN_TOOL_VERSION_ghc */
387−
388−/* tool ghc-pkg-8.6.5 */
389−#ifndef TOOL_VERSION_ghc_pkg
390−#define TOOL_VERSION_ghc_pkg "8.6.5"
391−#endif /* TOOL_VERSION_ghc_pkg */
392−#ifndef MIN_TOOL_VERSION_ghc_pkg
393−#define MIN_TOOL_VERSION_ghc_pkg(major1,major2,minor) (\
394− (major1) < 8 || \
395− (major1) == 8 && (major2) < 6 || \
396− (major1) == 8 && (major2) == 6 && (minor) <= 5)
397−#endif /* MIN_TOOL_VERSION_ghc_pkg */
398−
399−/* tool haddock-2.22.0 */
400−#ifndef TOOL_VERSION_haddock
401−#define TOOL_VERSION_haddock "2.22.0"
402−#endif /* TOOL_VERSION_haddock */
403−#ifndef MIN_TOOL_VERSION_haddock
404−#define MIN_TOOL_VERSION_haddock(major1,major2,minor) (\
405− (major1) < 2 || \
406− (major1) == 2 && (major2) < 22 || \
407− (major1) == 2 && (major2) == 22 && (minor) <= 0)
408−#endif /* MIN_TOOL_VERSION_haddock */
409−
410−/* tool hpc-0.67 */
411−#ifndef TOOL_VERSION_hpc
412−#define TOOL_VERSION_hpc "0.67"
413−#endif /* TOOL_VERSION_hpc */
414−#ifndef MIN_TOOL_VERSION_hpc
415−#define MIN_TOOL_VERSION_hpc(major1,major2,minor) (\
416− (major1) < 0 || \
417− (major1) == 0 && (major2) < 67 || \
418− (major1) == 0 && (major2) == 67 && (minor) <= 0)
419−#endif /* MIN_TOOL_VERSION_hpc */
420−
421−/* tool hsc2hs-0.68.5 */
422−#ifndef TOOL_VERSION_hsc2hs
423−#define TOOL_VERSION_hsc2hs "0.68.5"
424−#endif /* TOOL_VERSION_hsc2hs */
425−#ifndef MIN_TOOL_VERSION_hsc2hs
426−#define MIN_TOOL_VERSION_hsc2hs(major1,major2,minor) (\
427− (major1) < 0 || \
428− (major1) == 0 && (major2) < 68 || \
429− (major1) == 0 && (major2) == 68 && (minor) <= 5)
430−#endif /* MIN_TOOL_VERSION_hsc2hs */
431−
432−/* tool runghc-8.6.5 */
433−#ifndef TOOL_VERSION_runghc
434−#define TOOL_VERSION_runghc "8.6.5"
435−#endif /* TOOL_VERSION_runghc */
436−#ifndef MIN_TOOL_VERSION_runghc
437−#define MIN_TOOL_VERSION_runghc(major1,major2,minor) (\
438− (major1) < 8 || \
439− (major1) == 8 && (major2) < 6 || \
440− (major1) == 8 && (major2) == 6 && (minor) <= 5)
441−#endif /* MIN_TOOL_VERSION_runghc */
442−
443−#ifndef CURRENT_COMPONENT_ID
444−#define CURRENT_COMPONENT_ID "aeson-1.4.4.0-inplace-tests"
445−#endif /* CURRENT_COMPONENT_ID */
446−#ifndef CURRENT_PACKAGE_VERSION
447−#define CURRENT_PACKAGE_VERSION "1.4.4.0"
448−#endif /* CURRENT_PACKAGE_VERSION */
travis/install.sh−11
@@ -7,11 +7,6 @@ echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo
77stack --version
88
99case $BUILD in
10− hlint)
11− echo "Downloading hlint"
12− curl -sSL https://raw.github.com/ndmitchell/hlint/master/misc/run.sh > hlint.sh
13− chmod +x hlint.sh
14− ;;
1510 stack)
1611 stack --no-terminal --skip-ghc-check setup
1712 stack --no-terminal --skip-ghc-check test --only-snapshot
@@ -49,10 +44,4 @@ case $BUILD in
4944 cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
5045 fi
5146 ;;
52− cabal2)
53− cabal v2-update -v
54− sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
55−
56− cabal v2-build --only-dependencies --enable-tests --enable-benchmarks --dry
57− ;;
5847esac
travis/script.sh+2−4
@@ -19,11 +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 --enable-tests --enable-benchmarks
24− cabal v2-test --enable-tests --enable-benchmarks
25− ;;
2622 hlint)
23+ stack build --fast aeson --stack-yaml stack-lts8.yaml --system-ghc --no-terminal
24+ stack install hlint-2.0.5 --stack-yaml stack-lts8.yaml --system-ghc --no-terminal
2725 make lint
2826 ;;
2927esac