on master help sign in
.gitattributesnew file+1
since splitsnapshots
unifiedsplit
@@ -0,0 +1 @@
1+tests/JSONTestSuite/* linguist-vendored
.gitignore+10
@@ -1,11 +1,21 @@
1dist
1dist
2+dist-newstyle
3+.ghc.environment.*
2.cabal-sandbox/
4.cabal-sandbox/
3cabal.sandbox.config
5cabal.sandbox.config
4.stack-work/
6.stack-work/
7+.stack-work-bench/
8+*.yaml.lock
5
9
6*.o
10*.o
7*.hi
11*.hi
8*.nix
12*.nix
13+*.swp
14+.\#*
15+\#*
16+
17+# Emacs backup files
18+*~
9
19
10benchmarks/AesonEncode
20benchmarks/AesonEncode
11
21
.hlint.yamlnew file+11
@@ -0,0 +1,11 @@
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+43−16
@@ -12,31 +12,58 @@ cache:
12 - $HOME/.stack
12 - $HOME/.stack
13 - $HOME/.cabsnap
13 - $HOME/.cabsnap
14 - $HOME/.cabal/packages
14 - $HOME/.cabal/packages
15+ - $HOME/.cabal/store
15
16
16before_cache:
17before_cache:
17 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
18 - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
18− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
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
19
24
20matrix:
25matrix:
21 include:
26 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+
22− - env: BUILD=cabal CABALVER=1.18 GHCVER=7.4.2
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
23− compiler: ": #GHC 7.4.2"
57+ compiler: ": #GHC 8.0.2"
24− addons: {apt: {packages: [cabal-install-1.18,ghc-7.4.2], sources: [hvr-ghc]}}
58+ addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
59+
25− - env: BUILD=cabal CABALVER=1.18 GHCVER=7.6.3
60+ - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
26− compiler: ": #GHC 7.6.3"
61+ compiler: ": #GHC 7.10.3"
27− addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
62+ addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
63+
28 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
64 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
29 compiler: ": #GHC 7.8.4"
65 compiler: ": #GHC 7.8.4"
30 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
66 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
31− - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.2
32− compiler: ": #GHC 7.10.2"
33− addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
34− - env: BUILD=cabal CABALVER=1.24 GHCVER=8.0.1
35− compiler: ": #GHC 8.0.1"
36− addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}}
37− - env: BUILD=stack CABALVER=1.22 GHCVER=7.10.3 STACK_YAML=stack-bench.yaml
38− compiler: ": #GHC 7.10.3"
39− addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
40
67
41before_install:
68before_install:
42 - unset CC
69 - unset CC
CONTRIBUTING.md+52
@@ -1,3 +1,17 @@
1+# Documenting changes
2+
3+Changes that affect users of aeson should be mentioned in `changelog.md`. It should contain
4+* A description
5+* The level of change, see [PVP](https://wiki.haskell.org/Package_versioning_policy)
6+* Where applicable
7+ * How to migrate existing code
8+ * When it should be used
9+ * What it supersedes
10+
11+Add this entry under an `## Upcoming` header above existing releases.
12+
13+The exact format of entries is not important as we'll go through everything before a release is made.
14+
1# Style guide
15# Style guide
2
16
3* 4 space indentation
17* 4 space indentation
@@ -44,3 +58,41 @@ import Foo as F
44import Qux (Q(..), q)
58import Qux (Q(..), q)
45import qualified A
59import qualified A
46```
60```
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+82−12
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2-- Module: Data.Aeson
3-- Module: Data.Aeson
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -38,11 +39,16 @@ module Data.Aeson
38 , eitherDecode
39 , eitherDecode
39 , eitherDecode'
40 , eitherDecode'
40 , encode
41 , encode
42+ , encodeFile
41 -- ** Variants for strict bytestrings
43 -- ** Variants for strict bytestrings
42 , decodeStrict
44 , decodeStrict
45+ , decodeFileStrict
43 , decodeStrict'
46 , decodeStrict'
47+ , decodeFileStrict'
44 , eitherDecodeStrict
48 , eitherDecodeStrict
49+ , eitherDecodeFileStrict
45 , eitherDecodeStrict'
50 , eitherDecodeStrict'
51+ , eitherDecodeFileStrict'
46 -- * Core JSON types
52 -- * Core JSON types
47 , Value(..)
53 , Value(..)
48 , Encoding
54 , Encoding
@@ -57,11 +63,18 @@ module Data.Aeson
57 , fromJSON
63 , fromJSON
58 , ToJSON(..)
64 , ToJSON(..)
59 , KeyValue(..)
65 , KeyValue(..)
66+ , (<?>)
67+ , JSONPath
60 -- ** Keys for maps
68 -- ** Keys for maps
61 , ToJSONKey(..)
69 , ToJSONKey(..)
62 , ToJSONKeyFunction(..)
70 , ToJSONKeyFunction(..)
63 , FromJSONKey(..)
71 , FromJSONKey(..)
64 , FromJSONKeyFunction(..)
72 , FromJSONKeyFunction(..)
73+ -- *** Generic keys
74+ , GToJSONKey()
75+ , genericToJSONKey
76+ , GFromJSONKey()
77+ , genericFromJSONKey
65 -- ** Liftings to unary and binary type constructors
78 -- ** Liftings to unary and binary type constructors
66 , FromJSON1(..)
79 , FromJSON1(..)
67 , parseJSON1
80 , parseJSON1
@@ -76,8 +89,8 @@ module Data.Aeson
76 -- ** Generic JSON classes and options
89 -- ** Generic JSON classes and options
77 , GFromJSON(..)
90 , GFromJSON(..)
78 , FromArgs(..)
91 , FromArgs(..)
79− , GToJSON(..)
92+ , GToJSON
80− , GToEncoding(..)
93+ , GToEncoding
81 , ToArgs(..)
94 , ToArgs(..)
82 , Zero
95 , Zero
83 , One
96 , One
@@ -87,15 +100,34 @@ module Data.Aeson
87 , genericLiftToEncoding
100 , genericLiftToEncoding
88 , genericParseJSON
101 , genericParseJSON
89 , genericLiftParseJSON
102 , genericLiftParseJSON
103+ -- ** Generic and TH encoding configuration
104+ , Options
90 , defaultOptions
105 , 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
91
123
92 -- * Inspecting @'Value's@
124 -- * Inspecting @'Value's@
93 , withObject
125 , withObject
94 , withText
126 , withText
95 , withArray
127 , withArray
96− , withNumber
97 , withScientific
128 , withScientific
98 , withBool
129 , withBool
130+ , withEmbeddedJSON
99 -- * Constructors and accessors
131 -- * Constructors and accessors
100 , Series
132 , Series
101 , pairs
133 , pairs
@@ -108,16 +140,16 @@ module Data.Aeson
108 -- * Parsing
140 -- * Parsing
109 , json
141 , json
110 , json'
142 , json'
143+ , parseIndexedJSON
111 ) where
144 ) where
112
145
113−import Prelude ()
114import Prelude.Compat
146import Prelude.Compat
115
147
116−import Data.Aeson.Types.FromJSON (ifromJSON)
148+import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
117import Data.Aeson.Encoding (encodingToLazyByteString)
149import Data.Aeson.Encoding (encodingToLazyByteString)
118import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
150import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
119import Data.Aeson.Types
151import Data.Aeson.Types
120−import Data.Aeson.Types.Internal (JSONPath, formatError)
152+import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
121import qualified Data.ByteString as B
153import qualified Data.ByteString as B
122import qualified Data.ByteString.Lazy as L
154import qualified Data.ByteString.Lazy as L
123
155
@@ -127,6 +159,10 @@ import qualified Data.ByteString.Lazy as L
127encode :: (ToJSON a) => a -> L.ByteString
159encode :: (ToJSON a) => a -> L.ByteString
128encode = encodingToLazyByteString . toEncoding
160encode = encodingToLazyByteString . toEncoding
129
161
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+
130-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
166-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
131-- If this fails due to incomplete or invalid input, 'Nothing' is
167-- If this fails due to incomplete or invalid input, 'Nothing' is
132-- returned.
168-- returned.
@@ -153,6 +189,18 @@ decodeStrict :: (FromJSON a) => B.ByteString -> Maybe a
153decodeStrict = decodeStrictWith jsonEOF fromJSON
189decodeStrict = decodeStrictWith jsonEOF fromJSON
154{-# INLINE decodeStrict #-}
190{-# INLINE decodeStrict #-}
155
191
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+
156-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
204-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
157-- If this fails due to incomplete or invalid input, 'Nothing' is
205-- If this fails due to incomplete or invalid input, 'Nothing' is
158-- returned.
206-- returned.
@@ -179,6 +227,18 @@ decodeStrict' :: (FromJSON a) => B.ByteString -> Maybe a
179decodeStrict' = decodeStrictWith jsonEOF' fromJSON
227decodeStrict' = decodeStrictWith jsonEOF' fromJSON
180{-# INLINE decodeStrict' #-}
228{-# INLINE decodeStrict' #-}
181
229
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+
182eitherFormatError :: Either (JSONPath, String) a -> Either String a
242eitherFormatError :: Either (JSONPath, String) a -> Either String a
183eitherFormatError = either (Left . uncurry formatError) Right
243eitherFormatError = either (Left . uncurry formatError) Right
184{-# INLINE eitherFormatError #-}
244{-# INLINE eitherFormatError #-}
@@ -194,6 +254,12 @@ eitherDecodeStrict =
194 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
254 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
195{-# INLINE eitherDecodeStrict #-}
255{-# INLINE eitherDecodeStrict #-}
196
256
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+
197-- | Like 'decode'' but returns an error message when decoding fails.
263-- | Like 'decode'' but returns an error message when decoding fails.
198eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
264eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
199eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
265eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
@@ -205,6 +271,12 @@ eitherDecodeStrict' =
205 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
271 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
206{-# INLINE eitherDecodeStrict' #-}
272{-# INLINE eitherDecodeStrict' #-}
207
273
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+
208-- $use
280-- $use
209--
281--
210-- This section contains basic information on the different ways to
282-- This section contains basic information on the different ways to
@@ -274,11 +346,9 @@ eitherDecodeStrict' =
274-- > {-# LANGUAGE OverloadedStrings #-}
346-- > {-# LANGUAGE OverloadedStrings #-}
275-- >
347-- >
276-- > instance FromJSON Person where
348-- > instance FromJSON Person where
277−-- > parseJSON (Object v) = Person <$>
349+-- > parseJSON = withObject "Person" $ \v -> Person
278−-- > v .: "name" <*>
350+-- > <$> v .: "name"
279−-- > v .: "age"
351+-- > <*> v .: "age"
280−-- > -- A non-Object value is of the wrong type, so fail.
281−-- > parseJSON _ = empty
282--
352--
283-- We can now parse the JSON data like so:
353-- We can now parse the JSON data like so:
284--
354--
@@ -368,7 +438,7 @@ eitherDecodeStrict' =
368-- The 'Object' type contains JSON objects:
438-- The 'Object' type contains JSON objects:
369--
439--
370-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
440-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
371−-- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
441+-- > Just (fromList [("name",String "Dave"),("age",Number 2)])
372--
442--
373-- You can extract values from it with a parser using 'parse',
443-- You can extract values from it with a parser using 'parse',
374-- 'parseEither' or, in this example, 'parseMaybe':
444-- 'parseEither' or, in this example, 'parseMaybe':
Data/Aeson/Encode.hs+2−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2-- Module: Data.Aeson.Encode
3-- Module: Data.Aeson.Encode
3-- Copyright: (c) 2012-2016 Bryan O'Sullivan
4-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -8,13 +9,13 @@
8-- Portability: portable
9-- Portability: portable
9--
10--
10-- This module is left to supply limited backwards-compatibility.
11-- This module is left to supply limited backwards-compatibility.
12+
11module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
13module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
12 (
14 (
13 encode
15 encode
14 , encodeToTextBuilder
16 , encodeToTextBuilder
15 ) where
17 ) where
16
18
17−import Prelude ()
18
19
19import Data.ByteString.Lazy (ByteString)
20import Data.ByteString.Lazy (ByteString)
20import Data.Text.Lazy.Builder (Builder)
21import Data.Text.Lazy.Builder (Builder)
Data/Aeson/Encoding.hs+4−1
@@ -1,8 +1,10 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2--
3--
3-- Functions in this module return well-formed 'Encoding''.
4-- Functions in this module return well-formed 'Encoding''.
4-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
5-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
5-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
6-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
7+
6module Data.Aeson.Encoding
8module Data.Aeson.Encoding
7 (
9 (
8 -- * Encoding
10 -- * Encoding
@@ -14,6 +16,8 @@ module Data.Aeson.Encoding
14 , Series
16 , Series
15 , pairs
17 , pairs
16 , pair
18 , pair
19+ , pairStr
20+ , pair'
17 -- * Predicates
21 -- * Predicates
18 , nullEncoding
22 , nullEncoding
19 -- * Encoding constructors
23 -- * Encoding constructors
@@ -47,6 +51,5 @@ module Data.Aeson.Encoding
47 , value
51 , value
48 ) where
52 ) where
49
53
50−import Prelude ()
51
54
52import Data.Aeson.Encoding.Internal
55import Data.Aeson.Encoding.Internal
Data/Aeson/Encoding/Builder.hs+13−87
@@ -1,7 +1,6 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3−{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE TupleSections #-}
4−
5-- |
4-- |
6-- Module: Data.Aeson.Encoding.Builder
5-- Module: Data.Aeson.Encoding.Builder
7-- Copyright: (c) 2011 MailRank, Inc.
6-- Copyright: (c) 2011 MailRank, Inc.
@@ -37,7 +36,6 @@ module Data.Aeson.Encoding.Builder
37 , ascii5
36 , ascii5
38 ) where
37 ) where
39
38
40−import Prelude ()
41import Prelude.Compat
39import Prelude.Compat
42
40
43import Data.Aeson.Internal.Time
41import Data.Aeson.Internal.Time
@@ -46,8 +44,9 @@ import Data.ByteString.Builder as B
46import Data.ByteString.Builder.Prim as BP
44import Data.ByteString.Builder.Prim as BP
47import Data.ByteString.Builder.Scientific (scientificBuilder)
45import Data.ByteString.Builder.Scientific (scientificBuilder)
48import Data.Char (chr, ord)
46import Data.Char (chr, ord)
49−import Data.Monoid ((<>))
47+import Data.Semigroup ((<>))
50import Data.Scientific (Scientific, base10Exponent, coefficient)
48import Data.Scientific (Scientific, base10Exponent, coefficient)
49+import Data.Text.Encoding (encodeUtf8BuilderEscaped)
51import Data.Time (UTCTime(..))
50import Data.Time (UTCTime(..))
52import Data.Time.Calendar (Day(..), toGregorian)
51import Data.Time.Calendar (Day(..), toGregorian)
53import Data.Time.LocalTime
52import Data.Time.LocalTime
@@ -56,20 +55,6 @@ import qualified Data.HashMap.Strict as HMS
56import qualified Data.Text as T
55import qualified Data.Text as T
57import qualified Data.Vector as V
56import qualified Data.Vector as V
58
57
59−#if MIN_VERSION_bytestring(0,10,4)
60−import Data.Text.Encoding (encodeUtf8BuilderEscaped)
61−#else
62−import Data.Bits ((.&.))
63−import Data.Text.Internal (Text(..))
64−import Data.Text.Internal.Unsafe.Shift (shiftR)
65−import Foreign.Ptr (minusPtr, plusPtr)
66−import Foreign.Storable (poke)
67−import qualified Data.ByteString.Builder.Internal as B
68−import qualified Data.ByteString.Builder.Prim.Internal as BP
69−import qualified Data.Text.Array as A
70−import qualified Data.Text.Internal.Encoding.Utf16 as U16
71−#endif
72−
73-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
58-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
74--
59--
75-- Use this function if you are encoding over the wire, or need to
60-- Use this function if you are encoding over the wire, or need to
@@ -148,7 +133,7 @@ c2w c = fromIntegral (ord c)
148-- | Encode a JSON number.
133-- | Encode a JSON number.
149scientific :: Scientific -> Builder
134scientific :: Scientific -> Builder
150scientific s
135scientific s
151− | e < 0 = scientificBuilder s
136+ | e < 0 || e > 1024 = scientificBuilder s
152 | otherwise = B.integerDec (coefficient s * 10 ^ e)
137 | otherwise = B.integerDec (coefficient s * 10 ^ e)
153 where
138 where
154 e = base10Exponent s
139 e = base10Exponent s
@@ -193,12 +178,13 @@ day dd = encodeYear yr <>
193 !(T dh dl) = twoDigits d
178 !(T dh dl) = twoDigits d
194 encodeYear y
179 encodeYear y
195 | y >= 1000 = B.integerDec y
180 | y >= 1000 = B.integerDec y
196− | y > 0 =
181+ | y >= 0 = BP.primBounded (ascii4 (padYear y)) ()
182+ | y >= -999 = BP.primBounded (ascii5 ('-',padYear (- y))) ()
183+ | otherwise = B.integerDec y
184+ padYear y =
197− let (ab,c) = fromIntegral y `quotRem` 10
185+ let (ab,c) = fromIntegral y `quotRem` 10
198− (a,b) = ab `quotRem` 10
186+ (a,b) = ab `quotRem` 10
199− in BP.primBounded (ascii4 ('0',(digit a,(digit b,digit c)))) ()
187+ in ('0',(digit a,(digit b,digit c)))
200− | otherwise =
201− error "Data.Aeson.Encode.Builder.day: years BCE not supported"
202{-# INLINE day #-}
188{-# INLINE day #-}
203
189
204timeOfDay :: TimeOfDay -> Builder
190timeOfDay :: TimeOfDay -> Builder
@@ -215,7 +201,7 @@ timeOfDay64 (TOD h m s)
215 !(T mh ml) = twoDigits m
201 !(T mh ml) = twoDigits m
216 !(T sh sl) = twoDigits (fromIntegral real)
202 !(T sh sl) = twoDigits (fromIntegral real)
217 (real,frac) = s `quotRem` pico
203 (real,frac) = s `quotRem` pico
218− showFrac = (\x -> ('.', x)) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
204+ showFrac = ('.',) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
219 trunc12 = (`quotRem` micro) >$<
205 trunc12 = (`quotRem` micro) >$<
220 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
206 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
221 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
207 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
@@ -233,7 +219,6 @@ timeOfDay64 (TOD h m s)
233 pico = 1000000000000 -- number of picoseconds in 1 second
219 pico = 1000000000000 -- number of picoseconds in 1 second
234 micro = 1000000 -- number of microseconds in 1 second
220 micro = 1000000 -- number of microseconds in 1 second
235 milli = 1000 -- number of milliseconds in 1 second
221 milli = 1000 -- number of milliseconds in 1 second
236−{-# INLINE timeOfDay64 #-}
237
222
238timeZone :: TimeZone -> Builder
223timeZone :: TimeZone -> Builder
239timeZone (TimeZone off _ _)
224timeZone (TimeZone off _ _)
@@ -269,62 +254,3 @@ twoDigits a = T (digit hi) (digit lo)
269
254
270digit :: Int -> Char
255digit :: Int -> Char
271digit x = chr (x + 48)
256digit x = chr (x + 48)
272−
273−#if !(MIN_VERSION_bytestring(0,10,4))
274−-- | Encode text using UTF-8 encoding and escape the ASCII characters using
275−-- a 'BP.BoundedPrim'.
276−--
277−-- Use this function is to implement efficient encoders for text-based formats
278−-- like JSON or HTML.
279−{-# INLINE encodeUtf8BuilderEscaped #-}
280−-- TODO: Extend documentation with references to source code in @blaze-html@
281−-- or @aeson@ that uses this function.
282−encodeUtf8BuilderEscaped :: BP.BoundedPrim Word8 -> Text -> B.Builder
283−encodeUtf8BuilderEscaped be =
284− -- manual eta-expansion to ensure inlining works as expected
285− \txt -> B.builder (mkBuildstep txt)
286− where
287− bound = max 4 $ BP.sizeBound be
288−
289− mkBuildstep (Text arr off len) !k =
290− outerLoop off
291− where
292− iend = off + len
293−
294− outerLoop !i0 !br@(B.BufferRange op0 ope)
295− | i0 >= iend = k br
296− | outRemaining > 0 = goPartial (i0 + min outRemaining inpRemaining)
297− -- TODO: Use a loop with an integrated bound's check if outRemaining
298− -- is smaller than 8, as this will save on divisions.
299− | otherwise = return $ B.bufferFull bound op0 (outerLoop i0)
300− where
301− outRemaining = (ope `minusPtr` op0) `div` bound
302− inpRemaining = iend - i0
303−
304− goPartial !iendTmp = go i0 op0
305− where
306− go !i !op
307− | i < iendTmp = case A.unsafeIndex arr i of
308− w | w <= 0x7F -> do
309− BP.runB be (fromIntegral w) op >>= go (i + 1)
310− | w <= 0x7FF -> do
311− poke8 0 $ (w `shiftR` 6) + 0xC0
312− poke8 1 $ (w .&. 0x3f) + 0x80
313− go (i + 1) (op `plusPtr` 2)
314− | 0xD800 <= w && w <= 0xDBFF -> do
315− let c = ord $ U16.chr2 w (A.unsafeIndex arr (i+1))
316− poke8 0 $ (c `shiftR` 18) + 0xF0
317− poke8 1 $ ((c `shiftR` 12) .&. 0x3F) + 0x80
318− poke8 2 $ ((c `shiftR` 6) .&. 0x3F) + 0x80
319− poke8 3 $ (c .&. 0x3F) + 0x80
320− go (i + 2) (op `plusPtr` 4)
321− | otherwise -> do
322− poke8 0 $ (w `shiftR` 12) + 0xE0
323− poke8 1 $ ((w `shiftR` 6) .&. 0x3F) + 0x80
324− poke8 2 $ (w .&. 0x3F) + 0x80
325− go (i + 1) (op `plusPtr` 3)
326− | otherwise =
327− outerLoop i (B.BufferRange op ope)
328− where
329− poke8 j v = poke (op `plusPtr` j) (fromIntegral v :: Word8)
330−#endif
Data/Aeson/Encoding/Internal.hs+14−3
@@ -1,7 +1,9 @@
1{-# LANGUAGE DeriveDataTypeable #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE EmptyDataDecls #-}
2{-# LANGUAGE EmptyDataDecls #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RankNTypes #-}
6+
5module Data.Aeson.Encoding.Internal
7module Data.Aeson.Encoding.Internal
6 (
8 (
7 -- * Encoding
9 -- * Encoding
@@ -13,6 +15,8 @@ module Data.Aeson.Encoding.Internal
13 , Series (..)
15 , Series (..)
14 , pairs
16 , pairs
15 , pair
17 , pair
18+ , pairStr
19+ , pair'
16 -- * Predicates
20 -- * Predicates
17 , nullEncoding
21 , nullEncoding
18 -- * Encoding constructors
22 -- * Encoding constructors
@@ -53,7 +57,6 @@ module Data.Aeson.Encoding.Internal
53 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
57 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
54 ) where
58 ) where
55
59
56−import Prelude ()
57import Prelude.Compat
60import Prelude.Compat
58
61
59import Data.Aeson.Types.Internal (Value)
62import Data.Aeson.Types.Internal (Value)
@@ -79,7 +82,7 @@ newtype Encoding' tag = Encoding {
79 -- ^ Acquire the underlying bytestring builder.
82 -- ^ Acquire the underlying bytestring builder.
80 } deriving (Typeable)
83 } deriving (Typeable)
81
84
82−-- | Often used synonnym for 'Encoding''.
85+-- | Often used synonym for 'Encoding''.
83type Encoding = Encoding' Value
86type Encoding = Encoding' Value
84
87
85-- | Make Encoding from Builder.
88-- | Make Encoding from Builder.
@@ -122,7 +125,15 @@ data Series = Empty
122 deriving (Typeable)
125 deriving (Typeable)
123
126
124pair :: Text -> Encoding -> Series
127pair :: Text -> Encoding -> Series
128+pair name val = pair' (text name) val
129+{-# INLINE pair #-}
130+
131+pairStr :: String -> Encoding -> Series
132+pairStr name val = pair' (string name) val
133+{-# INLINE pairStr #-}
134+
135+pair' :: Encoding' Text -> Encoding -> Series
125−pair name val = Value $ retagEncoding $ text name >< colon >< val
136+pair' name val = Value $ retagEncoding $ retagEncoding name >< colon >< val
126
137
127instance Semigroup Series where
138instance Semigroup Series where
128 Empty <> a = a
139 Empty <> a = a
Data/Aeson/Internal.hs+1−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2-- Module: Data.Aeson.Internal
3-- Module: Data.Aeson.Internal
3-- Copyright: (c) 2015-2016 Bryan O'Sullivan
4-- Copyright: (c) 2015-2016 Bryan O'Sullivan
@@ -22,7 +23,6 @@ module Data.Aeson.Internal
22 , iparse
23 , iparse
23 ) where
24 ) where
24
25
25−import Prelude ()
26
26
27import Data.Aeson.Types.Internal
27import Data.Aeson.Types.Internal
28import Data.Aeson.Types.FromJSON (ifromJSON)
28import Data.Aeson.Types.FromJSON (ifromJSON)
Data/Aeson/Internal/Functions.hs+1−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2-- Module: Data.Aeson.Functions
3-- Module: Data.Aeson.Functions
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -14,7 +15,6 @@ module Data.Aeson.Internal.Functions
14 , mapKey
15 , mapKey
15 ) where
16 ) where
16
17
17−import Prelude ()
18import Prelude.Compat
18import Prelude.Compat
19
19
20import Data.Hashable (Hashable)
20import Data.Hashable (Hashable)
Data/Aeson/Internal/Time.hs+1−41
@@ -17,44 +17,4 @@ module Data.Aeson.Internal.Time
17 , toTimeOfDay64
17 , toTimeOfDay64
18 ) where
18 ) where
19
19
20−import Prelude ()
20+import Data.Attoparsec.Time.Internal
21−import Prelude.Compat
22−
23−import Data.Fixed (Pico)
24−import Data.Int (Int64)
25−import Data.Time
26−import Unsafe.Coerce (unsafeCoerce)
27−
28−#if MIN_VERSION_base(4,7,0)
29−
30−import Data.Fixed (Fixed(MkFixed))
31−
32−toPico :: Integer -> Pico
33−toPico = MkFixed
34−
35−fromPico :: Pico -> Integer
36−fromPico (MkFixed i) = i
37−
38−#else
39−
40−toPico :: Integer -> Pico
41−toPico = unsafeCoerce
42−
43−fromPico :: Pico -> Integer
44−fromPico = unsafeCoerce
45−
46−#endif
47−
48−-- | Like TimeOfDay, but using a fixed-width integer for seconds.
49−data TimeOfDay64 = TOD {-# UNPACK #-} !Int
50− {-# UNPACK #-} !Int
51− {-# UNPACK #-} !Int64
52−
53−diffTimeOfDay64 :: DiffTime -> TimeOfDay64
54−diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
55− where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
56− (m,s) = mp `quotRem` 60000000000000
57− pico = unsafeCoerce t :: Integer
58−
59−toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
60−toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
Data/Aeson/Parser.hs+13−4
@@ -1,5 +1,4 @@
1−{-# LANGUAGE OverloadedStrings #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2−
3-- |
2-- |
4-- Module: Data.Aeson.Parser
3-- Module: Data.Aeson.Parser
5-- Copyright: (c) 2012-2016 Bryan O'Sullivan
4-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -35,10 +34,21 @@ module Data.Aeson.Parser
35 json
34 json
36 , value
35 , value
37 , jstring
36 , jstring
37+ , scientific
38+ -- ** Handling objects with duplicate keys
39+ , jsonWith
40+ , jsonLast
41+ , jsonAccum
42+ , jsonNoDup
38 -- * Strict parsers
43 -- * Strict parsers
39 -- $strict
44 -- $strict
40 , json'
45 , json'
41 , value'
46 , value'
47+ -- ** Handling objects with duplicate keys
48+ , jsonWith'
49+ , jsonLast'
50+ , jsonAccum'
51+ , jsonNoDup'
42 -- * Decoding without FromJSON instances
52 -- * Decoding without FromJSON instances
43 , decodeWith
53 , decodeWith
44 , decodeStrictWith
54 , decodeStrictWith
@@ -46,9 +56,8 @@ module Data.Aeson.Parser
46 , eitherDecodeStrictWith
56 , eitherDecodeStrictWith
47 ) where
57 ) where
48
58
49−import Prelude ()
50
59
51−import Data.Aeson.Parser.Internal (decodeStrictWith, decodeWith, eitherDecodeStrictWith, eitherDecodeWith, json, json', jstring, value, value')
60+import Data.Aeson.Parser.Internal
52
61
53-- $lazy
62-- $lazy
54--
63--
Data/Aeson/Parser/Internal.hs+320−193
@@ -1,10 +1,14 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
4#if MIN_VERSION_ghc_prim(0,3,1)
5#if MIN_VERSION_ghc_prim(0,3,1)
5{-# LANGUAGE MagicHash #-}
6{-# LANGUAGE MagicHash #-}
6#endif
7#endif
7−
8+#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
9+-- Work around a compiler bug
10+{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
11+#endif
8-- |
12-- |
9-- Module: Data.Aeson.Parser.Internal
13-- Module: Data.Aeson.Parser.Internal
10-- Copyright: (c) 2011-2016 Bryan O'Sullivan
14-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -21,50 +25,56 @@ module Data.Aeson.Parser.Internal
21 (
25 (
22 -- * Lazy parsers
26 -- * Lazy parsers
23 json, jsonEOF
27 json, jsonEOF
28+ , jsonWith
29+ , jsonLast
30+ , jsonAccum
31+ , jsonNoDup
24 , value
32 , value
25 , jstring
33 , jstring
34+ , jstring_
35+ , scientific
26 -- * Strict parsers
36 -- * Strict parsers
27 , json', jsonEOF'
37 , json', jsonEOF'
38+ , jsonWith'
39+ , jsonLast'
40+ , jsonAccum'
41+ , jsonNoDup'
28 , value'
42 , value'
29 -- * Helpers
43 -- * Helpers
30 , decodeWith
44 , decodeWith
31 , decodeStrictWith
45 , decodeStrictWith
32 , eitherDecodeWith
46 , eitherDecodeWith
33 , eitherDecodeStrictWith
47 , eitherDecodeStrictWith
48+ -- ** Handling objects with duplicate keys
49+ , fromListAccum
50+ , parseListNoDup
34 ) where
51 ) where
35
52
36−import Prelude ()
37import Prelude.Compat
53import Prelude.Compat
38
54
39−import Control.Monad.IO.Class (liftIO)
55+import Control.Applicative ((<|>))
56+import Control.Monad (void, when)
40−import Data.Aeson.Types.Internal (IResult(..), JSONPath, Result(..), Value(..))
57+import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Value(..))
41−import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific, skipSpace, string)
58+import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
42−import Data.Bits ((.|.), shiftL)
59+import Data.Function (fix)
43−import Data.ByteString.Internal (ByteString(..))
60+import Data.Functor.Compat (($>))
44−import Data.Char (chr)
61+import Data.Scientific (Scientific)
45import Data.Text (Text)
62import Data.Text (Text)
46−import Data.Text.Encoding (decodeUtf8')
63+import Data.Vector (Vector)
47−import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4)
48−import Data.Text.Internal.Unsafe.Char (ord)
49−import Data.Vector as Vector (Vector, empty, fromList, reverse)
64+import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
50−import Data.Word (Word8)
51−import Foreign.ForeignPtr (withForeignPtr)
52−import Foreign.Marshal.Utils (copyBytes)
53−import Foreign.Ptr (Ptr, minusPtr, plusPtr)
54−import Foreign.Storable (poke)
55−import System.IO.Unsafe (unsafePerformIO)
56import qualified Data.Attoparsec.ByteString as A
65import qualified Data.Attoparsec.ByteString as A
57import qualified Data.Attoparsec.Lazy as L
66import qualified Data.Attoparsec.Lazy as L
58−import qualified Data.Attoparsec.Zepto as Z
59import qualified Data.ByteString as B
67import qualified Data.ByteString as B
60−import qualified Data.ByteString.Internal as B
61−import qualified Data.ByteString.Lazy as L
62import qualified Data.ByteString.Unsafe as B
68import qualified Data.ByteString.Unsafe as B
69+import qualified Data.ByteString.Lazy as L
63import qualified Data.HashMap.Strict as H
70import qualified Data.HashMap.Strict as H
71+import qualified Data.Scientific as Sci
72+import Data.Aeson.Parser.Unescape (unescapeText)
64
73
65#if MIN_VERSION_ghc_prim(0,3,1)
74#if MIN_VERSION_ghc_prim(0,3,1)
66−import GHC.Base (Int#, (==#), isTrue#, orI#, word2Int#)
75+import GHC.Base (Int#, (==#), isTrue#, word2Int#, orI#, andI#)
67import GHC.Word (Word8(W8#))
76import GHC.Word (Word8(W8#))
77+import qualified Data.Text.Encoding as TE
68#endif
78#endif
69
79
70#define BACKSLASH 92
80#define BACKSLASH 92
@@ -83,7 +93,7 @@ import GHC.Word (Word8(W8#))
83#define C_n 110
93#define C_n 110
84#define C_t 116
94#define C_t 116
85
95
86−-- | Parse a top-level JSON value.
96+-- | Parse any JSON value.
87--
97--
88-- The conversion of a parsed value to a Haskell value is deferred
98-- The conversion of a parsed value to a Haskell value is deferred
89-- until the Haskell value is needed. This may improve performance if
99-- until the Haskell value is needed. This may improve performance if
@@ -93,10 +103,15 @@ import GHC.Word (Word8(W8#))
93-- This function is an alias for 'value'. In aeson 0.8 and earlier, it
103-- This function is an alias for 'value'. In aeson 0.8 and earlier, it
94-- parsed only object or array types, in conformance with the
104-- parsed only object or array types, in conformance with the
95-- now-obsolete RFC 4627.
105-- 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'.
96json :: Parser Value
111json :: Parser Value
97json = value
112json = value
98
113
99−-- | Parse a top-level JSON value.
114+-- | Parse any JSON value.
100--
115--
101-- This is a strict version of 'json' which avoids building up thunks
116-- This is a strict version of 'json' which avoids building up thunks
102-- during parsing; it performs all conversions immediately. Prefer
117-- during parsing; it performs all conversions immediately. Prefer
@@ -105,46 +120,64 @@ json = value
105-- This function is an alias for 'value''. In aeson 0.8 and earlier, it
120-- This function is an alias for 'value''. In aeson 0.8 and earlier, it
106-- parsed only object or array types, in conformance with the
121-- parsed only object or array types, in conformance with the
107-- now-obsolete RFC 4627.
122-- 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''.
108json' :: Parser Value
128json' :: Parser Value
109json' = value'
129json' = value'
110
130
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+
111−object_ :: Parser Value
135+object_ :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
112−object_ = {-# SCC "object_" #-} Object <$> objectValues jstring value
136+object_ mkObject val = {-# SCC "object_" #-} Object <$> objectValues mkObject jstring val
137+{-# INLINE object_ #-}
113
138
114−object_' :: Parser Value
139+object_' :: ([(Text, Value)] -> Either String Object) -> Parser Value -> Parser Value
115−object_' = {-# SCC "object_'" #-} do
140+object_' mkObject val' = {-# SCC "object_'" #-} do
116− !vals <- objectValues jstring' value'
141+ !vals <- objectValues mkObject jstring' val'
117 return (Object vals)
142 return (Object vals)
118 where
143 where
119 jstring' = do
144 jstring' = do
120 !s <- jstring
145 !s <- jstring
121 return s
146 return s
147+{-# INLINE object_' #-}
122
148
149+objectValues :: ([(Text, Value)] -> Either String Object)
123−objectValues :: Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
150+ -> Parser Text -> Parser Value -> Parser (H.HashMap Text Value)
124−objectValues str val = do
151+objectValues mkObject str val = do
125 skipSpace
152 skipSpace
126 w <- A.peekWord8'
153 w <- A.peekWord8'
127 if w == CLOSE_CURLY
154 if w == CLOSE_CURLY
128 then A.anyWord8 >> return H.empty
155 then A.anyWord8 >> return H.empty
129− else loop H.empty
156+ else loop []
130 where
157 where
158+ -- Why use acc pattern here, you may ask? because 'H.fromList' use 'unsafeInsert'
159+ -- and it's much faster because it's doing in place update to the 'HashMap'!
131− loop m0 = do
160+ loop acc = do
132− k <- str <* skipSpace <* char ':'
161+ k <- (str A.<?> "object key") <* skipSpace <* (char ':' A.<?> "':'")
133− v <- val <* skipSpace
162+ v <- (val A.<?> "object value") <* skipSpace
134− let !m = H.insert k v m0
135− ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_CURLY
163+ ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_CURLY) A.<?> "',' or '}'"
164+ let acc' = (k, v) : acc
136 if ch == COMMA
165 if ch == COMMA
137− then skipSpace >> loop m
166+ then skipSpace >> loop acc'
138− else return m
167+ else case mkObject acc' of
168+ Left err -> fail err
169+ Right obj -> pure obj
139{-# INLINE objectValues #-}
170{-# INLINE objectValues #-}
140
171
141−array_ :: Parser Value
172+array_ :: Parser Value -> Parser Value
142−array_ = {-# SCC "array_" #-} Array <$> arrayValues value
173+array_ val = {-# SCC "array_" #-} Array <$> arrayValues val
174+{-# INLINE array_ #-}
143
175
144−array_' :: Parser Value
176+array_' :: Parser Value -> Parser Value
145−array_' = {-# SCC "array_'" #-} do
177+array_' val = {-# SCC "array_'" #-} do
146− !vals <- arrayValues value'
178+ !vals <- arrayValues val
147 return (Array vals)
179 return (Array vals)
180+{-# INLINE array_' #-}
148
181
149arrayValues :: Parser Value -> Parser (Vector Value)
182arrayValues :: Parser Value -> Parser (Vector Value)
150arrayValues val = do
183arrayValues val = do
@@ -152,60 +185,136 @@ arrayValues val = do
152 w <- A.peekWord8'
185 w <- A.peekWord8'
153 if w == CLOSE_SQUARE
186 if w == CLOSE_SQUARE
154 then A.anyWord8 >> return Vector.empty
187 then A.anyWord8 >> return Vector.empty
155− else loop []
188+ else loop [] 1
156 where
189 where
157− loop acc = do
190+ loop acc !len = do
158− v <- val <* skipSpace
191+ v <- (val A.<?> "json list value") <* skipSpace
159− ch <- A.satisfy $ \w -> w == COMMA || w == CLOSE_SQUARE
192+ ch <- A.satisfy (\w -> w == COMMA || w == CLOSE_SQUARE) A.<?> "',' or ']'"
160 if ch == COMMA
193 if ch == COMMA
161− then skipSpace >> loop (v:acc)
194+ then skipSpace >> loop (v:acc) (len+1)
162− else return (Vector.reverse (Vector.fromList (v:acc)))
195+ else return (Vector.reverse (Vector.fromListN len (v:acc)))
163{-# INLINE arrayValues #-}
196{-# INLINE arrayValues #-}
164
197
165−-- | Parse any JSON value. You should usually 'json' in preference to
198+-- | Parse any JSON value. Synonym of 'json'.
166−-- this function, as this function relaxes the object-or-array
167−-- requirement of RFC 4627.
168−--
169−-- In particular, be careful in using this function if you think your
170−-- code might interoperate with Javascript. A na&#xef;ve Javascript
171−-- library that parses JSON data using @eval@ is vulnerable to attack
172−-- unless the encoded data represents an object or an array. JSON
173−-- implementations in other languages conform to that same restriction
174−-- to preserve interoperability and security.
175value :: Parser Value
199value :: Parser Value
176−value = do
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.
207+--
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
177 skipSpace
237 skipSpace
178 w <- A.peekWord8'
238 w <- A.peekWord8'
179 case w of
239 case w of
180 DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring_)
240 DOUBLE_QUOTE -> A.anyWord8 *> (String <$> jstring_)
181− OPEN_CURLY -> A.anyWord8 *> object_
241+ OPEN_CURLY -> A.anyWord8 *> object_ mkObject value_
182− OPEN_SQUARE -> A.anyWord8 *> array_
242+ OPEN_SQUARE -> A.anyWord8 *> array_ value_
183− C_f -> string "false" *> pure (Bool False)
243+ C_f -> string "false" $> Bool False
184− C_t -> string "true" *> pure (Bool True)
244+ C_t -> string "true" $> Bool True
185− C_n -> string "null" *> pure Null
245+ C_n -> string "null" $> Null
186 _ | w >= 48 && w <= 57 || w == 45
246 _ | w >= 48 && w <= 57 || w == 45
187 -> Number <$> scientific
247 -> Number <$> scientific
188 | otherwise -> fail "not a valid json value"
248 | 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))
189
254
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+
190−-- | Strict version of 'value'. See also 'json''.
281+-- | Strict version of 'value'. Synonym of 'json''.
191value' :: Parser Value
282value' :: Parser Value
192−value' = do
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
193 skipSpace
288 skipSpace
194 w <- A.peekWord8'
289 w <- A.peekWord8'
195 case w of
290 case w of
196 DOUBLE_QUOTE -> do
291 DOUBLE_QUOTE -> do
197 !s <- A.anyWord8 *> jstring_
292 !s <- A.anyWord8 *> jstring_
198 return (String s)
293 return (String s)
199− OPEN_CURLY -> A.anyWord8 *> object_'
294+ OPEN_CURLY -> A.anyWord8 *> object_' mkObject value_
200− OPEN_SQUARE -> A.anyWord8 *> array_'
295+ OPEN_SQUARE -> A.anyWord8 *> array_' value_
201− C_f -> string "false" *> pure (Bool False)
296+ C_f -> string "false" $> Bool False
202− C_t -> string "true" *> pure (Bool True)
297+ C_t -> string "true" $> Bool True
203− C_n -> string "null" *> pure Null
298+ C_n -> string "null" $> Null
204 _ | w >= 48 && w <= 57 || w == 45
299 _ | w >= 48 && w <= 57 || w == 45
205 -> do
300 -> do
206 !n <- scientific
301 !n <- scientific
207 return (Number n)
302 return (Number n)
208 | otherwise -> fail "not a valid json value"
303 | 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
209
318
210-- | Parse a quoted JSON string.
319-- | Parse a quoted JSON string.
211jstring :: Parser Text
320jstring :: Parser Text
@@ -215,101 +324,48 @@ jstring = A.word8 DOUBLE_QUOTE *> jstring_
215jstring_ :: Parser Text
324jstring_ :: Parser Text
216{-# INLINE jstring_ #-}
325{-# INLINE jstring_ #-}
217jstring_ = {-# SCC "jstring_" #-} do
326jstring_ = {-# SCC "jstring_" #-} do
218− (s, fin) <- A.runScanner startState go
219− _ <- A.anyWord8
220− s1 <- if isEscaped fin
221− then case unescape s of
222− Right r -> return r
223− Left err -> fail err
224− else return s
225− case decodeUtf8' s1 of
226− Right r -> return r
227− Left err -> fail $ show err
228− where
229#if MIN_VERSION_ghc_prim(0,3,1)
327#if MIN_VERSION_ghc_prim(0,3,1)
328+ (s, S _ escaped) <- A.runScanner startState go <* A.anyWord8
329+ -- We escape only if there are
330+ -- non-ascii (over 7bit) characters or backslash present.
331+ --
332+ -- Note: if/when text will have fast ascii -> text conversion
333+ -- (e.g. uses utf8 encoding) we can have further speedup.
230− isEscaped (S _ escaped) = isTrue# escaped
334+ if isTrue# escaped
335+ then case unescapeText s of
336+ Right r -> return r
337+ Left err -> fail $ show err
338+ else return (TE.decodeUtf8 s)
339+ where
231 startState = S 0# 0#
340 startState = S 0# 0#
232− go (S a b) (W8# c)
341+ go (S skip escaped) (W8# c)
233− | isTrue# a = Just (S 0# b)
342+ | isTrue# skip = Just (S 0# escaped')
234− | isTrue# (word2Int# c ==# 34#) = Nothing -- double quote
343+ | isTrue# (w ==# 34#) = Nothing -- double quote
344+ | otherwise = Just (S skip' escaped')
345+ where
346+ w = word2Int# c
235− | otherwise = let a' = word2Int# c ==# 92# -- backslash
347+ skip' = w ==# 92# -- backslash
236− in Just (S a' (orI# a' b))
348+ escaped' = escaped
349+ `orI#` (w `andI#` 0x80# ==# 0x80#) -- c >= 0x80
350+ `orI#` skip'
351+ `orI#` (w `andI#` 0x1f# ==# w) -- c < 0x20
237
352
238data S = S Int# Int#
353data S = S Int# Int#
239#else
354#else
240− isEscaped (S _ escaped) = escaped
355+ s <- A.scan startState go <* A.anyWord8
356+ case unescapeText s of
357+ Right r -> return r
358+ Left err -> fail $ show err
359+ where
241− startState = S False False
360+ startState = False
242− go (S a b) c
361+ go a c
243− | a = Just (S False b)
362+ | a = Just False
244 | c == DOUBLE_QUOTE = Nothing
363 | c == DOUBLE_QUOTE = Nothing
245 | otherwise = let a' = c == backslash
364 | otherwise = let a' = c == backslash
246− in Just (S a' (a' || b))
365+ in Just a'
247 where backslash = BACKSLASH
366 where backslash = BACKSLASH
248−
249−data S = S !Bool !Bool
250#endif
367#endif
251
368
252−unescape :: ByteString -> Either String ByteString
253−unescape s = unsafePerformIO $ do
254− let len = B.length s
255− fp <- B.mallocByteString len
256− -- We perform no bounds checking when writing to the destination
257− -- string, as unescaping always makes it shorter than the source.
258− withForeignPtr fp $ \ptr -> do
259− ret <- Z.parseT (go ptr) s
260− case ret of
261− Left err -> return (Left err)
262− Right p -> do
263− let newlen = p `minusPtr` ptr
264− slop = len - newlen
265− Right <$> if slop >= 128 && slop >= len `quot` 4
266− then B.create newlen $ \np -> copyBytes np ptr newlen
267− else return (PS fp 0 newlen)
268− where
269− go ptr = do
270− h <- Z.takeWhile (/=BACKSLASH)
271− let rest = do
272− start <- Z.take 2
273− let !slash = B.unsafeHead start
274− !t = B.unsafeIndex start 1
275− escape = case B.elemIndex t "\"\\/ntbrfu" of
276− Just i -> i
277− _ -> 255
278− if slash /= BACKSLASH || escape == 255
279− then fail "invalid JSON escape sequence"
280− else
281− if t /= 117 -- 'u'
282− then copy h ptr >>= word8 (B.unsafeIndex mapping escape) >>= go
283− else do
284− a <- hexQuad
285− if a < 0xd800 || a > 0xdfff
286− then copy h ptr >>= charUtf8 (chr a) >>= go
287− else do
288− b <- Z.string "\\u" *> hexQuad
289− if a <= 0xdbff && b >= 0xdc00 && b <= 0xdfff
290− then let !c = ((a - 0xd800) `shiftL` 10) +
291− (b - 0xdc00) + 0x10000
292− in copy h ptr >>= charUtf8 (chr c) >>= go
293− else fail "invalid UTF-16 surrogates"
294− done <- Z.atEnd
295− if done
296− then copy h ptr
297− else rest
298− mapping = "\"\\/\n\t\b\r\f"
299−
300−hexQuad :: Z.ZeptoT IO Int
301−hexQuad = do
302− s <- Z.take 4
303− let hex n | w >= C_0 && w <= C_9 = w - C_0
304− | w >= C_a && w <= C_f = w - 87
305− | w >= C_A && w <= C_F = w - 55
306− | otherwise = 255
307− where w = fromIntegral $ B.unsafeIndex s n
308− a = hex 0; b = hex 1; c = hex 2; d = hex 3
309− if (a .|. b .|. c .|. d) /= 255
310− then return $! d .|. (c `shiftL` 4) .|. (b `shiftL` 8) .|. (a `shiftL` 12)
311− else fail "invalid hex escape"
312−
313decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
369decodeWith :: Parser Value -> (Value -> Result a) -> L.ByteString -> Maybe a
314decodeWith p to s =
370decodeWith p to s =
315 case L.parse p s of
371 case L.parse p s of
@@ -334,7 +390,12 @@ eitherDecodeWith p to s =
334 L.Done _ v -> case to v of
390 L.Done _ v -> case to v of
335 ISuccess a -> Right a
391 ISuccess a -> Right a
336 IError path msg -> Left (path, msg)
392 IError path msg -> Left (path, msg)
337− L.Fail _ _ msg -> Left ([], 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
338{-# INLINE eitherDecodeWith #-}
399{-# INLINE eitherDecodeWith #-}
339
400
340eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
401eitherDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
@@ -374,37 +435,103 @@ jsonEOF = json <* skipSpace <* endOfInput
374jsonEOF' :: Parser Value
435jsonEOF' :: Parser Value
375jsonEOF' = json' <* skipSpace <* endOfInput
436jsonEOF' = json' <* skipSpace <* endOfInput
376
437
377−word8 :: Word8 -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
438+-- | The only valid whitespace in a JSON document is space, newline,
378−word8 w ptr = do
439+-- carriage return, and tab.
379− liftIO $ poke ptr w
440+skipSpace :: Parser ()
380− return $! ptr `plusPtr` 1
441+skipSpace = A.skipWhile $ \w -> w == 0x20 || w == 0x0a || w == 0x0d || w == 0x09
381−
442+{-# INLINE skipSpace #-}
382−copy :: ByteString -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
443+
383−copy (PS fp off len) ptr =
444+------------------ Copy-pasted and adapted from attoparsec ------------------
384− liftIO . withForeignPtr fp $ \src -> do
445+
385− copyBytes ptr (src `plusPtr` off) len
446+-- A strict pair
386− return $! ptr `plusPtr` len
447+data SP = SP !Integer {-# UNPACK #-}!Int
387−
448+
388−charUtf8 :: Char -> Ptr Word8 -> Z.ZeptoT IO (Ptr Word8)
449+decimal0 :: Parser Integer
389−charUtf8 ch ptr
450+decimal0 = do
390− | ch < '\x80' = liftIO $ do
451+ let zero = 48
391− poke ptr (fromIntegral (ord ch))
452+ digits <- A.takeWhile1 isDigit_w8
392− return $! ptr `plusPtr` 1
453+ if B.length digits > 1 && B.unsafeHead digits == zero
393− | ch < '\x800' = liftIO $ do
454+ then fail "leading zero"
394− let (a,b) = ord2 ch
455+ else return (bsToInteger digits)
395− poke ptr a
456+
396− poke (ptr `plusPtr` 1) b
457+-- | Parse a JSON number.
397− return $! ptr `plusPtr` 2
458+scientific :: Parser Scientific
398− | ch < '\xffff' = liftIO $ do
459+scientific = do
399− let (a,b,c) = ord3 ch
460+ let minus = 45
400− poke ptr a
461+ plus = 43
401− poke (ptr `plusPtr` 1) b
462+ sign <- A.peekWord8'
402− poke (ptr `plusPtr` 2) c
463+ let !positive = sign == plus || sign /= minus
403− return $! ptr `plusPtr` 3
464+ when (sign == plus || sign == minus) $
404− | otherwise = liftIO $ do
465+ void A.anyWord8
405− let (a,b,c,d) = ord4 ch
466+
406− poke ptr a
467+ n <- decimal0
407− poke (ptr `plusPtr` 1) b
468+
408− poke (ptr `plusPtr` 2) c
469+ let f fracDigits = SP (B.foldl' step n fracDigits)
409− poke (ptr `plusPtr` 3) d
470+ (negate $ B.length fracDigits)
410− return $! ptr `plusPtr` 4
471+ step a w = a * 10 + fromIntegral (w - 48)
472+
473+ dotty <- A.peekWord8
474+ -- '.' -> ascii 46
475+ SP c e <- case dotty of
476+ Just 46 -> A.anyWord8 *> (f <$> A.takeWhile1 isDigit_w8)
477+ _ -> pure (SP n 0)
478+
479+ let !signedCoeff | positive = c
480+ | otherwise = -c
481+
482+ let littleE = 101
483+ bigE = 69
484+ (A.satisfy (\ex -> ex == littleE || ex == bigE) *>
485+ fmap (Sci.scientific signedCoeff . (e +)) (signed decimal)) <|>
486+ return (Sci.scientific signedCoeff e)
487+{-# 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+19−102
@@ -1,15 +1,4 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−
4−-- |
5−-- Module: Data.Aeson.Parser.Time
6−-- Copyright: (c) 2015-2016 Bryan O'Sullivan
7−-- License: BSD3
8−-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
9−-- Stability: experimental
10−-- Portability: portable
11−--
12−-- Parsers for parsing dates and times.
13
2
14module Data.Aeson.Parser.Time
3module Data.Aeson.Parser.Time
15 (
4 (
@@ -22,121 +11,51 @@ module Data.Aeson.Parser.Time
22 , zonedTime
11 , zonedTime
23 ) where
12 ) where
24
13
25−import Prelude ()
26import Prelude.Compat
14import Prelude.Compat
27
15
28−import Control.Monad (void, when)
29−import Data.Aeson.Internal.Time (toPico)
30−import Data.Attoparsec.Text as A
16+import Data.Attoparsec.Text (Parser)
31−import Data.Bits ((.&.))
32−import Data.Char (isDigit, ord)
33−import Data.Fixed (Pico)
34−import Data.Int (Int64)
35−import Data.Maybe (fromMaybe)
36import Data.Text (Text)
17import Data.Text (Text)
37−import Data.Time.Calendar (Day, fromGregorianValid)
18+import Data.Time.Calendar (Day)
38import Data.Time.Clock (UTCTime(..))
19import Data.Time.Clock (UTCTime(..))
39import qualified Data.Aeson.Types.Internal as Aeson
20import qualified Data.Aeson.Types.Internal as Aeson
40−import qualified Data.Text as T
21+import qualified Data.Attoparsec.Text as A
22+import qualified Data.Attoparsec.Time as T
41import qualified Data.Time.LocalTime as Local
23import qualified Data.Time.LocalTime as Local
42
24
43-- | Run an attoparsec parser as an aeson parser.
25-- | Run an attoparsec parser as an aeson parser.
44run :: Parser a -> Text -> Aeson.Parser a
26run :: Parser a -> Text -> Aeson.Parser a
45−run p t = case A.parseOnly (p <* endOfInput) t of
27+run p t = case A.parseOnly (p <* A.endOfInput) t of
46 Left err -> fail $ "could not parse date: " ++ err
28 Left err -> fail $ "could not parse date: " ++ err
47 Right r -> return r
29 Right r -> return r
48
30
49−-- | Parse a date of the form @YYYY-MM-DD@.
31+-- | Parse a date of the form @[+,-]YYYY-MM-DD@.
50day :: Parser Day
32day :: Parser Day
51−day = do
33+day = T.day
52− y <- decimal <* char '-'
34+{-# INLINE day #-}
53− m <- twoDigits <* char '-'
54− d <- twoDigits
55− maybe (fail "invalid date") return (fromGregorianValid y m d)
56−
57−-- | Parse a two-digit integer (e.g. day of month, hour).
58−twoDigits :: Parser Int
59−twoDigits = do
60− a <- digit
61− b <- digit
62− let c2d c = ord c .&. 15
63− return $! c2d a * 10 + c2d b
64
35
65-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
36-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
66timeOfDay :: Parser Local.TimeOfDay
37timeOfDay :: Parser Local.TimeOfDay
67−timeOfDay = do
38+timeOfDay = T.timeOfDay
68− h <- twoDigits
39+{-# INLINE timeOfDay #-}
69− m <- char ':' *> twoDigits
70− s <- option 0 (char ':' *> seconds)
71− if h < 24 && m < 60 && s < 61
72− then return (Local.TimeOfDay h m s)
73− else fail "invalid time"
74−
75−data T = T {-# UNPACK #-} !Int {-# UNPACK #-} !Int64
76−
77−-- | Parse a count of seconds, with the integer part being two digits
78−-- long.
79−seconds :: Parser Pico
80−seconds = do
81− real <- twoDigits
82− mc <- peekChar
83− case mc of
84− Just '.' -> do
85− t <- anyChar *> takeWhile1 isDigit
86− return $! parsePicos real t
87− _ -> return $! fromIntegral real
88− where
89− parsePicos a0 t = toPico (fromIntegral (t' * 10^n))
90− where T n t' = T.foldl' step (T 12 (fromIntegral a0)) t
91− step ma@(T m a) c
92− | m <= 0 = ma
93− | otherwise = T (m-1) (10 * a + fromIntegral (ord c) .&. 15)
94
40
95-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
41-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
96-- zero. (This makes some speedups possible.)
42-- zero. (This makes some speedups possible.)
97timeZone :: Parser (Maybe Local.TimeZone)
43timeZone :: Parser (Maybe Local.TimeZone)
98−timeZone = do
44+timeZone = T.timeZone
99− let maybeSkip c = do ch <- peekChar'; when (ch == c) (void anyChar)
45+{-# INLINE timeZone #-}
100− maybeSkip ' '
101− ch <- satisfy $ \c -> c == 'Z' || c == '+' || c == '-'
102− if ch == 'Z'
103− then return Nothing
104− else do
105− h <- twoDigits
106− mm <- peekChar
107− m <- case mm of
108− Just ':' -> anyChar *> twoDigits
109− Just d | isDigit d -> twoDigits
110− _ -> return 0
111− let off | ch == '-' = negate off0
112− | otherwise = off0
113− off0 = h * 60 + m
114− case undefined of
115− _ | off == 0 ->
116− return Nothing
117− | off < -720 || off > 840 || m > 59 ->
118− fail "invalid time zone offset"
119− | otherwise ->
120− let !tz = Local.minutesToTimeZone off
121− in return (Just tz)
122
46
123-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM[:SS[.SSS]]@.
47-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM[:SS[.SSS]]@.
124-- The space may be replaced with a @T@. The number of seconds is optional
48-- The space may be replaced with a @T@. The number of seconds is optional
125-- and may be followed by a fractional component.
49-- and may be followed by a fractional component.
126localTime :: Parser Local.LocalTime
50localTime :: Parser Local.LocalTime
127−localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay
51+localTime = T.localTime
128− where daySep = satisfy (\c -> c == 'T' || c == ' ')
52+{-# INLINE localTime #-}
129
53
130-- | Behaves as 'zonedTime', but converts any time zone offset into a
54-- | Behaves as 'zonedTime', but converts any time zone offset into a
131-- UTC time.
55-- UTC time.
132utcTime :: Parser UTCTime
56utcTime :: Parser UTCTime
133−utcTime = do
57+utcTime = T.utcTime
134− lt@(Local.LocalTime d t) <- localTime
58+{-# INLINE utcTime #-}
135− mtz <- timeZone
136− case mtz of
137− Nothing -> let !tt = Local.timeOfDayToTime t
138− in return (UTCTime d tt)
139− Just tz -> return $! Local.localTimeToUTC tz lt
140
59
141-- | Parse a date with time zone info. Acceptable formats:
60-- | Parse a date with time zone info. Acceptable formats:
142--
61--
@@ -150,7 +69,5 @@ utcTime = do
150-- two digits are hours, the @:@ is optional and the second two digits
69-- two digits are hours, the @:@ is optional and the second two digits
151-- (also optional) are minutes.
70-- (also optional) are minutes.
152zonedTime :: Parser Local.ZonedTime
71zonedTime :: Parser Local.ZonedTime
153−zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)
72+zonedTime = T.zonedTime
154−
73+{-# INLINE zonedTime #-}
155−utc :: Local.TimeZone
156−utc = Local.TimeZone 0 False ""
Data/Aeson/Parser/Unescape.hsnew file+11
@@ -0,0 +1,11 @@
1+{-# LANGUAGE CPP #-}
2+module Data.Aeson.Parser.Unescape
3+ (
4+ unescapeText
5+ ) where
6+
7+#ifdef CFFI
8+import Data.Aeson.Parser.UnescapeFFI (unescapeText)
9+#else
10+import Data.Aeson.Parser.UnescapePure (unescapeText)
11+#endif
Data/Aeson/QQ/Simple.hsnew file+25
@@ -0,0 +1,25 @@
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+450−897
@@ -13,6 +13,7 @@
13{-# LANGUAGE TemplateHaskell #-}
13{-# LANGUAGE TemplateHaskell #-}
14#endif
14#endif
15
15
16+#include "incoherent-compat.h"
16#include "overlapping-compat.h"
17#include "overlapping-compat.h"
17
18
18{-|
19{-|
@@ -84,7 +85,6 @@ $('deriveJSON' 'defaultOptions' ''(,,,))
84@
85@
85
86
86-}
87-}
87−
88module Data.Aeson.TH
88module Data.Aeson.TH
89 (
89 (
90 -- * Encoding configuration
90 -- * Encoding configuration
@@ -116,46 +116,42 @@ module Data.Aeson.TH
116 , mkLiftParseJSON2
116 , mkLiftParseJSON2
117 ) where
117 ) where
118
118
119−import Prelude ()
119+import Prelude.Compat
120−import Prelude.Compat hiding (exp)
121
120
122import Control.Applicative ((<|>))
121import Control.Applicative ((<|>))
123−import Data.Aeson (Object, (.=), (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
122+import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
124import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
123import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
125−import Data.Aeson.Types.Internal ((<?>), Pair, JSONPathElement(Key))
124+import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
126import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
125import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
126+import Data.Aeson.Types.ToJSON (fromPairs, pair)
127import Control.Monad (liftM2, unless, when)
127import Control.Monad (liftM2, unless, when)
128import Data.Foldable (foldr')
128import Data.Foldable (foldr')
129+#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
130+import Data.List (nub)
131+#endif
129−import Data.List (find, foldl', genericLength , intercalate , intersperse, partition, union)
132+import Data.List (foldl', genericLength, intercalate, partition, union)
130import Data.List.NonEmpty ((<|), NonEmpty((:|)))
133import Data.List.NonEmpty ((<|), NonEmpty((:|)))
131import Data.Map (Map)
134import Data.Map (Map)
132import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
135import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136+import qualified Data.Monoid as Monoid
133import Data.Set (Set)
137import Data.Set (Set)
134−#if MIN_VERSION_template_haskell(2,8,0)
135import Language.Haskell.TH hiding (Arity)
138import Language.Haskell.TH hiding (Arity)
136−#else
137−import Language.Haskell.TH
138−#endif
139−import Language.Haskell.TH.Syntax (VarStrictType)
139+import Language.Haskell.TH.Datatype
140−#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
141−import Language.Haskell.TH.Lib (starK)
142−#endif
143#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
140#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
144import Language.Haskell.TH.Syntax (mkNameG_tc)
141import Language.Haskell.TH.Syntax (mkNameG_tc)
145#endif
142#endif
146import Text.Printf (printf)
143import Text.Printf (printf)
147−import qualified Data.Aeson as A
148import qualified Data.Aeson.Encoding.Internal as E
144import qualified Data.Aeson.Encoding.Internal as E
149import qualified Data.Foldable as F (all)
145import qualified Data.Foldable as F (all)
150−import qualified Data.HashMap.Strict as H (lookup, toList)
146+import qualified Data.HashMap.Strict as H (difference, fromList, keys, lookup, toList)
151−import qualified Data.List.NonEmpty as NE (drop, length, reverse, splitAt)
147+import qualified Data.List.NonEmpty as NE (length, reverse)
152−import qualified Data.Map as M (fromList, findWithDefault, keys, lookup , singleton, size)
148+import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
149+import qualified Data.Semigroup as Semigroup (Option(..))
153import qualified Data.Set as Set (empty, insert, member)
150import qualified Data.Set as Set (empty, insert, member)
154import qualified Data.Text as T (Text, pack, unpack)
151import qualified Data.Text as T (Text, pack, unpack)
155−import qualified Data.Vector as V (unsafeIndex, null, length, create, fromList)
152+import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
156import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
153import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
157
154
158−
159--------------------------------------------------------------------------------
155--------------------------------------------------------------------------------
160-- Convenience
156-- Convenience
161--------------------------------------------------------------------------------
157--------------------------------------------------------------------------------
@@ -249,8 +245,8 @@ deriveToJSONCommon :: JSONClass
249 -> Name
245 -> Name
250 -- ^ Name of the type for which to generate an instance.
246 -- ^ Name of the type for which to generate an instance.
251 -> Q [Dec]
247 -> Q [Dec]
252−deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue jc)
248+deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue Value jc)
253− , (ToEncoding, \jc _ -> consToEncoding jc)
249+ , (ToEncoding, \jc _ -> consToValue Encoding jc)
254 ]
250 ]
255
251
256-- | Generates a lambda expression which encodes the given data type or
252-- | Generates a lambda expression which encodes the given data type or
@@ -280,7 +276,7 @@ mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
280 -> Options -- ^ Encoding options.
276 -> Options -- ^ Encoding options.
281 -> Name -- ^ Name of the encoded type.
277 -> Name -- ^ Name of the encoded type.
282 -> Q Exp
278 -> Q Exp
283−mkToJSONCommon = mkFunCommon (\jc _ -> consToValue jc)
279+mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
284
280
285-- | Generates a lambda expression which encodes the given data type or
281-- | Generates a lambda expression which encodes the given data type or
286-- data family instance constructor as a JSON string.
282-- data family instance constructor as a JSON string.
@@ -309,413 +305,272 @@ mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
309 -> Options -- ^ Encoding options.
305 -> Options -- ^ Encoding options.
310 -> Name -- ^ Name of the encoded type.
306 -> Name -- ^ Name of the encoded type.
311 -> Q Exp
307 -> Q Exp
312−mkToEncodingCommon = mkFunCommon (\jc _ -> consToEncoding jc)
308+mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
313
309
314-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
310-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
315−-- code to generate a 'Value' of a number of constructors. All constructors
311+-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
316−-- must be from the same type.
312+-- constructors must be from the same type.
313+consToValue :: ToJSONFun
314+ -- ^ The method ('toJSON' or 'toEncoding') being derived.
317−consToValue :: JSONClass
315+ -> JSONClass
318 -- ^ The ToJSON variant being derived.
316 -- ^ The ToJSON variant being derived.
319 -> Options
317 -> Options
320 -- ^ Encoding options.
318 -- ^ Encoding options.
321− -> [Con]
319+ -> [Type]
320+ -- ^ The types from the data type/data family instance declaration
321+ -> [ConstructorInfo]
322 -- ^ Constructors for which to generate JSON generating code.
322 -- ^ Constructors for which to generate JSON generating code.
323 -> Q Exp
323 -> Q Exp
324
324
325−consToValue _ _ [] = error $ "Data.Aeson.TH.consToValue: "
325+consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
326− ++ "Not a single constructor given!"
326+ ++ "Not a single constructor given!"
327
327
328−consToValue jc opts cons = do
328+consToValue target jc opts instTys cons = do
329 value <- newName "value"
329 value <- newName "value"
330 tjs <- newNameList "_tj" $ arityInt jc
330 tjs <- newNameList "_tj" $ arityInt jc
331 tjls <- newNameList "_tjl" $ arityInt jc
331 tjls <- newNameList "_tjl" $ arityInt jc
332 let zippedTJs = zip tjs tjls
332 let zippedTJs = zip tjs tjls
333 interleavedTJs = interleave tjs tjls
333 interleavedTJs = interleave tjs tjls
334+ lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
335+ tvMap = M.fromList $ zip lastTyVars zippedTJs
334 lamE (map varP $ interleavedTJs ++ [value]) $
336 lamE (map varP $ interleavedTJs ++ [value]) $
335− caseE (varE value) (matches zippedTJs)
337+ caseE (varE value) (matches tvMap)
336 where
338 where
337− matches tjs = case cons of
339+ matches tvMap = case cons of
338 -- A single constructor is directly encoded. The constructor itself may be
340 -- A single constructor is directly encoded. The constructor itself may be
339 -- forgotten.
341 -- forgotten.
340− [con] -> [argsToValue jc tjs opts False con]
342+ [con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
341 _ | allNullaryToStringTag opts && all isNullary cons ->
343 _ | allNullaryToStringTag opts && all isNullary cons ->
342− [ match (conP conName []) (normalB $ conStr opts conName) []
344+ [ match (conP conName []) (normalB $ conStr target opts conName) []
343 | con <- cons
345 | con <- cons
344− , let conName = getConName con
346+ , let conName = constructorName con
345 ]
347 ]
346− | otherwise -> [argsToValue jc tjs opts True con | con <- cons]
348+ | otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
347
349
350+-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
348−conStr :: Options -> Name -> Q Exp
351+conStr :: ToJSONFun -> Options -> Name -> Q Exp
349−conStr opts = appE [|String|] . conTxt opts
352+conStr Value opts = appE [|String|] . conTxt opts
353+conStr Encoding opts = appE [|E.text|] . conTxt opts
350
354
355+-- | Name of the constructor as a quoted 'Text'.
351conTxt :: Options -> Name -> Q Exp
356conTxt :: Options -> Name -> Q Exp
352−conTxt opts = appE [|T.pack|] . conStringE opts
357+conTxt opts = appE [|T.pack|] . stringE . conString opts
353−
354−conStringE :: Options -> Name -> Q Exp
355−conStringE opts = stringE . constructorTagModifier opts . nameBase
356−
357−-- | Helper function used by both 'deriveToJSON' and 'mkToEncoding'. Generates
358−-- code to write out a value for a number of constructors. All constructors
359−-- must be from the same type.
360−consToEncoding :: JSONClass
361− -- ^ The ToJSON variant being derived.
362− -> Options
363− -- ^ Encoding options.
364− -> [Con]
365− -- ^ Constructors for which to generate JSON generating code.
366− -> Q Exp
367−
368−consToEncoding _ _ [] = error $ "Data.Aeson.TH.consToEncoding: "
369− ++ "Not a single constructor given!"
370−
371−consToEncoding jc opts cons = do
372− value <- newName "value"
373− tes <- newNameList "_te" $ arityInt jc
374− tels <- newNameList "_tel" $ arityInt jc
375− let zippedTEs = zip tes tels
376− interleavedTEs = interleave tes tels
377− lamE (map varP $ interleavedTEs ++ [value]) $
378− caseE (varE value) (matches zippedTEs)
379− where
380− matches tes = case cons of
381− -- A single constructor is directly encoded. The constructor itself may be
382− -- forgotten.
383− [con] -> [argsToEncoding jc tes opts False con]
384− -- Encode just the name of the constructor of a sum type iff all the
385− -- constructors are nullary.
386− _ | allNullaryToStringTag opts && all isNullary cons ->
387− [ match (conP conName [])
388− (normalB $ encStr opts conName) []
389− | con <- cons
390− , let conName = getConName con
391− ]
392− | otherwise -> [argsToEncoding jc tes opts True con | con <- cons]
393
358
359+-- | Name of the constructor.
394−encStr :: Options -> Name -> Q Exp
360+conString :: Options -> Name -> String
395−encStr opts = appE [|E.text|] . conTxt opts
361+conString opts = constructorTagModifier opts . nameBase
396
362
397-- | If constructor is nullary.
363-- | If constructor is nullary.
398−isNullary :: Con -> Bool
364+isNullary :: ConstructorInfo -> Bool
399−isNullary (NormalC _ []) = True
365+isNullary ConstructorInfo { constructorVariant = NormalConstructor
366+ , constructorFields = tys } = null tys
400isNullary _ = False
367isNullary _ = False
401
368
402−sumToValue :: Options -> Bool -> Name -> Q Exp -> Q Exp
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 =
403−sumToValue opts multiCons conName exp
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
404 | multiCons =
410 | multiCons =
405 case sumEncoding opts of
411 case sumEncoding opts of
406 TwoElemArray ->
412 TwoElemArray ->
407− [|Array|] `appE` ([|V.fromList|] `appE` listE [conStr opts conName, exp])
413+ array target [conStr target opts conName, value]
408 TaggedObject{tagFieldName, contentsFieldName} ->
414 TaggedObject{tagFieldName, contentsFieldName} ->
409− [|A.object|] `appE` listE
415+ -- TODO: Maybe throw an error in case
410− [ infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
416+ -- tagFieldName overwrites a field in pairs.
417+ let tag = pairE tagFieldName (conStr target opts conName)
411− , infixApp [|T.pack contentsFieldName|] [|(.=)|] exp
418+ content = pairs contentsFieldName
412− ]
419+ in fromPairsE $
420+ if nullary then tag else infixApp tag [|(Monoid.<>)|] content
413 ObjectWithSingleField ->
421 ObjectWithSingleField ->
414− [|A.object|] `appE` listE
422+ objectE [(conString opts conName, value)]
415− [ infixApp (conTxt opts conName) [|(.=)|] exp
423+ UntaggedValue | nullary -> conStr target opts conName
416− ]
417− UntaggedValue -> exp
424+ UntaggedValue -> value
418− | otherwise = exp
425+ | otherwise = value
419−
420−nullarySumToValue :: Options -> Bool -> Name -> Q Exp
421−nullarySumToValue opts multiCons conName =
422− case sumEncoding opts of
423− TaggedObject{tagFieldName} ->
424− [|A.object|] `appE` listE
425− [ infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
426− ]
427− UntaggedValue -> conStr opts conName
428− _ -> sumToValue opts multiCons conName [e|toJSON ([] :: [()])|]
429
426
430-- | Generates code to generate the JSON encoding of a single constructor.
427-- | Generates code to generate the JSON encoding of a single constructor.
431−argsToValue :: JSONClass -> [(Name, Name)] -> Options -> Bool -> Con -> Q Match
428+argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
432−-- Nullary constructors. Generates code that explicitly matches against the
433−-- constructor even though it doesn't contain data. This is useful to prevent
434−-- type errors.
435−argsToValue jc tjs opts multiCons (NormalC conName []) = do
436− ([], _) <- reifyConTys jc tjs conName
437− match (conP conName [])
438− (normalB (nullarySumToValue opts multiCons conName))
439− []
440
429
441-- Polyadic constructors with special case for unary constructors.
430-- Polyadic constructors with special case for unary constructors.
442−argsToValue jc tjs opts multiCons (NormalC conName ts) = do
431+argsToValue target jc tvMap opts multiCons
443− (argTys, tvMap) <- reifyConTys jc tjs conName
432+ ConstructorInfo { constructorName = conName
433+ , constructorVariant = NormalConstructor
434+ , constructorFields = argTys } = do
435+ argTys' <- mapM resolveTypeSynonyms argTys
444− let len = length ts
436+ let len = length argTys'
445 args <- newNameList "arg" len
437 args <- newNameList "arg" len
446− js <- case [ dispatchToJSON jc conName tvMap argTy
438+ let js = case [ dispatchToJSON target jc conName tvMap argTy
447− `appE` varE arg
439+ `appE` varE arg
448− | (arg, argTy) <- zip args argTys
440+ | (arg, argTy) <- zip args argTys'
449− ] of
441+ ] of
450− -- Single argument is directly converted.
442+ -- Single argument is directly converted.
451− [e] -> return e
443+ [e] -> e
452− -- Multiple arguments are converted to a JSON array.
444+ -- Zero and multiple arguments are converted to a JSON array.
453− es -> do
445+ es -> array target es
454− mv <- newName "mv"
446+
455− let newMV = bindS (varP mv)
456− ([|VM.unsafeNew|] `appE`
457− litE (integerL $ fromIntegral len))
458− stmts = [ noBindS $
459− [|VM.unsafeWrite|] `appE`
460− varE mv `appE`
461− litE (integerL ix) `appE`
462− e
463− | (ix, e) <- zip [(0::Integer)..] es
464− ]
465− ret = noBindS $ [|return|] `appE` varE mv
466− return $ [|Array|] `appE`
467− (varE 'V.create `appE`
468− doE (newMV:stmts++[ret]))
469 match (conP conName $ map varP args)
447 match (conP conName $ map varP args)
470− (normalB $ sumToValue opts multiCons conName js)
448+ (normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
471 []
449 []
472
450
473-- Records.
451-- Records.
452+argsToValue target jc tvMap opts multiCons
453+ info@ConstructorInfo { constructorName = conName
454+ , constructorVariant = RecordConstructor fields
455+ , constructorFields = argTys } =
474−argsToValue jc tjs opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts, not multiCons, ts) of
456+ case (unwrapUnaryRecords opts, not multiCons, argTys) of
475− (True,True,[(_,st,ty)]) -> argsToValue jc tjs opts multiCons (NormalC conName [(st,ty)])
457+ (True,True,[_]) -> argsToValue target jc tvMap opts multiCons
458+ (info{constructorVariant = NormalConstructor})
476− _ -> do
459+ _ -> do
477− (argTys, tvMap) <- reifyConTys jc tjs conName
460+ argTys' <- mapM resolveTypeSynonyms argTys
478− args <- newNameList "arg" $ length ts
461+ args <- newNameList "arg" $ length argTys'
479− let exp = [|A.object|] `appE` pairs
480−
481− pairs | omitNothingFields opts = infixApp maybeFields
462+ let pairs | omitNothingFields opts = infixApp maybeFields
482− [|(++)|]
463+ [|(Monoid.<>)|]
483− restFields
464+ restFields
484− | otherwise = listE $ map toPair argCons
465+ | otherwise = mconcatE (map pureToPair argCons)
485−
466+
486− argCons = zip3 args argTys ts
467+ argCons = zip3 (map varE args) argTys' fields
487−
468+
488− maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
469+ maybeFields = mconcatE (map maybeToPair maybes)
489−
470+
490− restFields = listE $ map toPair rest
471+ restFields = mconcatE (map pureToPair rest)
491−
472+
492− (maybes, rest) = partition isMaybe argCons
473+ (maybes0, rest0) = partition isMaybe argCons
493−
474+ (options, rest) = partition isOption rest0
494− maybeToPair (arg, argTy, (field, _, _)) =
475+ maybes = maybes0 ++ map optionToMaybe options
495− infixApp ([|keyValuePairWith|]
476+
496− `appE` dispatchToJSON jc conName tvMap argTy
477+ maybeToPair = toPairLifted True
497− `appE` toFieldName field)
478+ pureToPair = toPairLifted False
498− [|(<$>)|]
479+
499− (varE arg)
500−
501− toPair (arg, argTy, (field, _, _)) =
480+ toPairLifted lifted (arg, argTy, field) =
502− [|keyValuePairWith|]
503− `appE` dispatchToJSON jc conName tvMap argTy
481+ let toValue = dispatchToJSON target jc conName tvMap argTy
504− `appE` toFieldName field
482+ fieldName = fieldLabel opts field
505− `appE` varE arg
483+ e arg' = pairE fieldName (toValue `appE` arg')
506−
484+ in if lifted
507− toFieldName field = [|T.pack|] `appE` fieldLabelExp opts field
485+ then do
508−
486+ x <- newName "x"
487+ [|maybe mempty|] `appE` lam1E (varP x) (e (varE x)) `appE` arg
488+ else e arg
489+
509− match (conP conName $ map varP args)
490+ match (conP conName $ map varP args)
510− ( normalB
491+ (normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
511− $ if multiCons
512− then case sumEncoding opts of
513− TwoElemArray -> [|toJSON|] `appE` tupE [conStr opts conName, exp]
514− TaggedObject{tagFieldName} ->
515− [|A.object|] `appE`
516− -- TODO: Maybe throw an error in case
517− -- tagFieldName overwrites a field in pairs.
518− infixApp (infixApp [|T.pack tagFieldName|]
519− [|(.=)|]
520− (conStr opts conName))
521− [|(:)|]
522− pairs
523− ObjectWithSingleField ->
524− [|A.object|] `appE` listE
525− [ infixApp (conTxt opts conName) [|(.=)|] exp ]
526− UntaggedValue -> exp
527− else exp
528− ) []
492+ []
529
493
530-- Infix constructors.
494-- Infix constructors.
531−argsToValue jc tjs opts multiCons (InfixC _ conName _) = do
495+argsToValue target jc tvMap opts multiCons
532− ([alTy, arTy], tvMap) <- reifyConTys jc tjs conName
496+ ConstructorInfo { constructorName = conName
497+ , constructorVariant = InfixConstructor
498+ , constructorFields = argTys } = do
499+ [alTy, arTy] <- mapM resolveTypeSynonyms argTys
533 al <- newName "argL"
500 al <- newName "argL"
534 ar <- newName "argR"
501 ar <- newName "argR"
535 match (infixP (varP al) conName (varP ar))
502 match (infixP (varP al) conName (varP ar))
536 ( normalB
503 ( normalB
537− $ sumToValue opts multiCons conName
504+ $ opaqueSumToValue target opts multiCons False conName
505+ $ array target
538− $ [|toJSON|] `appE` listE [ dispatchToJSON jc conName tvMap aTy
506+ [ dispatchToJSON target jc conName tvMap aTy
539− `appE` varE a
507+ `appE` varE a
540− | (a, aTy) <- [(al,alTy), (ar,arTy)]
508+ | (a, aTy) <- [(al,alTy), (ar,arTy)]
541− ]
509+ ]
542 )
510 )
543 []
511 []
544−-- Existentially quantified constructors.
545−argsToValue jc tjs opts multiCons (ForallC _ _ con) =
546− argsToValue jc tjs opts multiCons con
547
512
548−#if MIN_VERSION_template_haskell(2,11,0)
513+isMaybe :: (a, Type, b) -> Bool
549−-- GADTs.
514+isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
550−argsToValue jc tjs opts multiCons (GadtC conNames ts _) =
515+isMaybe _ = False
551− argsToValue jc tjs opts multiCons $ NormalC (head conNames) ts
552
516
553−argsToValue jc tjs opts multiCons (RecGadtC conNames ts _) =
517+isOption :: (a, Type, b) -> Bool
554− argsToValue jc tjs opts multiCons $ RecC (head conNames) ts
518+isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
555−#endif
519+isOption _ = False
556
520
557−isMaybe :: (a, b, (c, d, Type)) -> Bool
521+optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
558−isMaybe (_, _, (_, _, AppT (ConT t) _)) = t == ''Maybe
522+optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
559−isMaybe _ = False
560
523
561(<^>) :: ExpQ -> ExpQ -> ExpQ
524(<^>) :: ExpQ -> ExpQ -> ExpQ
562(<^>) a b = infixApp a [|(E.><)|] b
525(<^>) a b = infixApp a [|(E.><)|] b
563infixr 6 <^>
526infixr 6 <^>
564
527
565−(<:>) :: ExpQ -> ExpQ -> ExpQ
566−(<:>) a b = a <^> [|E.colon|] <^> b
567−infixr 5 <:>
568−
569(<%>) :: ExpQ -> ExpQ -> ExpQ
528(<%>) :: ExpQ -> ExpQ -> ExpQ
570(<%>) a b = a <^> [|E.comma|] <^> b
529(<%>) a b = a <^> [|E.comma|] <^> b
571infixr 4 <%>
530infixr 4 <%>
572
531
573−array :: ExpQ -> ExpQ
532+-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
574−array exp = [|E.wrapArray|] `appE` exp
533+array :: ToJSONFun -> [ExpQ] -> ExpQ
575−
534+array Encoding [] = [|E.emptyArray_|]
576−object :: ExpQ -> ExpQ
535+array Value [] = [|Array V.empty|]
577−object exp = [|E.wrapObject|] `appE` exp
536+array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
578−
537+array Value es = do
579−sumToEncoding :: Options -> Bool -> Name -> Q Exp -> Q Exp
538+ mv <- newName "mv"
580−sumToEncoding opts multiCons conName exp
539+ let newMV = bindS (varP mv)
581− | multiCons =
540+ ([|VM.unsafeNew|] `appE`
582− let fexp = exp in
541+ litE (integerL $ fromIntegral (length es)))
583− case sumEncoding opts of
542+ stmts = [ noBindS $
584− TwoElemArray ->
543+ [|VM.unsafeWrite|] `appE`
585− array (encStr opts conName <%> fexp)
544+ varE mv `appE`
586− TaggedObject{tagFieldName, contentsFieldName} ->
545+ litE (integerL ix) `appE`
587− object $
546+ e
588− ([|E.text (T.pack tagFieldName)|] <:> encStr opts conName) <%>
547+ | (ix, e) <- zip [(0::Integer)..] es
589− ([|E.text (T.pack contentsFieldName)|] <:> fexp)
548+ ]
590− ObjectWithSingleField ->
549+ ret = noBindS $ [|return|] `appE` varE mv
591− object (encStr opts conName <:> fexp)
550+ [|Array|] `appE`
592− UntaggedValue -> exp
551+ (varE 'V.create `appE`
593− | otherwise = exp
552+ doE (newMV:stmts++[ret]))
594−
595−nullarySumToEncoding :: Options -> Bool -> Name -> Q Exp
596−nullarySumToEncoding opts multiCons conName =
597− case sumEncoding opts of
598− TaggedObject{tagFieldName} ->
599− object $
600− [|E.text (T.pack tagFieldName)|] <:> encStr opts conName
601− UntaggedValue -> encStr opts conName
602− _ -> sumToEncoding opts multiCons conName [e|toEncoding ([] :: [()])|]
603−
604−-- | Generates code to generate the JSON encoding of a single constructor.
605−argsToEncoding :: JSONClass -> [(Name, Name)] -> Options -> Bool -> Con -> Q Match
606−-- Nullary constructors. Generates code that explicitly matches against the
607−-- constructor even though it doesn't contain data. This is useful to prevent
608−-- type errors.
609−argsToEncoding jc tes opts multiCons (NormalC conName []) = do
610− ([], _) <- reifyConTys jc tes conName
611− match (conP conName [])
612− (normalB (nullarySumToEncoding opts multiCons conName))
613− []
614−
615−-- Polyadic constructors with special case for unary constructors.
616−argsToEncoding jc tes opts multiCons (NormalC conName ts) = do
617− (argTys, tvMap) <- reifyConTys jc tes conName
618− let len = length ts
619− args <- newNameList "arg" len
620− js <- case zip args argTys of
621− -- Single argument is directly converted.
622− [(e,eTy)] -> return (dispatchToEncoding jc conName tvMap eTy
623− `appE` varE e)
624− -- Multiple arguments are converted to a JSON array.
625− es ->
626− return (array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap xTy
627− `appE` varE x
628− | (x,xTy) <- es
629− ]))
630− match (conP conName $ map varP args)
631− (normalB $ sumToEncoding opts multiCons conName js)
632− []
633
553
634−-- Records.
554+-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
635−argsToEncoding jc tes opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts, not multiCons, ts) of
555+objectE :: [(String, ExpQ)] -> ExpQ
636− (True,True,[(_,st,ty)]) -> argsToEncoding jc tes opts multiCons (NormalC conName [(st,ty)])
556+objectE = fromPairsE . mconcatE . fmap (uncurry pairE)
637− _ -> do
638− args <- newNameList "arg" $ length ts
639− (argTys, tvMap) <- reifyConTys jc tes conName
640− let exp = object objBody
641−
642− objBody = [|E.econcat|] `appE`
643− ([|intersperse E.comma|] `appE` pairs)
644− pairs | omitNothingFields opts = infixApp maybeFields
645− [|(++)|]
646− restFields
647− | otherwise = listE (map toPair argCons)
648−
649− argCons = zip3 args argTys ts
650−
651− maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
652−
653− restFields = listE (map toPair rest)
654−
655− (maybes, rest) = partition isMaybe argCons
656−
657− maybeToPair (arg, argTy, (field, _, _)) =
658− infixApp
659− (infixApp
660− (infixE
661− (Just $ toFieldName field <^> [|E.colon|])
662− [|(E.><)|]
663− Nothing)
664− [|(.)|]
665− (dispatchToEncoding jc conName tvMap argTy))
666− [|(<$>)|]
667− (varE arg)
668−
669− toPair (arg, argTy, (field, _, _)) =
670− toFieldName field
671− <:> dispatchToEncoding jc conName tvMap argTy
672− `appE` varE arg
673−
674− toFieldName field = [|E.text|] `appE`
675− ([|T.pack|] `appE` fieldLabelExp opts field)
676
557
677− match (conP conName $ map varP args)
558+-- | 'mconcat' a list of fixed length.
678− ( normalB
559+--
679− $ if multiCons
560+-- > mconcatE [ [|x|], [|y|], [|z|] ] = [| x <> (y <> z) |]
680− then case sumEncoding opts of
561+mconcatE :: [ExpQ] -> ExpQ
681− TwoElemArray -> array $
562+mconcatE [] = [|Monoid.mempty|]
682− encStr opts conName <%> exp
563+mconcatE [x] = x
683− TaggedObject{tagFieldName} -> object $
564+mconcatE (x : xs) = infixApp x [|(Monoid.<>)|] (mconcatE xs)
684− ([|E.text (T.pack tagFieldName)|] <:>
685− encStr opts conName) <%>
686− objBody
687− ObjectWithSingleField -> object $
688− encStr opts conName <:> exp
689− UntaggedValue -> exp
690− else exp
691− ) []
692−
693−-- Infix constructors.
694−argsToEncoding jc tes opts multiCons (InfixC _ conName _) = do
695− al <- newName "argL"
696− ar <- newName "argR"
697− ([alTy,arTy], tvMap) <- reifyConTys jc tes conName
698− match (infixP (varP al) conName (varP ar))
699− ( normalB
700− $ sumToEncoding opts multiCons conName
701− $ array (foldr1 (<%>) [ dispatchToEncoding jc conName tvMap aTy
702− `appE` varE a
703− | (a,aTy) <- [(al,alTy), (ar,arTy)]
704− ])
705− )
706− []
707−-- Existentially quantified constructors.
708−argsToEncoding jc tes opts multiCons (ForallC _ _ con) =
709− argsToEncoding jc tes opts multiCons con
710
565
711−#if MIN_VERSION_template_haskell(2,11,0)
566+fromPairsE :: ExpQ -> ExpQ
712−-- GADTs.
567+fromPairsE = ([|fromPairs|] `appE`)
713−argsToEncoding jc tes opts multiCons (GadtC conNames ts _) =
714− argsToEncoding jc tes opts multiCons $ NormalC (head conNames) ts
715
568
716−argsToEncoding jc tes opts multiCons (RecGadtC conNames ts _) =
569+-- | Create (an encoding of) a key-value pair.
717− argsToEncoding jc tes opts multiCons $ RecC (head conNames) ts
570+--
718−#endif
571+-- > pairE "k" [|v|] = [|pair "k" v|]
572+pairE :: String -> ExpQ -> ExpQ
573+pairE k v = [|pair k|] `appE` v
719
574
720--------------------------------------------------------------------------------
575--------------------------------------------------------------------------------
721-- FromJSON
576-- FromJSON
@@ -799,31 +654,41 @@ consFromJSON :: JSONClass
799 -- ^ Name of the type to which the constructors belong.
654 -- ^ Name of the type to which the constructors belong.
800 -> Options
655 -> Options
801 -- ^ Encoding options
656 -- ^ Encoding options
802− -> [Con]
657+ -> [Type]
658+ -- ^ The types from the data type/data family instance declaration
659+ -> [ConstructorInfo]
803 -- ^ Constructors for which to generate JSON parsing code.
660 -- ^ Constructors for which to generate JSON parsing code.
804 -> Q Exp
661 -> Q Exp
805
662
806−consFromJSON _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
663+consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
807− ++ "Not a single constructor given!"
664+ ++ "Not a single constructor given!"
808
665
809−consFromJSON jc tName opts cons = do
666+consFromJSON jc tName opts instTys cons = do
810 value <- newName "value"
667 value <- newName "value"
811 pjs <- newNameList "_pj" $ arityInt jc
668 pjs <- newNameList "_pj" $ arityInt jc
812 pjls <- newNameList "_pjl" $ arityInt jc
669 pjls <- newNameList "_pjl" $ arityInt jc
813 let zippedPJs = zip pjs pjls
670 let zippedPJs = zip pjs pjls
814 interleavedPJs = interleave pjs pjls
671 interleavedPJs = interleave pjs pjls
672+ lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
673+ tvMap = M.fromList $ zip lastTyVars zippedPJs
815− lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value zippedPJs
674+ lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
816
675
817 where
676 where
677+ checkExi tvMap con = checkExistentialContext jc tvMap
678+ (constructorContext con)
679+ (constructorName con)
680+
818− lamExpr value pjs = case cons of
681+ lamExpr value tvMap = case cons of
682+ [con]
683+ | not (tagSingleConstructors opts)
819− [con] -> parseArgs jc pjs tName opts con (Right value)
684+ -> checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right value)
820 _ | sumEncoding opts == UntaggedValue
685 _ | sumEncoding opts == UntaggedValue
821− -> parseUntaggedValue pjs cons value
686+ -> parseUntaggedValue tvMap cons value
822 | otherwise
687 | otherwise
823 -> caseE (varE value) $
688 -> caseE (varE value) $
824 if allNullaryToStringTag opts && all isNullary cons
689 if allNullaryToStringTag opts && all isNullary cons
825 then allNullaryMatches
690 then allNullaryMatches
826− else mixedMatches pjs
691+ else mixedMatches tvMap
827
692
828 allNullaryMatches =
693 allNullaryMatches =
829 [ do txt <- newName "txt"
694 [ do txt <- newName "txt"
@@ -832,12 +697,11 @@ consFromJSON jc tName opts cons = do
832 [ liftM2 (,) (normalG $
697 [ liftM2 (,) (normalG $
833 infixApp (varE txt)
698 infixApp (varE txt)
834 [|(==)|]
699 [|(==)|]
835− ([|T.pack|] `appE`
836− conStringE opts conName)
700+ (conTxt opts conName)
837 )
701 )
838 ([|pure|] `appE` conE conName)
702 ([|pure|] `appE` conE conName)
839 | con <- cons
703 | con <- cons
840− , let conName = getConName con
704+ , let conName = constructorName con
841 ]
705 ]
842 ++
706 ++
843 [ liftM2 (,)
707 [ liftM2 (,)
@@ -858,13 +722,13 @@ consFromJSON jc tName opts cons = do
858 []
722 []
859 ]
723 ]
860
724
861− mixedMatches pjs =
725+ mixedMatches tvMap =
862 case sumEncoding opts of
726 case sumEncoding opts of
863 TaggedObject {tagFieldName, contentsFieldName} ->
727 TaggedObject {tagFieldName, contentsFieldName} ->
864− parseObject $ parseTaggedObject pjs tagFieldName contentsFieldName
728+ parseObject $ parseTaggedObject tvMap tagFieldName contentsFieldName
865 UntaggedValue -> error "UntaggedValue: Should be handled already"
729 UntaggedValue -> error "UntaggedValue: Should be handled already"
866 ObjectWithSingleField ->
730 ObjectWithSingleField ->
867− parseObject $ parseObjectWithSingleField pjs
731+ parseObject $ parseObjectWithSingleField tvMap
868 TwoElemArray ->
732 TwoElemArray ->
869 [ do arr <- newName "array"
733 [ do arr <- newName "array"
870 match (conP 'Array [varP arr])
734 match (conP 'Array [varP arr])
@@ -872,7 +736,7 @@ consFromJSON jc tName opts cons = do
872 [ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
736 [ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
873 [|(==)|]
737 [|(==)|]
874 (litE $ integerL 2))
738 (litE $ integerL 2))
875− (parse2ElemArray pjs arr)
739+ (parse2ElemArray tvMap arr)
876 , liftM2 (,) (normalG [|otherwise|])
740 , liftM2 (,) (normalG [|otherwise|])
877 ([|not2ElemArray|]
741 ([|not2ElemArray|]
878 `appE` litE (stringL $ show tName)
742 `appE` litE (stringL $ show tName)
@@ -903,25 +767,29 @@ consFromJSON jc tName opts cons = do
903 []
767 []
904 ]
768 ]
905
769
906− parseTaggedObject pjs typFieldName valFieldName obj = do
770+ parseTaggedObject tvMap typFieldName valFieldName obj = do
907 conKey <- newName "conKey"
771 conKey <- newName "conKey"
908 doE [ bindS (varP conKey)
772 doE [ bindS (varP conKey)
909 (infixApp (varE obj)
773 (infixApp (varE obj)
910 [|(.:)|]
774 [|(.:)|]
911 ([|T.pack|] `appE` stringE typFieldName))
775 ([|T.pack|] `appE` stringE typFieldName))
912− , noBindS $ parseContents pjs conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
776+ , noBindS $ parseContents tvMap conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
913 ]
777 ]
914
778
915− parseUntaggedValue pjs cons' conVal =
779+ parseUntaggedValue tvMap cons' conVal =
916 foldr1 (\e e' -> infixApp e [|(<|>)|] e')
780 foldr1 (\e e' -> infixApp e [|(<|>)|] e')
917− (map (\x -> parseValue pjs x conVal) cons')
781+ (map (\x -> parseValue tvMap x conVal) cons')
918
782
919− parseValue _pjs (NormalC conName []) conVal = do
783+ parseValue _tvMap
784+ ConstructorInfo { constructorName = conName
785+ , constructorVariant = NormalConstructor
786+ , constructorFields = [] }
787+ conVal = do
920 str <- newName "str"
788 str <- newName "str"
921 caseE (varE conVal)
789 caseE (varE conVal)
922 [ match (conP 'String [varP str])
790 [ match (conP 'String [varP str])
923 (guardedB
791 (guardedB
924− [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] ([|T.pack|] `appE` conStringE opts conName)
792+ [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
925 )
793 )
926 ([|pure|] `appE` conE conName)
794 ([|pure|] `appE` conE conName)
927 ]
795 ]
@@ -929,11 +797,11 @@ consFromJSON jc tName opts cons = do
929 []
797 []
930 , matchFailed tName conName "String"
798 , matchFailed tName conName "String"
931 ]
799 ]
932− parseValue pjs con conVal =
800+ parseValue tvMap con conVal =
933− parseArgs jc pjs tName opts con (Right conVal)
801+ checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right conVal)
934
802
935
803
936− parse2ElemArray pjs arr = do
804+ parse2ElemArray tvMap arr = do
937 conKey <- newName "conKey"
805 conKey <- newName "conKey"
938 conVal <- newName "conVal"
806 conVal <- newName "conVal"
939 let letIx n ix =
807 let letIx n ix =
@@ -948,7 +816,7 @@ consFromJSON jc tName opts cons = do
948 (caseE (varE conKey)
816 (caseE (varE conKey)
949 [ do txt <- newName "txt"
817 [ do txt <- newName "txt"
950 match (conP 'String [varP txt])
818 match (conP 'String [varP txt])
951− (normalB $ parseContents pjs
819+ (normalB $ parseContents tvMap
952 txt
820 txt
953 (Right conVal)
821 (Right conVal)
954 'conNotFoundFail2ElemArray
822 'conNotFoundFail2ElemArray
@@ -965,12 +833,12 @@ consFromJSON jc tName opts cons = do
965 ]
833 ]
966 )
834 )
967
835
968− parseObjectWithSingleField pjs obj = do
836+ parseObjectWithSingleField tvMap obj = do
969 conKey <- newName "conKey"
837 conKey <- newName "conKey"
970 conVal <- newName "conVal"
838 conVal <- newName "conVal"
971 caseE ([e|H.toList|] `appE` varE obj)
839 caseE ([e|H.toList|] `appE` varE obj)
972 [ match (listP [tupP [varP conKey, varP conVal]])
840 [ match (listP [tupP [varP conKey, varP conVal]])
973− (normalB $ parseContents pjs conKey (Right conVal) 'conNotFoundFailObjectSingleField)
841+ (normalB $ parseContents tvMap conKey (Right conVal) 'conNotFoundFailObjectSingleField)
974 []
842 []
975 , do other <- newName "other"
843 , do other <- newName "other"
976 match (varP other)
844 match (varP other)
@@ -981,7 +849,7 @@ consFromJSON jc tName opts cons = do
981 []
849 []
982 ]
850 ]
983
851
984− parseContents pjs conKey contents errorFun =
852+ parseContents tvMap conKey contents errorFun =
985 caseE (varE conKey)
853 caseE (varE conKey)
986 [ match wildP
854 [ match wildP
987 ( guardedB $
855 ( guardedB $
@@ -989,7 +857,8 @@ consFromJSON jc tName opts cons = do
989 [|(==)|]
857 [|(==)|]
990 ([|T.pack|] `appE`
858 ([|T.pack|] `appE`
991 conNameExp opts con)
859 conNameExp opts con)
860+ e <- checkExi tvMap con $
992− e <- parseArgs jc pjs tName opts con contents
861+ parseArgs jc tvMap tName opts con contents
993 return (g, e)
862 return (g, e)
994 | con <- cons
863 | con <- cons
995 ]
864 ]
@@ -1002,7 +871,7 @@ consFromJSON jc tName opts cons = do
1002 . stringL
871 . stringL
1003 . constructorTagModifier opts
872 . constructorTagModifier opts
1004 . nameBase
873 . nameBase
1005− . getConName
874+ . constructorName
1006 ) cons
875 ) cons
1007 )
876 )
1008 `appE` ([|T.unpack|] `appE` varE conKey)
877 `appE` ([|T.unpack|] `appE` varE conKey)
@@ -1051,22 +920,42 @@ parseRecord :: JSONClass
1051 -> Options
920 -> Options
1052 -> Name
921 -> Name
1053 -> Name
922 -> Name
1054− -> [VarStrictType]
923+ -> [Name]
1055 -> Name
924 -> Name
925+ -> Bool
1056 -> ExpQ
926 -> ExpQ
1057−parseRecord jc tvMap argTys opts tName conName ts obj =
927+parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject =
928+ (if rejectUnknownFields opts
929+ then infixApp checkUnknownRecords [|(>>)|]
930+ else id) $
1058 foldl' (\a b -> infixApp a [|(<*>)|] b)
931 foldl' (\a b -> infixApp a [|(<*>)|] b)
1059 (infixApp (conE conName) [|(<$>)|] x)
932 (infixApp (conE conName) [|(<$>)|] x)
1060 xs
933 xs
1061 where
934 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+ ]
1062 x:xs = [ [|lookupField|]
951 x:xs = [ [|lookupField|]
1063 `appE` dispatchParseJSON jc conName tvMap argTy
952 `appE` dispatchParseJSON jc conName tvMap argTy
1064 `appE` litE (stringL $ show tName)
953 `appE` litE (stringL $ show tName)
1065 `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
954 `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
1066 `appE` varE obj
955 `appE` varE obj
1067− `appE` ( [|T.pack|] `appE` fieldLabelExp opts field
956+ `appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
1068 )
957 )
1069− | ((field, _, _), argTy) <- zip ts argTys
958+ | (field, argTy) <- zip fields argTys
1070 ]
959 ]
1071
960
1072getValField :: Name -> String -> [MatchQ] -> Q Exp
961getValField :: Name -> String -> [MatchQ] -> Q Exp
@@ -1085,67 +974,82 @@ matchCases (Right valName) = caseE (varE valName)
1085
974
1086-- | Generates code to parse the JSON encoding of a single constructor.
975-- | Generates code to parse the JSON encoding of a single constructor.
1087parseArgs :: JSONClass -- ^ The FromJSON variant being derived.
976parseArgs :: JSONClass -- ^ The FromJSON variant being derived.
977+ -> TyVarMap -- ^ Maps the last type variables to their decoding
1088− -> [(Name, Name)] -- ^ The names of the encoding/decoding function arguments.
978+ -- function arguments.
1089 -> Name -- ^ Name of the type to which the constructor belongs.
979 -> Name -- ^ Name of the type to which the constructor belongs.
1090 -> Options -- ^ Encoding options.
980 -> Options -- ^ Encoding options.
1091− -> Con -- ^ Constructor for which to generate JSON parsing code.
981+ -> ConstructorInfo -- ^ Constructor for which to generate JSON parsing code.
1092 -> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
982 -> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
1093 -- Right valName
983 -- Right valName
1094 -> Q Exp
984 -> Q Exp
1095-- Nullary constructors.
985-- Nullary constructors.
1096−parseArgs jc pjs _ _ (NormalC conName []) (Left _) = do
986+parseArgs _ _ _ _
1097− ([], _) <- reifyConTys jc pjs conName
987+ ConstructorInfo { constructorName = conName
988+ , constructorVariant = NormalConstructor
989+ , constructorFields = [] }
990+ (Left _) =
1098− [|pure|] `appE` conE conName
991+ [|pure|] `appE` conE conName
1099−parseArgs jc pjs tName _ (NormalC conName []) (Right valName) = do
992+parseArgs _ _ tName _
1100− ([], _) <- reifyConTys jc pjs conName
993+ ConstructorInfo { constructorName = conName
994+ , constructorVariant = NormalConstructor
995+ , constructorFields = [] }
996+ (Right valName) =
1101− caseE (varE valName) $ parseNullaryMatches tName conName
997+ caseE (varE valName) $ parseNullaryMatches tName conName
1102
998
1103-- Unary constructors.
999-- Unary constructors.
1104−parseArgs jc pjs _ _ (NormalC conName [_]) contents = do
1000+parseArgs jc tvMap _ _
1105− ([argTy], tvMap) <- reifyConTys jc pjs conName
1001+ ConstructorInfo { constructorName = conName
1002+ , constructorVariant = NormalConstructor
1003+ , constructorFields = [argTy] }
1004+ contents = do
1005+ argTy' <- resolveTypeSynonyms argTy
1106− matchCases contents $ parseUnaryMatches jc tvMap argTy conName
1006+ matchCases contents $ parseUnaryMatches jc tvMap argTy' conName
1107
1007
1108-- Polyadic constructors.
1008-- Polyadic constructors.
1109−parseArgs jc pjs tName _ (NormalC conName ts) contents = do
1009+parseArgs jc tvMap tName _
1110− (argTys, tvMap) <- reifyConTys jc pjs conName
1010+ ConstructorInfo { constructorName = conName
1011+ , constructorVariant = NormalConstructor
1012+ , constructorFields = argTys }
1013+ contents = do
1014+ argTys' <- mapM resolveTypeSynonyms argTys
1111− let len = genericLength ts
1015+ let len = genericLength argTys'
1112− matchCases contents $ parseProduct jc tvMap argTys tName conName len
1016+ matchCases contents $ parseProduct jc tvMap argTys' tName conName len
1113
1017
1114-- Records.
1018-- Records.
1115−parseArgs jc pjs tName opts (RecC conName ts) (Left (_, obj)) = do
1019+parseArgs jc tvMap tName opts
1116− (argTys, tvMap) <- reifyConTys jc pjs conName
1020+ ConstructorInfo { constructorName = conName
1021+ , constructorVariant = RecordConstructor fields
1022+ , constructorFields = argTys }
1023+ (Left (_, obj)) = do
1024+ argTys' <- mapM resolveTypeSynonyms argTys
1117− parseRecord jc tvMap argTys opts tName conName ts obj
1025+ parseRecord jc tvMap argTys' opts tName conName fields obj True
1026+parseArgs jc tvMap tName opts
1027+ info@ConstructorInfo { constructorName = conName
1028+ , constructorVariant = RecordConstructor fields
1029+ , constructorFields = argTys }
1030+ (Right valName) =
1118−parseArgs jc pjs tName opts (RecC conName ts) (Right valName) = case (unwrapUnaryRecords opts,ts) of
1031+ case (unwrapUnaryRecords opts,argTys) of
1119− (True,[(_,st,ty)])-> parseArgs jc pjs tName opts (NormalC conName [(st,ty)]) (Right valName)
1032+ (True,[_])-> parseArgs jc tvMap tName opts
1033+ (info{constructorVariant = NormalConstructor})
1034+ (Right valName)
1120− _ -> do
1035+ _ -> do
1121− obj <- newName "recObj"
1036+ obj <- newName "recObj"
1122− (argTys, tvMap) <- reifyConTys jc pjs conName
1037+ argTys' <- mapM resolveTypeSynonyms argTys
1123− caseE (varE valName)
1038+ caseE (varE valName)
1124− [ match (conP 'Object [varP obj]) (normalB $
1039+ [ match (conP 'Object [varP obj]) (normalB $
1125− parseRecord jc tvMap argTys opts tName conName ts obj) []
1040+ parseRecord jc tvMap argTys' opts tName conName fields obj False) []
1126− , matchFailed tName conName "Object"
1041+ , matchFailed tName conName "Object"
1127− ]
1042+ ]
1128
1043
1129-- Infix constructors. Apart from syntax these are the same as
1044-- Infix constructors. Apart from syntax these are the same as
1130-- polyadic constructors.
1045-- polyadic constructors.
1131−parseArgs jc pjs tName _ (InfixC _ conName _) contents = do
1046+parseArgs jc tvMap tName _
1132− (argTys, tvMap) <- reifyConTys jc pjs conName
1047+ ConstructorInfo { constructorName = conName
1048+ , constructorVariant = InfixConstructor
1049+ , constructorFields = argTys }
1050+ contents = do
1051+ argTys' <- mapM resolveTypeSynonyms argTys
1133− matchCases contents $ parseProduct jc tvMap argTys tName conName 2
1052+ matchCases contents $ parseProduct jc tvMap argTys' tName conName 2
1134−
1135−-- Existentially quantified constructors. We ignore the quantifiers
1136−-- and proceed with the contained constructor.
1137−parseArgs jc pjs tName opts (ForallC _ _ con) contents =
1138− parseArgs jc pjs tName opts con contents
1139−
1140−#if MIN_VERSION_template_haskell(2,11,0)
1141−-- GADTs. We ignore the refined return type and proceed as if it were a
1142−-- NormalC or RecC.
1143−parseArgs jc pjs tName opts (GadtC conNames ts _) contents =
1144− parseArgs jc pjs tName opts (NormalC (head conNames) ts) contents
1145−
1146−parseArgs jc pjs tName opts (RecGadtC conNames ts _) contents =
1147− parseArgs jc pjs tName opts (RecC (head conNames) ts) contents
1148−#endif
1149
1053
1150-- | Generates code to parse the JSON encoding of an n-ary
1054-- | Generates code to parse the JSON encoding of an n-ary
1151-- constructor.
1055-- constructor.
@@ -1219,9 +1123,14 @@ class LookupField a where
1219instance OVERLAPPABLE_ LookupField a where
1123instance OVERLAPPABLE_ LookupField a where
1220 lookupField = lookupFieldWith
1124 lookupField = lookupFieldWith
1221
1125
1222−instance LookupField (Maybe a) where
1126+instance INCOHERENT_ LookupField (Maybe a) where
1223 lookupField pj _ _ = parseOptionalFieldWith pj
1127 lookupField pj _ _ = parseOptionalFieldWith pj
1224
1128
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+
1225lookupFieldWith :: (Value -> Parser a) -> String -> String
1134lookupFieldWith :: (Value -> Parser a) -> String -> String
1226 -> Object -> T.Text -> Parser a
1135 -> Object -> T.Text -> Parser a
1227lookupFieldWith pj tName rec obj key =
1136lookupFieldWith pj tName rec obj key =
@@ -1229,9 +1138,6 @@ lookupFieldWith pj tName rec obj key =
1229 Nothing -> unknownFieldFail tName rec (T.unpack key)
1138 Nothing -> unknownFieldFail tName rec (T.unpack key)
1230 Just v -> pj v <?> Key key
1139 Just v -> pj v <?> Key key
1231
1140
1232−keyValuePairWith :: (v -> Value) -> T.Text -> v -> Pair
1233−keyValuePairWith tj name value = (name, tj value)
1234−
1235unknownFieldFail :: String -> String -> String -> Parser fail
1141unknownFieldFail :: String -> String -> String -> Parser fail
1236unknownFieldFail tName rec key =
1142unknownFieldFail tName rec key =
1237 fail $ printf "When parsing the record %s of type %s the key %s was not present."
1143 fail $ printf "When parsing the record %s of type %s the key %s was not present."
@@ -1300,7 +1206,8 @@ deriveJSONBoth dtj dfj opts name =
1300 liftM2 (++) (dtj opts name) (dfj opts name)
1206 liftM2 (++) (dtj opts name) (dfj opts name)
1301
1207
1302-- | Functionality common to @deriveToJSON(1)(2)@ and @deriveFromJSON(1)(2)@.
1208-- | Functionality common to @deriveToJSON(1)(2)@ and @deriveFromJSON(1)(2)@.
1303−deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Con] -> Q Exp)]
1209+deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
1210+ -> [ConstructorInfo] -> Q Exp)]
1304 -- ^ The class methods and the functions which derive them.
1211 -- ^ The class methods and the functions which derive them.
1305 -> JSONClass
1212 -> JSONClass
1306 -- ^ The class for which to generate an instance.
1213 -- ^ The class for which to generate an instance.
@@ -1310,27 +1217,34 @@ deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Con] -> Q Exp)]
1310 -- ^ Name of the type for which to generate a class instance
1217 -- ^ Name of the type for which to generate a class instance
1311 -- declaration.
1218 -- declaration.
1312 -> Q [Dec]
1219 -> Q [Dec]
1313−deriveJSONClass consFuns jc opts name =
1220+deriveJSONClass consFuns jc opts name = do
1314− withType name $ \name' ctxt tvbs cons mbTys ->
1221+ info <- reifyDatatype name
1315− (:[]) <$> fromCons name' ctxt tvbs cons mbTys
1222+ case info of
1316− where
1223+ DatatypeInfo { datatypeContext = ctxt
1317− fromCons :: Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q Dec
1224+ , datatypeName = parentName
1318− fromCons name' ctxt tvbs cons mbTys = do
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
1232+ } -> do
1319 (instanceCxt, instanceType)
1233 (instanceCxt, instanceType)
1320− <- buildTypeInstance name' jc ctxt tvbs mbTys
1234+ <- buildTypeInstance parentName jc ctxt instTys variant
1321− instanceD (return instanceCxt)
1235+ (:[]) <$> instanceD (return instanceCxt)
1322− (return instanceType)
1236+ (return instanceType)
1323− (methodDecs name' cons)
1237+ (methodDecs parentName instTys cons)
1324−
1238+ where
1325− methodDecs :: Name -> [Con] -> [Q Dec]
1239+ methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
1326− methodDecs name' cons = flip map consFuns $ \(jf, jfMaker) ->
1240+ methodDecs parentName instTys cons = flip map consFuns $ \(jf, jfMaker) ->
1327 funD (jsonFunValName jf (arity jc))
1241 funD (jsonFunValName jf (arity jc))
1328 [ clause []
1242 [ clause []
1329− (normalB $ jfMaker jc name' opts cons)
1243+ (normalB $ jfMaker jc parentName opts instTys cons)
1330 []
1244 []
1331 ]
1245 ]
1332
1246
1333−mkFunCommon :: (JSONClass -> Name -> Options -> [Con] -> Q Exp)
1247+mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q Exp)
1334 -- ^ The function which derives the expression.
1248 -- ^ The function which derives the expression.
1335 -> JSONClass
1249 -> JSONClass
1336 -- ^ Which class's method is being derived.
1250 -- ^ Which class's method is being derived.
@@ -1339,15 +1253,24 @@ mkFunCommon :: (JSONClass -> Name -> Options -> [Con] -> Q Exp)
1339 -> Name
1253 -> Name
1340 -- ^ Name of the encoded type.
1254 -- ^ Name of the encoded type.
1341 -> Q Exp
1255 -> Q Exp
1342−mkFunCommon consFun jc opts name = withType name fromCons
1256+mkFunCommon consFun jc opts name = do
1343− where
1257+ info <- reifyDatatype name
1344− fromCons :: Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q Exp
1258+ case info of
1345− fromCons name' ctxt tvbs cons mbTys = do
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
1268+ } -> do
1346− -- We force buildTypeInstance here since it performs some checks for whether
1269+ -- We force buildTypeInstance here since it performs some checks for whether
1347− -- or not the provided datatype's kind matches the derived method's
1270+ -- or not the provided datatype's kind matches the derived method's
1348− -- typeclass, and produces errors if it can't.
1271+ -- typeclass, and produces errors if it can't.
1349− !_ <- buildTypeInstance name' jc ctxt tvbs mbTys
1272+ !_ <- buildTypeInstance parentName jc ctxt instTys variant
1350− consFun jc name' opts cons
1273+ consFun jc parentName opts instTys cons
1351
1274
1352dispatchFunByType :: JSONClass
1275dispatchFunByType :: JSONClass
1353 -> JSONFun
1276 -> JSONFun
@@ -1392,200 +1315,37 @@ dispatchFunByType jc jf conName tvMap list ty = do
1392 (interleave rhsArgs rhsArgs)
1315 (interleave rhsArgs rhsArgs)
1393 else varE $ jsonFunValOrListName list jf Arity0
1316 else varE $ jsonFunValOrListName list jf Arity0
1394
1317
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+
1395−dispatchToJSON, dispatchToEncoding, dispatchParseJSON
1323+dispatchParseJSON
1396 :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1324 :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1397−dispatchToJSON jc n tvMap = dispatchFunByType jc ToJSON n tvMap False
1398−dispatchToEncoding jc n tvMap = dispatchFunByType jc ToEncoding n tvMap False
1399dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
1325dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
1400
1326
1401--------------------------------------------------------------------------------
1327--------------------------------------------------------------------------------
1402-- Utility functions
1328-- Utility functions
1403--------------------------------------------------------------------------------
1329--------------------------------------------------------------------------------
1404
1330
1405−-- | Boilerplate for top level splices.
1331+-- For the given Types, generate an instance context and head.
1406−--
1407−-- The given 'Name' must meet one of two criteria:
1408−--
1409−-- 1. It must be the name of a type constructor of a plain data type or newtype.
1410−-- 2. It must be the name of a data family instance or newtype instance constructor.
1411−
1412−-- Any other value will result in an exception.
1413−withType :: Name
1414− -> (Name -> Cxt -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q a)
1415− -- ^ Function that generates the actual code. Will be applied
1416− -- to the datatype/data family 'Name', datatype context, type
1417− -- variable binders and constructors extracted from the given
1418− -- 'Name'. If the 'Name' is from a data family instance
1419− -- constructor, it will also have its instantiated types;
1420− -- otherwise, it will be 'Nothing'.
1421− -> Q a
1422− -- ^ Resulting value in the 'Q'uasi monad.
1423−withType name f = do
1424− info <- reify name
1425− case info of
1426− TyConI dec ->
1427− case dec of
1428−#if MIN_VERSION_template_haskell(2,11,0)
1429− DataD ctxt _ tvbs _ cons _ -> f name ctxt tvbs cons Nothing
1430− NewtypeD ctxt _ tvbs _ con _ -> f name ctxt tvbs [con] Nothing
1431−#else
1432− DataD ctxt _ tvbs cons _ -> f name ctxt tvbs cons Nothing
1433− NewtypeD ctxt _ tvbs con _ -> f name ctxt tvbs [con] Nothing
1434−#endif
1435− other -> fail $ ns ++ "Unsupported type: " ++ show other
1436−#if MIN_VERSION_template_haskell(2,11,0)
1437− DataConI _ _ parentName -> do
1438−#else
1439− DataConI _ _ parentName _ -> do
1440−#endif
1441− parentInfo <- reify parentName
1442− case parentInfo of
1443−#if MIN_VERSION_template_haskell(2,11,0)
1444− FamilyI (DataFamilyD _ tvbs _) decs ->
1445−#else
1446− FamilyI (FamilyD DataFam _ tvbs _) decs ->
1447−#endif
1448− let instDec = flip find decs $ \dec -> case dec of
1449−#if MIN_VERSION_template_haskell(2,11,0)
1450− DataInstD _ _ _ _ cons _ -> any ((name ==) . getConName) cons
1451− NewtypeInstD _ _ _ _ con _ -> name == getConName con
1452−#else
1453− DataInstD _ _ _ cons _ -> any ((name ==) . getConName) cons
1454− NewtypeInstD _ _ _ con _ -> name == getConName con
1455−#endif
1456− _ -> error $ ns ++ "Must be a data or newtype instance."
1457− in case instDec of
1458−#if MIN_VERSION_template_haskell(2,11,0)
1459− Just (DataInstD ctxt _ instTys _ cons _) -> f parentName ctxt tvbs cons $ Just instTys
1460− Just (NewtypeInstD ctxt _ instTys _ con _) -> f parentName ctxt tvbs [con] $ Just instTys
1461−#else
1462− Just (DataInstD ctxt _ instTys cons _) -> f parentName ctxt tvbs cons $ Just instTys
1463− Just (NewtypeInstD ctxt _ instTys con _) -> f parentName ctxt tvbs [con] $ Just instTys
1464−#endif
1465− _ -> fail $ ns ++
1466− "Could not find data or newtype instance constructor."
1467− _ -> fail $ ns ++ "Data constructor " ++ show name ++
1468− " is not from a data family instance constructor."
1469−#if MIN_VERSION_template_haskell(2,11,0)
1470− FamilyI DataFamilyD{} _ ->
1471−#else
1472− FamilyI (FamilyD DataFam _ _ _) _ ->
1473−#endif
1474− fail $ ns ++
1475− "Cannot use a data family name. Use a data family instance constructor instead."
1476− _ -> fail $ ns ++ "I need the name of a plain data type constructor, "
1477− ++ "or a data family instance constructor."
1478− where
1479− ns :: String
1480− ns = "Data.Aeson.TH.withType: "
1481−
1482−-- | Infer the context and instance head needed for a FromJSON or ToJSON instance.
1483buildTypeInstance :: Name
1332buildTypeInstance :: Name
1484 -- ^ The type constructor or data family name
1333 -- ^ The type constructor or data family name
1485 -> JSONClass
1334 -> JSONClass
1486 -- ^ The typeclass to derive
1335 -- ^ The typeclass to derive
1487 -> Cxt
1336 -> Cxt
1488 -- ^ The datatype context
1337 -- ^ The datatype context
1489− -> [TyVarBndr]
1338+ -> [Type]
1490− -- ^ The type variables from the data type/data family declaration
1339+ -- ^ The types to instantiate the instance with
1491− -> Maybe [Type]
1340+ -> DatatypeVariant
1492− -- ^ 'Just' the types used to instantiate a data family instance,
1341+ -- ^ Are we dealing with a data family instance or not
1493− -- or 'Nothing' if it's a plain data type
1494 -> Q (Cxt, Type)
1342 -> Q (Cxt, Type)
1495− -- ^ The resulting 'Cxt' and 'Type' to use in a class instance
1496−-- Plain data type/newtype case
1497−buildTypeInstance tyConName jc dataCxt tvbs Nothing =
1498− let varTys :: [Type]
1499− varTys = map tvbToType tvbs
1500− in buildTypeInstanceFromTys tyConName jc dataCxt varTys False
1501−-- Data family instance case
1502−--
1503−-- The CPP is present to work around a couple of annoying old GHC bugs.
1504−-- See Note [Polykinded data families in Template Haskell]
1505−buildTypeInstance dataFamName jc dataCxt tvbs (Just instTysAndKinds) = do
1506−#if !(MIN_VERSION_template_haskell(2,8,0)) || MIN_VERSION_template_haskell(2,10,0)
1507− let instTys :: [Type]
1508− instTys = zipWith stealKindForType tvbs instTysAndKinds
1509−#else
1510− let kindVarNames :: [Name]
1511− kindVarNames = nub $ concatMap (tyVarNamesOfType . tvbKind) tvbs
1512−
1513− numKindVars :: Int
1514− numKindVars = length kindVarNames
1515−
1516− givenKinds, givenKinds' :: [Kind]
1517− givenTys :: [Type]
1518− (givenKinds, givenTys) = splitAt numKindVars instTysAndKinds
1519− givenKinds' = map sanitizeStars givenKinds
1520−
1521− -- A GHC 7.6-specific bug requires us to replace all occurrences of
1522− -- (ConT GHC.Prim.*) with StarT, or else Template Haskell will reject it.
1523− -- Luckily, (ConT GHC.Prim.*) only seems to occur in this one spot.
1524− sanitizeStars :: Kind -> Kind
1525− sanitizeStars = go
1526− where
1527− go :: Kind -> Kind
1528− go (AppT t1 t2) = AppT (go t1) (go t2)
1529− go (SigT t k) = SigT (go t) (go k)
1530− go (ConT n) | n == starKindName = StarT
1531− go t = t
1532−
1533− -- It's quite awkward to import * from GHC.Prim, so we'll just
1534− -- hack our way around it.
1535− starKindName :: Name
1536− starKindName = mkNameG_tc "ghc-prim" "GHC.Prim" "*"
1537−
1538− -- If we run this code with GHC 7.8, we might have to generate extra type
1539− -- variables to compensate for any type variables that Template Haskell
1540− -- eta-reduced away.
1541− -- See Note [Polykinded data families in Template Haskell]
1542− xTypeNames <- newNameList "tExtra" (length tvbs - length givenTys)
1543−
1544− let xTys :: [Type]
1545− xTys = map VarT xTypeNames
1546− -- ^ Because these type variables were eta-reduced away, we can only
1547− -- determine their kind by using stealKindForType. Therefore, we mark
1548− -- them as VarT to ensure they will be given an explicit kind annotation
1549− -- (and so the kind inference machinery has the right information).
1550−
1551− substNamesWithKinds :: [(Name, Kind)] -> Type -> Type
1552− substNamesWithKinds nks t = foldr' (uncurry substNameWithKind) t nks
1553−
1554− -- The types from the data family instance might not have explicit kind
1555− -- annotations, which the kind machinery needs to work correctly. To
1556− -- compensate, we use stealKindForType to explicitly annotate any
1557− -- types without kind annotations.
1558− instTys :: [Type]
1559− instTys = map (substNamesWithKinds (zip kindVarNames givenKinds'))
1560− -- Note that due to a GHC 7.8-specific bug
1561− -- (see Note [Polykinded data families in Template Haskell]),
1562− -- there may be more kind variable names than there are kinds
1563− -- to substitute. But this is OK! If a kind is eta-reduced, it
1564− -- means that is was not instantiated to something more specific,
1565− -- so we need not substitute it. Using stealKindForType will
1566− -- grab the correct kind.
1567− $ zipWith stealKindForType tvbs (givenTys ++ xTys)
1568−#endif
1569− buildTypeInstanceFromTys dataFamName jc dataCxt instTys True
1570−
1571−-- For the given Types, generate an instance context and head.
1572−buildTypeInstanceFromTys :: Name
1573− -- ^ The type constructor or data family name
1574− -> JSONClass
1575− -- ^ The typeclass to derive
1576− -> Cxt
1577− -- ^ The datatype context
1578− -> [Type]
1579− -- ^ The types to instantiate the instance with
1580− -> Bool
1581− -- ^ True if it's a data family, False otherwise
1582− -> Q (Cxt, Type)
1583−buildTypeInstanceFromTys tyConName jc dataCxt varTysOrig isDataFamily = do
1343+buildTypeInstance tyConName jc dataCxt varTysOrig variant = do
1584 -- Make sure to expand through type/kind synonyms! Otherwise, the
1344 -- Make sure to expand through type/kind synonyms! Otherwise, the
1585 -- eta-reduction check might get tripped up over type variables in a
1345 -- eta-reduction check might get tripped up over type variables in a
1586 -- synonym that are actually dropped.
1346 -- synonym that are actually dropped.
1587 -- (See GHC Trac #11416 for a scenario where this actually happened.)
1347 -- (See GHC Trac #11416 for a scenario where this actually happened.)
1588− varTysExp <- mapM expandSyn varTysOrig
1348+ varTysExp <- mapM resolveTypeSynonyms varTysOrig
1589
1349
1590 let remainingLength :: Int
1350 let remainingLength :: Int
1591 remainingLength = length varTysOrig - arityInt jc
1351 remainingLength = length varTysOrig - arityInt jc
@@ -1615,7 +1375,7 @@ buildTypeInstanceFromTys tyConName jc dataCxt varTysOrig isDataFamily = do
1615 -- All of the type variables mentioned in the dropped types
1375 -- All of the type variables mentioned in the dropped types
1616 -- (post-synonym expansion)
1376 -- (post-synonym expansion)
1617 droppedTyVarNames :: [Name]
1377 droppedTyVarNames :: [Name]
1618− droppedTyVarNames = concatMap tyVarNamesOfType droppedTysExpSubst
1378+ droppedTyVarNames = freeVariables droppedTysExpSubst
1619
1379
1620 -- If any of the dropped types were polykinded, ensure that they are of kind *
1380 -- If any of the dropped types were polykinded, ensure that they are of kind *
1621 -- after substituting * for the dropped kind variables. If not, throw an error.
1381 -- after substituting * for the dropped kind variables. If not, throw an error.
@@ -1653,9 +1413,16 @@ buildTypeInstanceFromTys tyConName jc dataCxt varTysOrig isDataFamily = do
1653 -- instance C (Fam [Char])
1413 -- instance C (Fam [Char])
1654 remainingTysOrigSubst :: [Type]
1414 remainingTysOrigSubst :: [Type]
1655 remainingTysOrigSubst =
1415 remainingTysOrigSubst =
1656− map (substNamesWithKindStar (union droppedKindVarNames kvNames'))
1416+ map (substNamesWithKindStar (droppedKindVarNames `union` kvNames'))
1657 $ take remainingLength varTysOrig
1417 $ take remainingLength varTysOrig
1658
1418
1419+ isDataFamily :: Bool
1420+ isDataFamily = case variant of
1421+ Datatype -> False
1422+ Newtype -> False
1423+ DataInstance -> True
1424+ NewtypeInstance -> True
1425+
1659 remainingTysOrigSubst' :: [Type]
1426 remainingTysOrigSubst' :: [Type]
1660 -- See Note [Kind signatures in derived instances] for an explanation
1427 -- See Note [Kind signatures in derived instances] for an explanation
1661 -- of the isDataFamily check.
1428 -- of the isDataFamily check.
@@ -1709,55 +1476,6 @@ deriveConstraint jc t
1709 jcConstraint = jsonClassName . JSONClass (direction jc)
1476 jcConstraint = jsonClassName . JSONClass (direction jc)
1710
1477
1711{-
1478{-
1712−Note [Polykinded data families in Template Haskell]
1713−~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1714−
1715−In order to come up with the correct instance context and head for an instance, e.g.,
1716−
1717− instance C a => C (Data a) where ...
1718−
1719−We need to know the exact types and kinds used to instantiate the instance. For
1720−plain old datatypes, this is simple: every type must be a type variable, and
1721−Template Haskell reliably tells us the type variables and their kinds.
1722−
1723−Doing the same for data families proves to be much harder for three reasons:
1724−
1725−1. On any version of Template Haskell, it may not tell you what an instantiated
1726− type's kind is. For instance, in the following data family instance:
1727−
1728− data family Fam (f :: * -> *) (a :: *)
1729− data instance Fam f a
1730−
1731− Then if we use TH's reify function, it would tell us the TyVarBndrs of the
1732− data family declaration are:
1733−
1734− [KindedTV f (AppT (AppT ArrowT StarT) StarT),KindedTV a StarT]
1735−
1736− and the instantiated types of the data family instance are:
1737−
1738− [VarT f1,VarT a1]
1739−
1740− We can't just pass [VarT f1,VarT a1] to buildTypeInstanceFromTys, since we
1741− have no way of knowing their kinds. Luckily, the TyVarBndrs tell us what the
1742− kind is in case an instantiated type isn't a SigT, so we use the stealKindForType
1743− function to ensure all of the instantiated types are SigTs before passing them
1744− to buildTypeInstanceFromTys.
1745−2. On GHC 7.6 and 7.8, a bug is present in which Template Haskell lists all of
1746− the specified kinds of a data family instance efore any of the instantiated
1747− types. Fortunately, this is easy to deal with: you simply count the number of
1748− distinct kind variables in the data family declaration, take that many elements
1749− from the front of the Types list of the data family instance, substitute the
1750− kind variables with their respective instantiated kinds (which you took earlier),
1751− and proceed as normal.
1752−3. On GHC 7.8, an even uglier bug is present (GHC Trac #9692) in which Template
1753− Haskell might not even list all of the Types of a data family instance, since
1754− they are eta-reduced away! And yes, kinds can be eta-reduced too.
1755−
1756− The simplest workaround is to count how many instantiated types are missing from
1757− the list and generate extra type variables to use in their place. Luckily, we
1758− needn't worry much if its kind was eta-reduced away, since using stealKindForType
1759− will get it back.
1760−
1761Note [Kind signatures in derived instances]
1479Note [Kind signatures in derived instances]
1762~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1480~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1763
1481
@@ -1835,50 +1553,14 @@ things we can do to make instance contexts that work for 80% of use cases:
1835 and perform kind substitution as in the other cases.
1553 and perform kind substitution as in the other cases.
1836-}
1554-}
1837
1555
1838−-- Determines the types of a constructor's arguments as well as the last type
1556+checkExistentialContext :: JSONClass -> TyVarMap -> Cxt -> Name
1839−-- parameters (mapped to their encoding/decoding functions), expanding through
1557+ -> Q a -> Q a
1840−-- any type synonyms.
1558+checkExistentialContext jc tvMap ctxt conName q =
1841−--
1842−-- The type parameters are determined on a constructor-by-constructor basis since
1843−-- they may be refined to be particular types in a GADT.
1844−reifyConTys :: JSONClass
1845− -> [(Name, Name)]
1846− -> Name
1847− -> Q ([Type], TyVarMap)
1848−reifyConTys jc tpjs conName = do
1849− info <- reify conName
1850− (ctxt, uncTy) <- case info of
1851− DataConI _ ty _
1852−#if !(MIN_VERSION_template_haskell(2,11,0))
1853− _
1854−#endif
1855− -> fmap uncurryTy (expandSyn ty)
1856− _ -> error "Must be a data constructor"
1857− let (argTys, [resTy]) = NE.splitAt (NE.length uncTy - 1) uncTy
1858− unapResTy = unapplyTy resTy
1859− -- If one of the last type variables is refined to a particular type
1860− -- (i.e., not truly polymorphic), we mark it with Nothing and filter
1861− -- it out later, since we only apply encoding/decoding functions to
1862− -- arguments of a type that it (1) one of the last type variables,
1863− -- and (2) of a truly polymorphic type.
1864− jArity = arityInt jc
1865− mbTvNames = map varTToNameMaybe $
1866− NE.drop (NE.length unapResTy - jArity) unapResTy
1867− -- We use M.fromList to ensure that if there are any duplicate type
1868− -- variables (as can happen in a GADT), the rightmost type variable gets
1869− -- associated with the show function.
1870− --
1871− -- See Note [Matching functions with GADT type variables]
1872− tvMap = M.fromList
1873− . catMaybes -- Drop refined types
1874− $ zipWith (\mbTvName tpj ->
1875− fmap (\tvName -> (tvName, tpj)) mbTvName)
1876− mbTvNames tpjs
1877− if (any (`predMentionsName` M.keys tvMap) ctxt
1559+ if (any (`predMentionsName` M.keys tvMap) ctxt
1878− || M.size tvMap < jArity)
1560+ || M.size tvMap < arityInt jc)
1879− && not (allowExQuant jc)
1561+ && not (allowExQuant jc)
1880− then existentialContextError conName
1562+ then existentialContextError conName
1881− else return (argTys, tvMap)
1563+ else q
1882
1564
1883{-
1565{-
1884Note [Matching functions with GADT type variables]
1566Note [Matching functions with GADT type variables]
@@ -1918,69 +1600,22 @@ Both.
1918-- are the function arguments of types (b -> Value) and ([b] -> Value).
1600-- are the function arguments of types (b -> Value) and ([b] -> Value).
1919type TyVarMap = Map Name (Name, Name)
1601type TyVarMap = Map Name (Name, Name)
1920
1602
1921−-- | If a VarT is missing an explicit kind signature, steal it from a TyVarBndr.
1922−stealKindForType :: TyVarBndr -> Type -> Type
1923−stealKindForType tvb t@VarT{} = SigT t (tvbKind tvb)
1924−stealKindForType _ t = t
1925−
1926−-- | Extracts the kind from a type variable binder.
1927−tvbKind :: TyVarBndr -> Kind
1928−#if MIN_VERSION_template_haskell(2,8,0)
1929−tvbKind (PlainTV _ ) = StarT
1930−#else
1931−tvbKind (PlainTV _ ) = StarK
1932−#endif
1933−tvbKind (KindedTV _ k) = k
1934−
1935−tvbToType :: TyVarBndr -> Type
1936−tvbToType (PlainTV n) = VarT n
1937−tvbToType (KindedTV n k) = SigT (VarT n) k
1938−
1939-- | Returns True if a Type has kind *.
1603-- | Returns True if a Type has kind *.
1940hasKindStar :: Type -> Bool
1604hasKindStar :: Type -> Bool
1941hasKindStar VarT{} = True
1605hasKindStar VarT{} = True
1942−#if MIN_VERSION_template_haskell(2,8,0)
1943hasKindStar (SigT _ StarT) = True
1606hasKindStar (SigT _ StarT) = True
1944−#else
1945−hasKindStar (SigT _ StarK) = True
1946−#endif
1947hasKindStar _ = False
1607hasKindStar _ = False
1948
1608
1949-- Returns True is a kind is equal to *, or if it is a kind variable.
1609-- Returns True is a kind is equal to *, or if it is a kind variable.
1950isStarOrVar :: Kind -> Bool
1610isStarOrVar :: Kind -> Bool
1951−#if MIN_VERSION_template_haskell(2,8,0)
1952isStarOrVar StarT = True
1611isStarOrVar StarT = True
1953isStarOrVar VarT{} = True
1612isStarOrVar VarT{} = True
1954−#else
1955−isStarOrVar StarK = True
1956−#endif
1957isStarOrVar _ = False
1613isStarOrVar _ = False
1958
1614
1959-- Generate a list of fresh names with a common prefix, and numbered suffixes.
1615-- Generate a list of fresh names with a common prefix, and numbered suffixes.
1960newNameList :: String -> Int -> Q [Name]
1616newNameList :: String -> Int -> Q [Name]
1961newNameList prefix len = mapM newName [prefix ++ show n | n <- [1..len]]
1617newNameList prefix len = mapM newName [prefix ++ show n | n <- [1..len]]
1962
1618
1963−-- Gets all of the type/kind variable names mentioned somewhere in a Type.
1964−tyVarNamesOfType :: Type -> [Name]
1965−tyVarNamesOfType = go
1966− where
1967− go :: Type -> [Name]
1968− go (AppT t1 t2) = go t1 ++ go t2
1969− go (SigT t _k) = go t
1970−#if MIN_VERSION_template_haskell(2,8,0)
1971− ++ go _k
1972−#endif
1973− go (VarT n) = [n]
1974− go _ = []
1975−
1976−-- | Gets all of the type/kind variable names mentioned somewhere in a Kind.
1977−tyVarNamesOfKind :: Kind -> [Name]
1978−#if MIN_VERSION_template_haskell(2,8,0)
1979−tyVarNamesOfKind = tyVarNamesOfType
1980−#else
1981−tyVarNamesOfKind _ = [] -- There are no kind variables
1982−#endif
1983−
1984-- | @hasKindVarChain n kind@ Checks if @kind@ is of the form
1619-- | @hasKindVarChain n kind@ Checks if @kind@ is of the form
1985-- k_0 -> k_1 -> ... -> k_(n-1), where k0, k1, ..., and k_(n-1) can be * or
1620-- k_0 -> k_1 -> ... -> k_(n-1), where k0, k1, ..., and k_(n-1) can be * or
1986-- kind variables.
1621-- kind variables.
@@ -1988,7 +1623,7 @@ hasKindVarChain :: Int -> Type -> Maybe [Name]
1988hasKindVarChain kindArrows t =
1623hasKindVarChain kindArrows t =
1989 let uk = uncurryKind (tyKind t)
1624 let uk = uncurryKind (tyKind t)
1990 in if (NE.length uk - 1 == kindArrows) && F.all isStarOrVar uk
1625 in if (NE.length uk - 1 == kindArrows) && F.all isStarOrVar uk
1991− then Just (concatMap tyVarNamesOfKind uk)
1626+ then Just (concatMap freeVariables uk)
1992 else Nothing
1627 else Nothing
1993
1628
1994-- | If a Type is a SigT, returns its kind signature. Otherwise, return *.
1629-- | If a Type is a SigT, returns its kind signature. Otherwise, return *.
@@ -2008,17 +1643,6 @@ varTToNameMaybe _ = Nothing
2008varTToName :: Type -> Name
1643varTToName :: Type -> Name
2009varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe
1644varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe
2010
1645
2011−-- | Extracts the name from a constructor.
2012−getConName :: Con -> Name
2013−getConName (NormalC name _) = name
2014−getConName (RecC name _) = name
2015−getConName (InfixC _ name _) = name
2016−getConName (ForallC _ _ con) = getConName con
2017−#if MIN_VERSION_template_haskell(2,11,0)
2018−getConName (GadtC names _ _) = head names
2019−getConName (RecGadtC names _ _) = head names
2020−#endif
2021−
2022interleave :: [a] -> [a] -> [a]
1646interleave :: [a] -> [a] -> [a]
2023interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
1647interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
2024interleave _ _ = []
1648interleave _ _ = []
@@ -2043,9 +1667,7 @@ isTyFamily (ConT n) = do
2043#else
1667#else
2044 FamilyI (FamilyD TypeFam _ _ _) _ -> True
1668 FamilyI (FamilyD TypeFam _ _ _) _ -> True
2045#endif
1669#endif
2046−#if MIN_VERSION_template_haskell(2,9,0)
2047 FamilyI ClosedTypeFamilyD{} _ -> True
1670 FamilyI ClosedTypeFamilyD{} _ -> True
2048−#endif
2049 _ -> False
1671 _ -> False
2050isTyFamily _ = return False
1672isTyFamily _ = return False
2051
1673
@@ -2073,9 +1695,7 @@ mentionsName = go
2073 go :: Type -> [Name] -> Bool
1695 go :: Type -> [Name] -> Bool
2074 go (AppT t1 t2) names = go t1 names || go t2 names
1696 go (AppT t1 t2) names = go t1 names || go t2 names
2075 go (SigT t _k) names = go t names
1697 go (SigT t _k) names = go t names
2076−#if MIN_VERSION_template_haskell(2,8,0)
2077 || go _k names
1698 || go _k names
2078−#endif
2079 go (VarT n) names = n `elem` names
1699 go (VarT n) names = n `elem` names
2080 go _ _ = False
1700 go _ _ = False
2081
1701
@@ -2131,37 +1751,28 @@ uncurryTy t = ([], t :| [])
2131
1751
2132-- | Like uncurryType, except on a kind level.
1752-- | Like uncurryType, except on a kind level.
2133uncurryKind :: Kind -> NonEmpty Kind
1753uncurryKind :: Kind -> NonEmpty Kind
2134−#if MIN_VERSION_template_haskell(2,8,0)
2135uncurryKind = snd . uncurryTy
1754uncurryKind = snd . uncurryTy
2136−#else
2137−uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
2138−uncurryKind k = k :| []
2139−#endif
2140
1755
2141createKindChain :: Int -> Kind
1756createKindChain :: Int -> Kind
2142createKindChain = go starK
1757createKindChain = go starK
2143 where
1758 where
2144 go :: Kind -> Int -> Kind
1759 go :: Kind -> Int -> Kind
2145− go k !0 = k
1760+ go k 0 = k
2146−#if MIN_VERSION_template_haskell(2,8,0)
2147 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1761 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
2148−#else
2149− go k !n = go (ArrowK StarK k) (n - 1)
2150−#endif
2151
1762
2152-- | Makes a string literal expression from a constructor's name.
1763-- | Makes a string literal expression from a constructor's name.
2153−conNameExp :: Options -> Con -> Q Exp
1764+conNameExp :: Options -> ConstructorInfo -> Q Exp
2154conNameExp opts = litE
1765conNameExp opts = litE
2155 . stringL
1766 . stringL
2156 . constructorTagModifier opts
1767 . constructorTagModifier opts
2157 . nameBase
1768 . nameBase
2158− . getConName
1769+ . constructorName
2159
1770
2160−-- | Creates a string literal expression from a record field label.
1771+-- | Extracts a record field label.
2161−fieldLabelExp :: Options -- ^ Encoding options
1772+fieldLabel :: Options -- ^ Encoding options
2162− -> Name
1773+ -> Name
2163− -> Q Exp
1774+ -> String
2164−fieldLabelExp opts = litE . stringL . fieldLabelModifier opts . nameBase
1775+fieldLabel opts = fieldLabelModifier opts . nameBase
2165
1776
2166-- | The name of the outermost 'Value' constructor.
1777-- | The name of the outermost 'Value' constructor.
2167valueConName :: Value -> String
1778valueConName :: Value -> String
@@ -2200,76 +1811,14 @@ canEtaReduce remaining dropped =
2200-- Expanding type synonyms
1811-- Expanding type synonyms
2201-------------------------------------------------------------------------------
1812-------------------------------------------------------------------------------
2202
1813
2203−-- | Expands all type synonyms in a type. Written by Dan Rosén in the
1814+applySubstitutionKind :: Map Name Kind -> Type -> Type
2204−-- @genifunctors@ package (licensed under BSD3).
1815+applySubstitutionKind = applySubstitution
2205−expandSyn :: Type -> Q Type
2206−expandSyn (ForallT tvs ctx t) = ForallT tvs ctx <$> expandSyn t
2207−expandSyn t@AppT{} = expandSynApp t []
2208−expandSyn t@ConT{} = expandSynApp t []
2209−expandSyn (SigT t k) = do t' <- expandSyn t
2210− k' <- expandSynKind k
2211− return (SigT t' k')
2212−expandSyn t = return t
2213−
2214−expandSynKind :: Kind -> Q Kind
2215−#if MIN_VERSION_template_haskell(2,8,0)
2216−expandSynKind = expandSyn
2217−#else
2218−expandSynKind = return -- There are no kind synonyms to deal with
2219−#endif
2220−
2221−expandSynApp :: Type -> [Type] -> Q Type
2222−expandSynApp (AppT t1 t2) ts = do
2223− t2' <- expandSyn t2
2224− expandSynApp t1 (t2':ts)
2225−expandSynApp (ConT n) ts | nameBase n == "[]" = return $ foldl' AppT ListT ts
2226−expandSynApp t@(ConT n) ts = do
2227− info <- reify n
2228− case info of
2229− TyConI (TySynD _ tvs rhs) ->
2230− let (ts', ts'') = splitAt (length tvs) ts
2231− subs = mkSubst tvs ts'
2232− rhs' = substType subs rhs
2233− in expandSynApp rhs' ts''
2234− _ -> return $ foldl' AppT t ts
2235−expandSynApp t ts = do
2236− t' <- expandSyn t
2237− return $ foldl' AppT t' ts
2238−
2239−type TypeSubst = Map Name Type
2240−type KindSubst = Map Name Kind
2241−
2242−mkSubst :: [TyVarBndr] -> [Type] -> TypeSubst
2243−mkSubst vs ts =
2244− let vs' = map un vs
2245− un (PlainTV v) = v
2246− un (KindedTV v _) = v
2247− in M.fromList $ zip vs' ts
2248−
2249−substType :: TypeSubst -> Type -> Type
2250−substType subs (ForallT v c t) = ForallT v c $ substType subs t
2251−substType subs t@(VarT n) = M.findWithDefault t n subs
2252−substType subs (AppT t1 t2) = AppT (substType subs t1) (substType subs t2)
2253−substType subs (SigT t k) = SigT (substType subs t)
2254−#if MIN_VERSION_template_haskell(2,8,0)
2255− (substType subs k)
2256−#else
2257− k
2258−#endif
2259−substType _ t = t
2260−
2261−substKind :: KindSubst -> Type -> Type
2262−#if MIN_VERSION_template_haskell(2,8,0)
2263−substKind = substType
2264−#else
2265−substKind _ t = t -- There are no kind variables!
2266−#endif
2267
1816
2268substNameWithKind :: Name -> Kind -> Type -> Type
1817substNameWithKind :: Name -> Kind -> Type -> Type
2269−substNameWithKind n k = substKind (M.singleton n k)
1818+substNameWithKind n k = applySubstitutionKind (M.singleton n k)
2270
1819
2271substNamesWithKindStar :: [Name] -> Type -> Type
1820substNamesWithKindStar :: [Name] -> Type -> Type
2272−substNamesWithKindStar ns t = foldr' (flip substNameWithKind starK) t ns
1821+substNamesWithKindStar ns t = foldr' (`substNameWithKind` starK) t ns
2273
1822
2274-------------------------------------------------------------------------------
1823-------------------------------------------------------------------------------
2275-- Error messages
1824-- Error messages
@@ -2352,6 +1901,13 @@ data Direction = To | From
2352-- | A representation of which typeclass method is being spliced in.
1901-- | A representation of which typeclass method is being spliced in.
2353data JSONFun = ToJSON | ToEncoding | ParseJSON
1902data JSONFun = ToJSON | ToEncoding | ParseJSON
2354
1903
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+
2355-- | A representation of which typeclass is being derived.
1911-- | A representation of which typeclass is being derived.
2356data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
1912data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
2357
1913
@@ -2418,13 +1974,10 @@ data StarKindStatus = NotKindStar
2418
1974
2419-- | Does a Type have kind * or k (for some kind variable k)?
1975-- | Does a Type have kind * or k (for some kind variable k)?
2420canRealizeKindStar :: Type -> StarKindStatus
1976canRealizeKindStar :: Type -> StarKindStatus
2421−canRealizeKindStar t
1977+canRealizeKindStar t = case t of
2422− | hasKindStar t = KindStar
1978+ _ | hasKindStar t -> KindStar
2423− | otherwise = case t of
2424−#if MIN_VERSION_template_haskell(2,8,0)
2425− SigT _ (VarT k) -> IsKindVar k
1979+ SigT _ (VarT k) -> IsKindVar k
2426−#endif
2427− _ -> NotKindStar
1980+ _ -> NotKindStar
2428
1981
2429-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
1982-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
2430-- Otherwise, returns 'Nothing'.
1983-- Otherwise, returns 'Nothing'.
Data/Aeson/Text.hs+5−4
@@ -1,4 +1,5 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3-- |
4-- |
4-- Module: Data.Aeson.Text
5-- Module: Data.Aeson.Text
@@ -14,18 +15,18 @@
14--
15--
15-- You can use the conversions to 'Builder's when embedding JSON messages as
16-- You can use the conversions to 'Builder's when embedding JSON messages as
16-- parts of a protocol.
17-- parts of a protocol.
18+
17module Data.Aeson.Text
19module Data.Aeson.Text
18 (
20 (
19 encodeToLazyText
21 encodeToLazyText
20 , encodeToTextBuilder
22 , encodeToTextBuilder
21 ) where
23 ) where
22
24
23−import Prelude ()
24import Prelude.Compat
25import Prelude.Compat
25
26
26import Data.Aeson.Types (Value(..), ToJSON(..))
27import Data.Aeson.Types (Value(..), ToJSON(..))
27import Data.Aeson.Encoding (encodingToLazyByteString)
28import Data.Aeson.Encoding (encodingToLazyByteString)
28−import Data.Monoid ((<>))
29+import Data.Semigroup ((<>))
29import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
30import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
30import Data.Text.Lazy.Builder
31import Data.Text.Lazy.Builder
31import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
32import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
@@ -46,8 +47,8 @@ encodeToLazyText = LT.decodeUtf8 . encodingToLazyByteString . toEncoding
46-- embedded efficiently in a text-based protocol.
47-- embedded efficiently in a text-based protocol.
47--
48--
48-- If you are going to immediately encode straight to a
49-- If you are going to immediately encode straight to a
49−-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
50+-- 'L.ByteString', it is more efficient to use 'encode' (lazy ByteString)
50−-- instead.
51+-- or @'fromEncoding' . 'toEncoding'@ (ByteString.Builder) instead.
51--
52--
52-- /Note:/ Uses 'toJSON'
53-- /Note:/ Uses 'toJSON'
53encodeToTextBuilder :: ToJSON a => a -> Builder
54encodeToTextBuilder :: ToJSON a => a -> Builder
Data/Aeson/Types.hs+49−5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1-- |
2-- |
2-- Module: Data.Aeson.Types
3-- Module: Data.Aeson.Types
3-- Copyright: (c) 2011-2016 Bryan O'Sullivan
4-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -25,6 +26,7 @@ module Data.Aeson.Types
25 -- * Convenience types and functions
26 -- * Convenience types and functions
26 , DotNetTime(..)
27 , DotNetTime(..)
27 , typeMismatch
28 , typeMismatch
29+ , unexpected
28 -- * Type conversion
30 -- * Type conversion
29 , Parser
31 , Parser
30 , Result(..)
32 , Result(..)
@@ -36,6 +38,9 @@ module Data.Aeson.Types
36 , ToJSON(..)
38 , ToJSON(..)
37 , KeyValue(..)
39 , KeyValue(..)
38 , modifyFailure
40 , modifyFailure
41+ , prependFailure
42+ , parserThrowError
43+ , parserCatchError
39
44
40 -- ** Keys for maps
45 -- ** Keys for maps
41 , ToJSONKey(..)
46 , ToJSONKey(..)
@@ -48,6 +53,12 @@ module Data.Aeson.Types
48 , coerceFromJSONKeyFunction
53 , coerceFromJSONKeyFunction
49 , mapFromJSONKeyFunction
54 , mapFromJSONKeyFunction
50
55
56+ -- *** Generic keys
57+ , GToJSONKey()
58+ , genericToJSONKey
59+ , GFromJSONKey()
60+ , genericFromJSONKey
61+
51 -- ** Liftings to unary and binary type constructors
62 -- ** Liftings to unary and binary type constructors
52 , FromJSON1(..)
63 , FromJSON1(..)
53 , parseJSON1
64 , parseJSON1
@@ -63,8 +74,8 @@ module Data.Aeson.Types
63 -- ** Generic JSON classes
74 -- ** Generic JSON classes
64 , GFromJSON(..)
75 , GFromJSON(..)
65 , FromArgs(..)
76 , FromArgs(..)
66− , GToJSON(..)
77+ , GToJSON
67− , GToEncoding(..)
78+ , GToEncoding
68 , ToArgs(..)
79 , ToArgs(..)
69 , Zero
80 , Zero
70 , One
81 , One
@@ -79,9 +90,9 @@ module Data.Aeson.Types
79 , withObject
90 , withObject
80 , withText
91 , withText
81 , withArray
92 , withArray
82− , withNumber
83 , withScientific
93 , withScientific
84 , withBool
94 , withBool
95+ , withEmbeddedJSON
85
96
86 , pairs
97 , pairs
87 , foldable
98 , foldable
@@ -90,21 +101,51 @@ module Data.Aeson.Types
90 , (.:!)
101 , (.:!)
91 , (.!=)
102 , (.!=)
92 , object
103 , object
104+ , parseField
105+ , parseFieldMaybe
106+ , parseFieldMaybe'
107+ , explicitParseField
108+ , explicitParseFieldMaybe
109+ , explicitParseFieldMaybe'
93
110
94 , listEncoding
111 , listEncoding
95 , listValue
112 , listValue
96 , listParser
113 , listParser
97
114
98 -- * Generic and TH encoding configuration
115 -- * Generic and TH encoding configuration
99− , Options(..)
116+ , Options
117+
118+ -- ** Options fields
119+ -- $optionsFields
120+ , fieldLabelModifier
121+ , constructorTagModifier
122+ , allNullaryToStringTag
123+ , omitNothingFields
124+ , sumEncoding
125+ , unwrapUnaryRecords
126+ , tagSingleConstructors
127+ , rejectUnknownFields
128+
129+ -- ** Options utilities
100 , SumEncoding(..)
130 , SumEncoding(..)
101 , camelTo
131 , camelTo
102 , camelTo2
132 , camelTo2
103 , defaultOptions
133 , defaultOptions
104 , defaultTaggedObject
134 , 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
105 ) where
147 ) where
106
148
107−import Prelude ()
108import Prelude.Compat
149import Prelude.Compat
109
150
110import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
151import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
@@ -116,3 +157,6 @@ import Data.Foldable (toList)
116foldable :: (Foldable t, ToJSON a) => t a -> Encoding
157foldable :: (Foldable t, ToJSON a) => t a -> Encoding
117foldable = toEncoding . toList
158foldable = toEncoding . toList
118{-# INLINE foldable #-}
159{-# INLINE foldable #-}
160+
161+-- $optionsFields
162+-- The functions here are in fact record fields of the 'Options' type.
Data/Aeson/Types/Class.hs+26−7
@@ -1,10 +1,11 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE NoImplicitPrelude #-}
6{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeSynonymInstances #-}
8
9
9-- |
10-- |
10-- Module: Data.Aeson.Types.Class
11-- Module: Data.Aeson.Types.Class
@@ -36,8 +37,8 @@ module Data.Aeson.Types.Class
36 -- * Generic JSON classes
37 -- * Generic JSON classes
37 , GFromJSON(..)
38 , GFromJSON(..)
38 , FromArgs(..)
39 , FromArgs(..)
39− , GToJSON(..)
40+ , GToJSON
40− , GToEncoding(..)
41+ , GToEncoding
41 , ToArgs(..)
42 , ToArgs(..)
42 , Zero
43 , Zero
43 , One
44 , One
@@ -57,6 +58,11 @@ module Data.Aeson.Types.Class
57 , fromJSONKeyCoerce
58 , fromJSONKeyCoerce
58 , coerceFromJSONKeyFunction
59 , coerceFromJSONKeyFunction
59 , mapFromJSONKeyFunction
60 , mapFromJSONKeyFunction
61+ -- ** Generic keys
62+ , GToJSONKey()
63+ , genericToJSONKey
64+ , GFromJSONKey()
65+ , genericFromJSONKey
60 -- * Object key-value pairs
66 -- * Object key-value pairs
61 , KeyValue(..)
67 , KeyValue(..)
62
68
@@ -69,22 +75,35 @@ module Data.Aeson.Types.Class
69 , withObject
75 , withObject
70 , withText
76 , withText
71 , withArray
77 , withArray
72− , withNumber
73 , withScientific
78 , withScientific
74 , withBool
79 , withBool
80+ , withEmbeddedJSON
75
81
76 -- * Functions
82 -- * Functions
77 , fromJSON
83 , fromJSON
78 , ifromJSON
84 , ifromJSON
85+ , typeMismatch
86+ , unexpected
87+ , parseField
88+ , parseFieldMaybe
89+ , parseFieldMaybe'
90+ , explicitParseField
91+ , explicitParseFieldMaybe
92+ , explicitParseFieldMaybe'
93+ -- ** Operators
79 , (.:)
94 , (.:)
80 , (.:?)
95 , (.:?)
81 , (.:!)
96 , (.:!)
82 , (.!=)
97 , (.!=)
83− , typeMismatch
84 ) where
98 ) where
85
99
86−import Prelude ()
87
100
88import Data.Aeson.Types.FromJSON
101import Data.Aeson.Types.FromJSON
89import Data.Aeson.Types.Generic (One, Zero)
102import Data.Aeson.Types.Generic (One, Zero)
90−import Data.Aeson.Types.ToJSON
103+import Data.Aeson.Types.ToJSON hiding (GToJSON)
104+import qualified Data.Aeson.Types.ToJSON as ToJSON
105+import Data.Aeson.Types.Internal (Value)
106+import Data.Aeson.Encoding (Encoding)
107+
108+type GToJSON = ToJSON.GToJSON Value
109+type GToEncoding = ToJSON.GToJSON Encoding
Data/Aeson/Types/FromJSON.hs+1079−464
@@ -5,19 +5,17 @@
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NamedFieldPuns #-}
8+{-# LANGUAGE NoImplicitPrelude #-}
9{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RecordWildCards #-}
11{-# LANGUAGE RecordWildCards #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE TypeOperators #-}
13{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE TypeSynonymInstances #-}
14+{-# LANGUAGE TupleSections #-}
14{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE ViewPatterns #-}
16{-# LANGUAGE ViewPatterns #-}
16
17
17−#if __GLASGOW_HASKELL__ >= 706
18+#include "incoherent-compat.h"
18−{-# LANGUAGE PolyKinds #-}
19−#endif
20−
21#include "overlapping-compat.h"
19#include "overlapping-compat.h"
22
20
23-- TODO: Drop this when we remove support for Data.Attoparsec.Number
21-- TODO: Drop this when we remove support for Data.Attoparsec.Number
@@ -44,6 +42,9 @@ module Data.Aeson.Types.FromJSON
44 , coerceFromJSONKeyFunction
42 , coerceFromJSONKeyFunction
45 , mapFromJSONKeyFunction
43 , mapFromJSONKeyFunction
46
44
45+ , GFromJSONKey()
46+ , genericFromJSONKey
47+
47 -- * List functions
48 -- * List functions
48 , listParser
49 , listParser
49
50
@@ -51,34 +52,42 @@ module Data.Aeson.Types.FromJSON
51 , withObject
52 , withObject
52 , withText
53 , withText
53 , withArray
54 , withArray
54− , withNumber
55 , withScientific
55 , withScientific
56 , withBool
56 , withBool
57+ , withEmbeddedJSON
57
58
58 -- * Functions
59 -- * Functions
59 , fromJSON
60 , fromJSON
60 , ifromJSON
61 , ifromJSON
62+ , typeMismatch
63+ , unexpected
64+ , parseField
65+ , parseFieldMaybe
66+ , parseFieldMaybe'
67+ , explicitParseField
68+ , explicitParseFieldMaybe
69+ , explicitParseFieldMaybe'
70+ , parseIndexedJSON
71+ -- ** Operators
61 , (.:)
72 , (.:)
62 , (.:?)
73 , (.:?)
63 , (.:!)
74 , (.:!)
64 , (.!=)
75 , (.!=)
65− , typeMismatch
66
76
67 -- * Internal
77 -- * Internal
68 , parseOptionalFieldWith
78 , parseOptionalFieldWith
69 ) where
79 ) where
70
80
71−import Prelude ()
72import Prelude.Compat
81import Prelude.Compat
73
82
74−import Control.Applicative ((<|>), Const(..))
83+import Control.Applicative ((<|>), Const(..), liftA2)
75−import Control.Monad ((<=<), zipWithM)
84+import Control.Monad (zipWithM)
76import Data.Aeson.Internal.Functions (mapKey)
85import Data.Aeson.Internal.Functions (mapKey)
86+import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
77import Data.Aeson.Types.Generic
87import Data.Aeson.Types.Generic
78import Data.Aeson.Types.Internal
88import Data.Aeson.Types.Internal
79−import Data.Attoparsec.Number (Number(..))
80import Data.Bits (unsafeShiftR)
89import Data.Bits (unsafeShiftR)
81−import Data.Fixed (Fixed, HasResolution)
90+import Data.Fixed (Fixed, HasResolution (resolution), Nano)
82import Data.Functor.Compose (Compose(..))
91import Data.Functor.Compose (Compose(..))
83import Data.Functor.Identity (Identity(..))
92import Data.Functor.Identity (Identity(..))
84import Data.Functor.Product (Product(..))
93import Data.Functor.Product (Product(..))
@@ -87,57 +96,67 @@ import Data.Hashable (Hashable(..))
87import Data.Int (Int16, Int32, Int64, Int8)
96import Data.Int (Int16, Int32, Int64, Int8)
88import Data.List.NonEmpty (NonEmpty(..))
97import Data.List.NonEmpty (NonEmpty(..))
89import Data.Maybe (fromMaybe)
98import Data.Maybe (fromMaybe)
90−import Data.Monoid ((<>), Dual(..), First (..), Last (..))
99+import Data.Semigroup ((<>))
91import Data.Proxy (Proxy(..))
100import Data.Proxy (Proxy(..))
92import Data.Ratio ((%), Ratio)
101import Data.Ratio ((%), Ratio)
93−import Data.Scientific (Scientific)
102+import Data.Scientific (Scientific, base10Exponent)
94import Data.Tagged (Tagged(..))
103import Data.Tagged (Tagged(..))
95import Data.Text (Text, pack, unpack)
104import Data.Text (Text, pack, unpack)
96−import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
105+import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
97−import Data.Time.Format (parseTime)
106+import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
107+import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
108+import Data.Time.Clock.System.Compat (SystemTime (..))
98−import Data.Time.Locale.Compat (defaultTimeLocale)
109+import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
99import Data.Traversable as Tr (sequence)
110import Data.Traversable as Tr (sequence)
100import Data.Vector (Vector)
111import Data.Vector (Vector)
101import Data.Version (Version, parseVersion)
112import Data.Version (Version, parseVersion)
113+import Data.Void (Void)
102import Data.Word (Word16, Word32, Word64, Word8)
114import Data.Word (Word16, Word32, Word64, Word8)
103import Foreign.Storable (Storable)
115import Foreign.Storable (Storable)
116+import Foreign.C.Types (CTime (..))
104import GHC.Generics
117import GHC.Generics
105import Numeric.Natural (Natural)
118import Numeric.Natural (Natural)
106import Text.ParserCombinators.ReadP (readP_to_S)
119import Text.ParserCombinators.ReadP (readP_to_S)
120+import Unsafe.Coerce (unsafeCoerce)
107import qualified Data.Aeson.Parser.Time as Time
121import qualified Data.Aeson.Parser.Time as Time
108import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
122import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123+import qualified Data.ByteString.Lazy as L
109import qualified Data.DList as DList
124import qualified Data.DList as DList
110import qualified Data.HashMap.Strict as H
125import qualified Data.HashMap.Strict as H
111import qualified Data.HashSet as HashSet
126import qualified Data.HashSet as HashSet
112import qualified Data.IntMap as IntMap
127import qualified Data.IntMap as IntMap
113import qualified Data.IntSet as IntSet
128import qualified Data.IntSet as IntSet
114import qualified Data.Map as M
129import qualified Data.Map as M
130+import qualified Data.Monoid as Monoid
115import qualified Data.Scientific as Scientific
131import qualified Data.Scientific as Scientific
132+import qualified Data.Semigroup as Semigroup
116import qualified Data.Sequence as Seq
133import qualified Data.Sequence as Seq
117import qualified Data.Set as Set
134import qualified Data.Set as Set
118import qualified Data.Text as T
135import qualified Data.Text as T
119import qualified Data.Text.Encoding as T
136import qualified Data.Text.Encoding as T
120import qualified Data.Text.Lazy as LT
137import qualified Data.Text.Lazy as LT
121import qualified Data.Tree as Tree
138import qualified Data.Tree as Tree
139+import qualified Data.UUID.Types as UUID
122import qualified Data.Vector as V
140import qualified Data.Vector as V
123import qualified Data.Vector.Generic as VG
141import qualified Data.Vector.Generic as VG
124import qualified Data.Vector.Primitive as VP
142import qualified Data.Vector.Primitive as VP
125import qualified Data.Vector.Storable as VS
143import qualified Data.Vector.Storable as VS
126import qualified Data.Vector.Unboxed as VU
144import qualified Data.Vector.Unboxed as VU
127−import Unsafe.Coerce (unsafeCoerce)
128
145
129−#ifndef HAS_COERCIBLE
146+import qualified GHC.Exts as Exts
130−#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
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
154+#endif
155+import qualified Data.Primitive.PrimArray as PM
131#endif
156#endif
132
157
133−#if HAS_COERCIBLE
158+
134import Data.Coerce (Coercible, coerce)
159import Data.Coerce (Coercible, coerce)
135−coerce' :: Coercible a b => a -> b
136−coerce' = coerce
137−#else
138−coerce' :: a -> b
139−coerce' = unsafeCoerce
140−#endif
141
160
142parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
161parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
143parseIndexedJSON p idx value = p value <?> Index idx
162parseIndexedJSON p idx value = p value <?> Index idx
@@ -146,7 +165,7 @@ parseIndexedJSON p idx value = p value <?> Index idx
146parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
165parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
147parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
166parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
148 where
167 where
149− p = withArray "(k,v)" $ \ab ->
168+ p = withArray "(k, v)" $ \ab ->
150 let n = V.length ab
169 let n = V.length ab
151 in if n == 2
170 in if n == 2
152 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
171 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
@@ -158,43 +177,34 @@ parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
158parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
177parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
159parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
178parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
160
179
161−scientificToNumber :: Scientific -> Number
162−scientificToNumber s
163− | e < 0 = D $ Scientific.toRealFloat s
164− | otherwise = I $ c * 10 ^ e
165− where
166− e = Scientific.base10Exponent s
167− c = Scientific.coefficient s
168−{-# INLINE scientificToNumber #-}
169−
170parseRealFloat :: RealFloat a => String -> Value -> Parser a
180parseRealFloat :: RealFloat a => String -> Value -> Parser a
171−parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
181+parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
172−parseRealFloat _ Null = pure (0/0)
182+parseRealFloat _ Null = pure (0/0)
173−parseRealFloat expected v = typeMismatch expected v
183+parseRealFloat name v = prependContext name (unexpected v)
174{-# INLINE parseRealFloat #-}
184{-# INLINE parseRealFloat #-}
175
185
176−parseIntegralFromScientific :: forall a. Integral a => String -> Scientific -> Parser a
186+parseIntegralFromScientific :: forall a. Integral a => Scientific -> Parser a
177−parseIntegralFromScientific expected s =
187+parseIntegralFromScientific s =
178 case Scientific.floatingOrInteger s :: Either Double a of
188 case Scientific.floatingOrInteger s :: Either Double a of
179 Right x -> pure x
189 Right x -> pure x
180− Left _ -> fail $ "Floating number specified for " ++ expected ++ ": " ++ show s
190+ Left _ -> fail $ "unexpected floating number " ++ show s
181{-# INLINE parseIntegralFromScientific #-}
191{-# INLINE parseIntegralFromScientific #-}
182
192
183parseIntegral :: Integral a => String -> Value -> Parser a
193parseIntegral :: Integral a => String -> Value -> Parser a
184−parseIntegral expected =
194+parseIntegral name =
185− withScientific expected $ parseIntegralFromScientific expected
195+ prependContext name . withBoundedScientific' parseIntegralFromScientific
186{-# INLINE parseIntegral #-}
196{-# INLINE parseIntegral #-}
187
197
188−parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => String -> Scientific -> Parser a
198+parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => Scientific -> Parser a
189−parseBoundedIntegralFromScientific expected s = maybe
199+parseBoundedIntegralFromScientific s = maybe
190− (fail $ expected ++ " is either floating or will cause over or underflow: " ++ show s)
200+ (fail $ "value is either floating or will cause over or underflow " ++ show s)
191 pure
201 pure
192 (Scientific.toBoundedInteger s)
202 (Scientific.toBoundedInteger s)
193{-# INLINE parseBoundedIntegralFromScientific #-}
203{-# INLINE parseBoundedIntegralFromScientific #-}
194
204
195parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
205parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
196−parseBoundedIntegral expected =
206+parseBoundedIntegral name =
197− withScientific expected $ parseBoundedIntegralFromScientific expected
207+ prependContext name . withScientific' parseBoundedIntegralFromScientific
198{-# INLINE parseBoundedIntegral #-}
208{-# INLINE parseBoundedIntegral #-}
199
209
200parseScientificText :: Text -> Parser Scientific
210parseScientificText :: Text -> Parser Scientific
@@ -204,13 +214,20 @@ parseScientificText
204 . T.encodeUtf8
214 . T.encodeUtf8
205
215
206parseIntegralText :: Integral a => String -> Text -> Parser a
216parseIntegralText :: Integral a => String -> Text -> Parser a
207−parseIntegralText expected t =
217+parseIntegralText name t =
218+ prependContext name $
219+ parseScientificText t
220+ >>= rejectLargeExponent
208− parseScientificText t >>= parseIntegralFromScientific expected
221+ >>= parseIntegralFromScientific
222+ where
223+ rejectLargeExponent :: Scientific -> Parser Scientific
224+ rejectLargeExponent s = withBoundedScientific' pure (Number s)
209{-# INLINE parseIntegralText #-}
225{-# INLINE parseIntegralText #-}
210
226
211parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
227parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
212−parseBoundedIntegralText expected t =
228+parseBoundedIntegralText name t =
229+ prependContext name $
213− parseScientificText t >>= parseBoundedIntegralFromScientific expected
230+ parseScientificText t >>= parseBoundedIntegralFromScientific
214
231
215parseOptionalFieldWith :: (Value -> Parser (Maybe a))
232parseOptionalFieldWith :: (Value -> Parser (Maybe a))
216 -> Object -> Text -> Parser (Maybe a)
233 -> Object -> Text -> Parser (Maybe a)
@@ -270,14 +287,21 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
270--
287--
271-- The basic ways to signal a failed conversion are as follows:
288-- The basic ways to signal a failed conversion are as follows:
272--
289--
290+-- * 'fail' yields a custom error message: it is the recommended way of
291+-- reporting a failure;
292+--
273−-- * 'empty' and 'mzero' work, but are terse and uninformative
293+-- * 'Control.Applicative.empty' (or 'Control.Monad.mzero') is uninformative:
294+-- use it when the error is meant to be caught by some @('<|>')@;
274--
295--
275−-- * 'fail' yields a custom error message
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.
276--
300--
277−-- * 'typeMismatch' produces an informative message for cases when the
301+-- 'prependFailure' (or 'modifyFailure') add more information to a parser's
278−-- value encountered is not of the expected type
302+-- error messages.
279--
303--
280−-- An example type and instance:
304+-- An example type and instance using 'typeMismatch' and 'prependFailure':
281--
305--
282-- @
306-- @
283-- \-- Allow ourselves to write 'Text' literals.
307-- \-- Allow ourselves to write 'Text' literals.
@@ -285,15 +309,30 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
285--
309--
286-- data Coord = Coord { x :: Double, y :: Double }
310-- data Coord = Coord { x :: Double, y :: Double }
287--
311--
288−-- instance FromJSON Coord where
312+-- instance 'FromJSON' Coord where
289−-- parseJSON ('Object' v) = Coord '<$>'
313+-- 'parseJSON' ('Object' v) = Coord
290−-- v '.:' \"x\" '<*>'
314+-- '<$>' v '.:' \"x\"
291−-- v '.:' \"y\"
315+-- '<*>' v '.:' \"y\"
292--
316--
293−-- \-- We do not expect a non-'Object' value here.
317+-- \-- We do not expect a non-'Object' value here.
294−-- \-- We could use 'mzero' to fail, but 'typeMismatch'
318+-- \-- We could use 'Control.Applicative.empty' to fail, but 'typeMismatch'
295−-- \-- gives a much more informative error message.
319+-- \-- gives a much more informative error message.
320+-- 'parseJSON' invalid =
321+-- 'prependFailure' "parsing Coord failed, "
296−-- parseJSON invalid = 'typeMismatch' \"Coord\" invalid
322+-- ('typeMismatch' \"Object\" invalid)
323+-- @
324+--
325+-- For this common case of only being concerned with a single
326+-- type of JSON value, the functions 'withObject', 'withScientific', etc.
327+-- are provided. Their use is to be preferred when possible, since
328+-- they are more terse. Using 'withObject', we can rewrite the above instance
329+-- (assuming the same language extension and data type) as:
330+--
331+-- @
332+-- instance 'FromJSON' Coord where
333+-- 'parseJSON' = 'withObject' \"Coord\" $ \\v -> Coord
334+-- '<$>' v '.:' \"x\"
335+-- '<*>' v '.:' \"y\"
297-- @
336-- @
298--
337--
299-- Instead of manually writing your 'FromJSON' instance, there are two options
338-- Instead of manually writing your 'FromJSON' instance, there are two options
@@ -301,7 +340,7 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
301--
340--
302-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
341-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
303-- instance at compile time. The generated instance is optimized for your type
342-- instance at compile time. The generated instance is optimized for your type
304−-- so will probably be more efficient than the following two options:
343+-- so it will probably be more efficient than the following option.
305--
344--
306-- * The compiler can provide a default generic implementation for
345-- * The compiler can provide a default generic implementation for
307-- 'parseJSON'.
346-- 'parseJSON'.
@@ -319,18 +358,21 @@ genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
319--
358--
320-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
359-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
321--
360--
322−-- instance FromJSON Coord
361+-- instance 'FromJSON' Coord
323-- @
362-- @
324--
363--
325−-- If @DefaultSignatures@ doesn't give exactly the results you want,
364+-- The default implementation will be equivalent to
365+-- @parseJSON = 'genericParseJSON' 'defaultOptions'@; if you need different
326−-- you can customize the generic decoding with only a tiny amount of
366+-- options, you can customize the generic decoding by defining:
327−-- effort, using 'genericParseJSON' with your preferred 'Options':
328--
367--
329-- @
368-- @
369+-- customOptions = 'defaultOptions'
370+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
371+-- }
372+--
330−-- instance FromJSON Coord where
373+-- instance 'FromJSON' Coord where
331−-- parseJSON = 'genericParseJSON' 'defaultOptions'
374+-- 'parseJSON' = 'genericParseJSON' customOptions
332-- @
375-- @
333−
334class FromJSON a where
376class FromJSON a where
335 parseJSON :: Value -> Parser a
377 parseJSON :: Value -> Parser a
336
378
@@ -338,13 +380,11 @@ class FromJSON a where
338 parseJSON = genericParseJSON defaultOptions
380 parseJSON = genericParseJSON defaultOptions
339
381
340 parseJSONList :: Value -> Parser [a]
382 parseJSONList :: Value -> Parser [a]
341− parseJSONList (Array a)
383+ parseJSONList = withArray "[]" $ \a ->
342− = zipWithM (parseIndexedJSON parseJSON) [0..]
384+ zipWithM (parseIndexedJSON parseJSON) [0..]
343 . V.toList
385 . V.toList
344 $ a
386 $ a
345
387
346− parseJSONList v = typeMismatch "[a]" v
347−
348-------------------------------------------------------------------------------
388-------------------------------------------------------------------------------
349-- Classes and types for map keys
389-- Classes and types for map keys
350-------------------------------------------------------------------------------
390-------------------------------------------------------------------------------
@@ -356,6 +396,16 @@ class FromJSON a where
356-- > newtype SomeId = SomeId { getSomeId :: Text }
396-- > newtype SomeId = SomeId { getSomeId :: Text }
357-- > deriving (Eq,Ord,Hashable,FromJSONKey)
397-- > deriving (Eq,Ord,Hashable,FromJSONKey)
358--
398--
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+-- @
359class FromJSONKey a where
409class FromJSONKey a where
360 -- | Strategy for parsing the key of a map-like container.
410 -- | Strategy for parsing the key of a map-like container.
361 fromJSONKey :: FromJSONKeyFunction a
411 fromJSONKey :: FromJSONKeyFunction a
@@ -378,11 +428,7 @@ class FromJSONKey a where
378-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
428-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
379-- can be inspected, but cannot be constructed.
429-- can be inspected, but cannot be constructed.
380data CoerceText a where
430data CoerceText a where
381−#if HAS_COERCIBLE
382 CoerceText :: Coercible Text a => CoerceText a
431 CoerceText :: Coercible Text a => CoerceText a
383−#else
384− CoerceText :: CoerceText a
385−#endif
386
432
387-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
433-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
388-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
434-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
@@ -402,7 +448,7 @@ data FromJSONKeyFunction a
402
448
403-- | Only law abiding up to interpretation
449-- | Only law abiding up to interpretation
404instance Functor FromJSONKeyFunction where
450instance Functor FromJSONKeyFunction where
405− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
451+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
406 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
452 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
407 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
453 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
408 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
454 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
@@ -416,9 +462,7 @@ instance Functor FromJSONKeyFunction where
416--
462--
417-- On pre GHC 7.8 this is unconstrainted function.
463-- On pre GHC 7.8 this is unconstrainted function.
418fromJSONKeyCoerce ::
464fromJSONKeyCoerce ::
419−#if HAS_COERCIBLE
420 Coercible Text a =>
465 Coercible Text a =>
421−#endif
422 FromJSONKeyFunction a
466 FromJSONKeyFunction a
423fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
467fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
424
468
@@ -426,60 +470,91 @@ fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
426--
470--
427-- See note on 'fromJSONKeyCoerce'.
471-- See note on 'fromJSONKeyCoerce'.
428coerceFromJSONKeyFunction ::
472coerceFromJSONKeyFunction ::
429−#if HAS_COERCIBLE
430 Coercible a b =>
473 Coercible a b =>
431−#endif
432 FromJSONKeyFunction a -> FromJSONKeyFunction b
474 FromJSONKeyFunction a -> FromJSONKeyFunction b
433−#if HAS_COERCIBLE
434coerceFromJSONKeyFunction = coerce
475coerceFromJSONKeyFunction = coerce
435−#else
436−coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
437−coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
438−coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
439−coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
440−#endif
441
476
442{-# RULES
477{-# RULES
443 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
478 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
444 fmap id x = x
479 fmap id x = x
445 #-}
480 #-}
446−#if HAS_COERCIBLE
447{-# RULES
481{-# RULES
448 "FromJSONKeyCoerce: fmap coerce" forall x .
482 "FromJSONKeyCoerce: fmap coerce" forall x .
449 fmap coerce x = coerceFromJSONKeyFunction x
483 fmap coerce x = coerceFromJSONKeyFunction x
450 #-}
484 #-}
451−#endif
452
485
453-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
486-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
454mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
487mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
455mapFromJSONKeyFunction = fmap
488mapFromJSONKeyFunction = fmap
456
489
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+
457-------------------------------------------------------------------------------
519-------------------------------------------------------------------------------
458-- Functions needed for documentation
520-- Functions needed for documentation
459-------------------------------------------------------------------------------
521-------------------------------------------------------------------------------
460
522
461-- | Fail parsing due to a type mismatch, with a descriptive message.
523-- | Fail parsing due to a type mismatch, with a descriptive message.
462--
524--
463−-- Example usage:
525+-- The following wrappers should generally be prefered:
526+-- 'withObject', 'withArray', 'withText', 'withBool'.
464--
527--
465−-- @
528+-- ==== Error message example
466−-- instance FromJSON Coord where
529+--
467−-- parseJSON ('Object' v) = {- type matches, life is good -}
530+-- > typeMismatch "Object" (String "oops")
468−-- parseJSON wat = 'typeMismatch' \"Coord\" wat
531+-- > -- Error: "expected Object, but encountered String"
469−-- @
470−typeMismatch :: String -- ^ The name of the type you are trying to parse.
532+typeMismatch :: String -- ^ The name of the JSON type being parsed
533+ -- (@\"Object\"@, @\"Array\"@, @\"String\"@, @\"Number\"@,
534+ -- @\"Boolean\"@, or @\"Null\"@).
471 -> Value -- ^ The actual value encountered.
535 -> Value -- ^ The actual value encountered.
472 -> Parser a
536 -> Parser a
473typeMismatch expected actual =
537typeMismatch expected actual =
474− fail $ "expected " ++ expected ++ ", encountered " ++ name
538+ fail $ "expected " ++ expected ++ ", but encountered " ++ typeOf actual
475− where
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
476− name = case actual of
551+typeOf v = case v of
477− Object _ -> "Object"
552+ Object _ -> "Object"
478− Array _ -> "Array"
553+ Array _ -> "Array"
479− String _ -> "String"
554+ String _ -> "String"
480− Number _ -> "Number"
555+ Number _ -> "Number"
481− Bool _ -> "Boolean"
556+ Bool _ -> "Boolean"
482− Null -> "Null"
557+ Null -> "Null"
483
558
484-------------------------------------------------------------------------------
559-------------------------------------------------------------------------------
485-- Lifings of FromJSON and ToJSON to unary and binary type constructors
560-- Lifings of FromJSON and ToJSON to unary and binary type constructors
@@ -492,7 +567,7 @@ typeMismatch expected actual =
492--
567--
493-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
568-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
494-- instance at compile time. The generated instance is optimized for your type
569-- instance at compile time. The generated instance is optimized for your type
495−-- so will probably be more efficient than the following two options:
570+-- so it will probably be more efficient than the following option.
496--
571--
497-- * The compiler can provide a default generic implementation for
572-- * The compiler can provide a default generic implementation for
498-- 'liftParseJSON'.
573-- 'liftParseJSON'.
@@ -510,16 +585,20 @@ typeMismatch expected actual =
510--
585--
511-- data Pair a b = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
586-- data Pair a b = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
512--
587--
513−-- instance FromJSON a => FromJSON1 (Pair a)
588+-- instance 'FromJSON' a => 'FromJSON1' (Pair a)
514-- @
589-- @
515--
590--
516−-- If @DefaultSignatures@ doesn't give exactly the results you want,
591+-- If the default implementation doesn't give exactly the results you want,
517-- you can customize the generic decoding with only a tiny amount of
592-- you can customize the generic decoding with only a tiny amount of
518-- effort, using 'genericLiftParseJSON' with your preferred 'Options':
593-- effort, using 'genericLiftParseJSON' with your preferred 'Options':
519--
594--
520-- @
595-- @
596+-- customOptions = 'defaultOptions'
597+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
598+-- }
599+--
521−-- instance FromJSON a => FromJSON1 (Pair a) where
600+-- instance 'FromJSON' a => 'FromJSON1' (Pair a) where
522−-- liftParseJSON = 'genericLiftParseJSON' 'defaultOptions'
601+-- 'liftParseJSON' = 'genericLiftParseJSON' customOptions
523-- @
602-- @
524class FromJSON1 f where
603class FromJSON1 f where
525 liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
604 liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
@@ -556,9 +635,8 @@ class FromJSON2 f where
556 -> (Value -> Parser b)
635 -> (Value -> Parser b)
557 -> (Value -> Parser [b])
636 -> (Value -> Parser [b])
558 -> Value -> Parser [f a b]
637 -> Value -> Parser [f a b]
559− liftParseJSONList2 fa ga fb gb v = case v of
638+ liftParseJSONList2 fa ga fb gb = withArray "[]" $ \vals ->
560− Array vals -> fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
639+ fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals)
561− _ -> typeMismatch "[a]" v
562
640
563-- | Lift the standard 'parseJSON' function through the type constructor.
641-- | Lift the standard 'parseJSON' function through the type constructor.
564parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
642parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
@@ -572,7 +650,7 @@ parseJSON2 = liftParseJSON2 parseJSON parseJSONList parseJSON parseJSONList
572-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
650-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
573listParser :: (Value -> Parser a) -> Value -> Parser [a]
651listParser :: (Value -> Parser a) -> Value -> Parser [a]
574listParser f (Array xs) = fmap V.toList (V.mapM f xs)
652listParser f (Array xs) = fmap V.toList (V.mapM f xs)
575−listParser _ v = typeMismatch "[a]" v
653+listParser _ v = typeMismatch "Array" v
576{-# INLINE listParser #-}
654{-# INLINE listParser #-}
577
655
578-------------------------------------------------------------------------------
656-------------------------------------------------------------------------------
@@ -590,48 +668,139 @@ instance (FromJSON a) => FromJSON [a] where
590-- Functions
668-- Functions
591-------------------------------------------------------------------------------
669-------------------------------------------------------------------------------
592
670
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+
593−-- | @withObject expected f value@ applies @f@ to the 'Object' when @value@ is an @Object@
679+-- | @'withObject' name f value@ applies @f@ to the 'Object' when @value@
594−-- and fails using @'typeMismatch' expected@ otherwise.
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"
595withObject :: String -> (Object -> Parser a) -> Value -> Parser a
686withObject :: String -> (Object -> Parser a) -> Value -> Parser a
596−withObject _ f (Object obj) = f obj
687+withObject _ f (Object obj) = f obj
597−withObject expected _ v = typeMismatch expected v
688+withObject name _ v = prependContext name (typeMismatch "Object" v)
598{-# INLINE withObject #-}
689{-# INLINE withObject #-}
599
690
600−-- | @withText expected f value@ applies @f@ to the 'Text' when @value@ is a @String@
691+-- | @'withText' name f value@ applies @f@ to the 'Text' when @value@ is a
601−-- and fails using @'typeMismatch' expected@ otherwise.
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"
602withText :: String -> (Text -> Parser a) -> Value -> Parser a
698withText :: String -> (Text -> Parser a) -> Value -> Parser a
603−withText _ f (String txt) = f txt
699+withText _ f (String txt) = f txt
604−withText expected _ v = typeMismatch expected v
700+withText name _ v = prependContext name (typeMismatch "String" v)
605{-# INLINE withText #-}
701{-# INLINE withText #-}
606
702
607−-- | @withArray expected f value@ applies @f@ to the 'Array' when @value@ is an @Array@
703+-- | @'withArray' expected f value@ applies @f@ to the 'Array' when @value@ is
608−-- and fails using @'typeMismatch' expected@ otherwise.
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"
609withArray :: String -> (Array -> Parser a) -> Value -> Parser a
710withArray :: String -> (Array -> Parser a) -> Value -> Parser a
610−withArray _ f (Array arr) = f arr
711+withArray _ f (Array arr) = f arr
611−withArray expected _ v = typeMismatch expected v
712+withArray name _ v = prependContext name (typeMismatch "Array" v)
612{-# INLINE withArray #-}
713{-# INLINE withArray #-}
613
714
614−-- | @withNumber expected f value@ applies @f@ to the 'Number' when @value@ is a 'Number'.
615−-- and fails using @'typeMismatch' expected@ otherwise.
616−withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
617−withNumber expected f = withScientific expected (f . scientificToNumber)
618−{-# INLINE withNumber #-}
619−{-# DEPRECATED withNumber "Use withScientific instead" #-}
620−
621−-- | @withScientific expected f value@ applies @f@ to the 'Scientific' number when @value@ is a 'Number'.
715+-- | @'withScientific' name f value@ applies @f@ to the 'Scientific' number
622−-- and fails using @'typeMismatch' expected@ otherwise.
716+-- when @value@ is a 'Data.Aeson.Number' and fails using 'typeMismatch'
717+-- 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"
623withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
728withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
624−withScientific _ f (Number scientific) = f scientific
729+withScientific _ f (Number scientific) = f scientific
625−withScientific expected _ v = typeMismatch expected v
730+withScientific name _ v = prependContext name (typeMismatch "Number" v)
626{-# INLINE withScientific #-}
731{-# INLINE withScientific #-}
627
732
628−-- | @withBool expected f value@ applies @f@ to the 'Bool' when @value@ is a @Bool@
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+
782+-- | @'withBool' expected f value@ applies @f@ to the 'Bool' when @value@ is a
629−-- and fails using @'typeMismatch' expected@ otherwise.
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"
630withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
789withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
631−withBool _ f (Bool arr) = f arr
790+withBool _ f (Bool arr) = f arr
632−withBool expected _ v = typeMismatch expected v
791+withBool name _ v = prependContext name (typeMismatch "Boolean" v)
633{-# INLINE withBool #-}
792{-# INLINE withBool #-}
634
793
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+
635-- | Convert a value from JSON, failing if the types do not match.
804-- | Convert a value from JSON, failing if the types do not match.
636fromJSON :: (FromJSON a) => Value -> Result a
805fromJSON :: (FromJSON a) => Value -> Result a
637fromJSON = parse parseJSON
806fromJSON = parse parseJSON
@@ -650,32 +819,68 @@ ifromJSON = iparse parseJSON
650-- in an object for it to be valid. If the key and value are
819-- in an object for it to be valid. If the key and value are
651-- optional, use '.:?' instead.
820-- optional, use '.:?' instead.
652(.:) :: (FromJSON a) => Object -> Text -> Parser a
821(.:) :: (FromJSON a) => Object -> Text -> Parser a
653−obj .: key = case H.lookup key obj of
822+(.:) = explicitParseField parseJSON
654− Nothing -> fail $ "key " ++ show key ++ " not present"
655− Just v -> parseJSON v <?> Key key
656{-# INLINE (.:) #-}
823{-# INLINE (.:) #-}
657
824
658−-- | Retrieve the value associated with the given key of an 'Object'. The
825+-- | Retrieve the value associated with the given key of an 'Object'. The
659−-- result is 'Nothing' if the key is not present or value is 'Null', or 'empty'
826+-- result is 'Nothing' if the key is not present or if its value is 'Null',
660−-- if the value cannot be converted to the desired type.
827+-- or 'empty' if the value cannot be converted to the desired type.
661--
828--
662-- This accessor is most useful if the key and value can be absent
829-- This accessor is most useful if the key and value can be absent
663-- from an object without affecting its validity. If the key and
830-- from an object without affecting its validity. If the key and
664-- value are mandatory, use '.:' instead.
831-- value are mandatory, use '.:' instead.
665(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
832(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
666−obj .:? key = case H.lookup key obj of
833+(.:?) = explicitParseFieldMaybe parseJSON
667− Nothing -> pure Nothing
668− Just v -> parseJSON v <?> Key key
669{-# INLINE (.:?) #-}
834{-# INLINE (.:?) #-}
670
835
671−-- | Like '.:?', but the resulting parser will fail,
836+-- | Retrieve the value associated with the given key of an 'Object'.
672−-- if the key is present but is 'Null'.
837+-- The result is 'Nothing' if the key is not present or 'empty' if the
838+-- value cannot be converted to the desired type.
839+--
840+-- This differs from '.:?' by attempting to parse 'Null' the same as any
841+-- other JSON value, instead of interpreting it as 'Nothing'.
673(.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
842(.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
674−obj .:! key = case H.lookup key obj of
843+(.:!) = explicitParseFieldMaybe' parseJSON
675− Nothing -> pure Nothing
676− Just v -> Just <$> parseJSON v <?> Key key
677{-# INLINE (.:!) #-}
844{-# INLINE (.:!) #-}
678
845
846+-- | Function variant of '.:'.
847+parseField :: (FromJSON a) => Object -> Text -> Parser a
848+parseField = (.:)
849+{-# INLINE parseField #-}
850+
851+-- | Function variant of '.:?'.
852+parseFieldMaybe :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
853+parseFieldMaybe = (.:?)
854+{-# INLINE parseFieldMaybe #-}
855+
856+-- | Function variant of '.:!'.
857+parseFieldMaybe' :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
858+parseFieldMaybe' = (.:!)
859+{-# INLINE parseFieldMaybe' #-}
860+
861+-- | Variant of '.:' with explicit parser function.
862+--
863+-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
864+explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
865+explicitParseField p obj key = case H.lookup key obj of
866+ Nothing -> fail $ "key " ++ show key ++ " not found"
867+ Just v -> p v <?> Key key
868+{-# INLINE explicitParseField #-}
869+
870+-- | Variant of '.:?' with explicit parser function.
871+explicitParseFieldMaybe :: (Value -> Parser a) -> Object -> Text -> Parser (Maybe a)
872+explicitParseFieldMaybe p obj key = case H.lookup key obj of
873+ Nothing -> pure Nothing
874+ Just v -> liftParseJSON p (listParser p) v <?> Key key -- listParser isn't used by maybe instance.
875+{-# INLINE explicitParseFieldMaybe #-}
876+
877+-- | Variant of '.:!' with explicit parser function.
878+explicitParseFieldMaybe' :: (Value -> Parser a) -> Object -> Text -> Parser (Maybe a)
879+explicitParseFieldMaybe' p obj key = case H.lookup key obj of
880+ Nothing -> pure Nothing
881+ Just v -> Just <$> p v <?> Key key
882+{-# INLINE explicitParseFieldMaybe' #-}
883+
679-- | Helper for use in combination with '.:?' to provide default
884-- | Helper for use in combination with '.:?' to provide default
680-- values for optional JSON object fields.
885-- values for optional JSON object fields.
681--
886--
@@ -698,11 +903,47 @@ pmval .!= val = fromMaybe val <$> pmval
698-- Generic parseJSON
903-- Generic parseJSON
699-------------------------------------------------------------------------------
904-------------------------------------------------------------------------------
700
905
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+
701instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
911instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
702 -- Meta-information, which is not handled elsewhere, is just added to the
912 -- Meta-information, which is not handled elsewhere, is just added to the
703 -- parsed value:
913 -- parsed value:
704 gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
914 gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
705
915
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+
706instance (FromJSON a) => GFromJSON arity (K1 i a) where
947instance (FromJSON a) => GFromJSON arity (K1 i a) where
707 -- Constant values are decoded using their FromJSON instance:
948 -- Constant values are decoded using their FromJSON instance:
708 gParseJSON _opts _ = fmap K1 . parseJSON
949 gParseJSON _opts _ = fmap K1 . parseJSON
@@ -717,327 +958,499 @@ instance (FromJSON1 f) => GFromJSON One (Rec1 f) where
717 -- FromJSON1 instance:
958 -- FromJSON1 instance:
718 gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
959 gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
719
960
720−instance GFromJSON arity U1 where
721− -- Empty constructors are expected to be encoded as an empty array:
722− gParseJSON _opts _ v
723− | isEmptyArray v = pure U1
724− | otherwise = typeMismatch "unit constructor (U1)" v
725−
726−instance (ConsFromJSON arity a) => GFromJSON arity (C1 c a) where
727− -- Constructors need to be decoded differently depending on whether they're
728− -- a record or not. This distinction is made by consParseJSON:
729− gParseJSON opts fargs = fmap M1 . consParseJSON opts fargs
730−
731−instance ( FromProduct arity a, FromProduct arity b
732− , ProductSize a, ProductSize b
733− ) => GFromJSON arity (a :*: b) where
734− -- Products are expected to be encoded to an array. Here we check whether we
735− -- got an array of the same size as the product, then parse each of the
736− -- product's elements using parseProduct:
737− gParseJSON opts fargs = withArray "product (:*:)" $ \arr ->
738− let lenArray = V.length arr
739− lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
740− productSize in
741− if lenArray == lenProduct
742− then parseProduct opts fargs arr 0 lenProduct
743− else fail $ "When expecting a product of " ++ show lenProduct ++
744− " values, encountered an Array of " ++ show lenArray ++
745− " elements instead"
746−
747−instance ( AllNullary (a :+: b) allNullary
748− , ParseSum arity (a :+: b) allNullary
749− ) => GFromJSON arity (a :+: b) where
750− -- If all constructors of a sum datatype are nullary and the
751− -- 'allNullaryToStringTag' option is set they are expected to be
752− -- encoded as strings. This distinction is made by 'parseSum':
753− gParseJSON opts fargs =
754− (unTagged :: Tagged allNullary (Parser ((a :+: b) d)) ->
755− Parser ((a :+: b) d))
756− . parseSum opts fargs
757−
758instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
961instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
759 -- If an occurrence of the last type parameter is nested inside two
962 -- If an occurrence of the last type parameter is nested inside two
760 -- composed types, it is decoded by using the outermost type's FromJSON1
963 -- composed types, it is decoded by using the outermost type's FromJSON1
761 -- instance to generically decode the innermost type:
964 -- instance to generically decode the innermost type:
762 gParseJSON opts fargs =
965 gParseJSON opts fargs =
763− let gpj = gParseJSON opts fargs in
966+ let gpj = gParseJSON opts fargs in
764− fmap Comp1 . liftParseJSON gpj (listParser gpj)
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)
985+
986+-- | Single constructor.
987+instance ( ConsFromJSON arity a
988+ , AllNullary (C1 c a) allNullary
989+ , ParseSum arity (C1 c a) allNullary
990+ , Constructor c
991+ ) => GFromJSON' arity (C1 c a) where
992+ -- The option 'tagSingleConstructors' determines whether to wrap
993+ -- a single-constructor type.
994+ gParseJSON' p@(_ :* opts :* _)
995+ | 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
1006+ -- If all constructors of a sum datatype are nullary and the
1007+ -- 'allNullaryToStringTag' option is set they are expected to be
1008+ -- 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
765
1013
766--------------------------------------------------------------------------------
1014--------------------------------------------------------------------------------
767
1015
768class ParseSum arity f allNullary where
1016class ParseSum arity f allNullary where
769− parseSum :: Options -> FromArgs arity a
1017+ parseSum :: TypeName :* Options :* FromArgs arity a
1018+ -> Value
770− -> Value -> Tagged allNullary (Parser (f a))
1019+ -> Tagged allNullary (Parser (f a))
771−
1020+
1021+instance ( ConstructorNames f
772−instance ( SumFromString (a :+: b)
1022+ , SumFromString f
773− , FromPair arity (a :+: b)
1023+ , FromPair arity f
774− , FromTaggedObject arity (a :+: b)
1024+ , FromTaggedObject arity f
775− , FromUntaggedValue arity (a :+: b)
1025+ , FromUntaggedValue arity f
776− ) => ParseSum arity (a :+: b) True where
1026+ ) => ParseSum arity f True where
777− parseSum opts fargs
1027+ parseSum p@(tname :* opts :* _)
778− | allNullaryToStringTag opts = Tagged . parseAllNullarySum opts
1028+ | allNullaryToStringTag opts = Tagged . parseAllNullarySum tname opts
779− | otherwise = Tagged . parseNonAllNullarySum opts fargs
1029+ | otherwise = Tagged . parseNonAllNullarySum p
780−
1030+
1031+instance ( ConstructorNames f
781−instance ( FromPair arity (a :+: b)
1032+ , FromPair arity f
782− , FromTaggedObject arity (a :+: b)
1033+ , FromTaggedObject arity f
783− , FromUntaggedValue arity (a :+: b)
1034+ , FromUntaggedValue arity f
784− ) => ParseSum arity (a :+: b) False where
1035+ ) => ParseSum arity f False where
785− parseSum opts fargs = Tagged . parseNonAllNullarySum opts fargs
1036+ parseSum p = Tagged . parseNonAllNullarySum p
786
1037
787--------------------------------------------------------------------------------
1038--------------------------------------------------------------------------------
788
1039
789−parseAllNullarySum :: SumFromString f => Options -> Value -> Parser (f a)
1040+parseAllNullarySum :: (SumFromString f, ConstructorNames f)
1041+ => TypeName -> Options -> Value -> Parser (f a)
790−parseAllNullarySum opts = withText "Text" $ \key ->
1042+parseAllNullarySum tname opts =
1043+ withText tname $ \tag ->
791− maybe (notFound key) return $
1044+ maybe (badTag tag) return $
792− parseSumFromString opts key
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])
793
1062
794class SumFromString f where
1063class SumFromString f where
795− parseSumFromString :: Options -> Text -> Maybe (f a)
1064+ parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
796
1065
797instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1066instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
798 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1067 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
799 (R1 <$> parseSumFromString opts key)
1068 (R1 <$> parseSumFromString opts key)
800
1069
801instance (Constructor c) => SumFromString (C1 c U1) where
1070instance (Constructor c) => SumFromString (C1 c U1) where
802− parseSumFromString opts key | key == name = Just $ M1 U1
1071+ parseSumFromString modifier key
1072+ | key == name = Just $ M1 U1
803− | otherwise = Nothing
1073+ | otherwise = Nothing
804− where
1074+ where
805− name = pack $ constructorTagModifier opts $
806− conName (undefined :: t c U1 p)
1075+ name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
807
1076
808−--------------------------------------------------------------------------------
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)
809
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
1111+
1112+--------------------------------------------------------------------------------
810−parseNonAllNullarySum :: ( FromPair arity (a :+: b)
1113+parseNonAllNullarySum :: forall f c arity.
1114+ ( FromPair arity f
811− , FromTaggedObject arity (a :+: b)
1115+ , FromTaggedObject arity f
812− , FromUntaggedValue arity (a :+: b)
1116+ , FromUntaggedValue arity f
1117+ , ConstructorNames f
813− ) => Options -> FromArgs arity c
1118+ ) => TypeName :* Options :* FromArgs arity c
814− -> Value -> Parser ((a :+: b) c)
1119+ -> Value -> Parser (f c)
815−parseNonAllNullarySum opts fargs =
1120+parseNonAllNullarySum p@(tname :* opts :* _) =
816 case sumEncoding opts of
1121 case sumEncoding opts of
817 TaggedObject{..} ->
1122 TaggedObject{..} ->
818− withObject "Object" $ \obj -> do
1123+ withObject tname $ \obj -> do
819− tag <- obj .: pack tagFieldName
1124+ tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
820− fromMaybe (notFound tag) $
1125+ fromMaybe (badTag tag <?> Key tagKey) $
821− parseFromTaggedObject opts fargs contentsFieldName obj tag
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])
822
1133
823 ObjectWithSingleField ->
1134 ObjectWithSingleField ->
824− withObject "Object" $ \obj ->
825− case H.toList obj of
1135+ withObject tname $ \obj -> case H.toList obj of
826− [pair@(tag, _)] -> fromMaybe (notFound tag) $
1136+ [(tag, v)] -> maybe (badTag tag) (<?> Key tag) $
827− parsePair opts fargs pair
1137+ parsePair (tag :* p) v
828− _ -> fail "Object doesn't have a single field"
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
829
1145
830 TwoElemArray ->
1146 TwoElemArray ->
831− withArray "Array" $ \arr ->
1147+ withArray tname $ \arr -> case V.length arr of
832− if V.length arr == 2
1148+ 2 | String tag <- V.unsafeIndex arr 0 ->
833− then case V.unsafeIndex arr 0 of
1149+ maybe (badTag tag <?> Index 0) (<?> Index 1) $
834− String tag -> fromMaybe (notFound tag) $
835− parsePair opts fargs (tag, V.unsafeIndex arr 1)
1150+ parsePair (tag :* p) (V.unsafeIndex arr 1)
1151+ | otherwise ->
1152+ contextType tname $
836− _ -> fail "First element is not a String"
1153+ fail "tag element is not a String" <?> Index 0
837− else fail "Array doesn't have 2 elements"
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
838
1161
839− UntaggedValue -> parseUntaggedValue opts fargs
1162+ UntaggedValue -> parseUntaggedValue p
1163+ where
1164+ failWith_ = failWithCTags tname (constructorTagModifier opts)
840
1165
841--------------------------------------------------------------------------------
1166--------------------------------------------------------------------------------
842
1167
843class FromTaggedObject arity f where
1168class 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.
844− parseFromTaggedObject :: Options -> FromArgs arity a
1171+ parseFromTaggedObject
1172+ :: Text :* String :* TypeName :* Options :* FromArgs arity a
845− -> String -> Object
1173+ -> Object
846− -> Text -> Maybe (Parser (f a))
1174+ -> Maybe (Parser (f a))
847
1175
848instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
1176instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
849 FromTaggedObject arity (a :+: b) where
1177 FromTaggedObject arity (a :+: b) where
850− parseFromTaggedObject opts fargs contentsFieldName obj tag =
1178+ parseFromTaggedObject p obj =
851− (fmap L1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag) <|>
1179+ (fmap L1 <$> parseFromTaggedObject p obj) <|>
852− (fmap R1 <$> parseFromTaggedObject opts fargs contentsFieldName obj tag)
1180+ (fmap R1 <$> parseFromTaggedObject p obj)
853
1181
1182+instance ( IsRecord f isRecord
854−instance ( FromTaggedObject' arity f
1183+ , FromTaggedObject' arity f isRecord
855 , Constructor c
1184 , Constructor c
856 ) => FromTaggedObject arity (C1 c f) where
1185 ) => FromTaggedObject arity (C1 c f) where
857− parseFromTaggedObject opts fargs contentsFieldName obj tag
1186+ parseFromTaggedObject (tag :* contentsFieldName :* p@(_ :* opts :* _))
1187+ | tag == tag'
1188+ = Just . fmap M1 .
1189+ (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
858− | tag == name = Just $ M1 <$> parseFromTaggedObject'
1190+ parseFromTaggedObject' (contentsFieldName :* cname :* p)
859− opts fargs contentsFieldName obj
860− | otherwise = Nothing
1191+ | otherwise = const Nothing
861− where
1192+ where
862− name = pack $ constructorTagModifier opts $
1193+ tag' = pack $ constructorTagModifier opts cname
863− conName (undefined :: t c f p)
1194+ cname = conName (undefined :: M1 _i c _f _p)
864
1195
865--------------------------------------------------------------------------------
1196--------------------------------------------------------------------------------
866
1197
867−class FromTaggedObject' arity f where
1198+class FromTaggedObject' arity f isRecord where
868− parseFromTaggedObject' :: Options -> FromArgs arity a -> String
1199+ -- The first component of the parameter tuple is the contents field name.
869− -> Object -> Parser (f a)
1200+ parseFromTaggedObject'
870−
871−class FromTaggedObject'' arity f isRecord where
872− parseFromTaggedObject'' :: Options -> FromArgs arity a -> String
1201+ :: String :* ConName :* TypeName :* Options :* FromArgs arity a
873− -> Object -> Tagged isRecord (Parser (f a))
1202+ -> Object -> Tagged isRecord (Parser (f a))
874−
1203+
875−instance ( IsRecord f isRecord
876− , FromTaggedObject'' arity f isRecord
877− ) => FromTaggedObject' arity f where
1204+instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
878− parseFromTaggedObject' opts fargs contentsFieldName =
1205+ -- Records are unpacked in the tagged object
879− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
880− parseFromTaggedObject'' opts fargs contentsFieldName
1206+ parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
881−
1207+
882−instance (FromRecord arity f) => FromTaggedObject'' arity f True where
883− parseFromTaggedObject'' opts fargs _ =
884− Tagged . parseRecord opts fargs Nothing
885−
886−instance (GFromJSON arity f) => FromTaggedObject'' arity f False where
1208+instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
1209+ -- Nonnullary nonrecords are encoded in the contents field
887− parseFromTaggedObject'' opts fargs contentsFieldName = Tagged .
1210+ parseFromTaggedObject' p obj = Tagged $ do
1211+ contents <- contextCons cname tname (obj .: key)
1212+ consParseJSON p' contents <?> Key key
1213+ where
888− (gParseJSON opts fargs <=< (.: pack contentsFieldName))
1214+ key = pack contentsFieldName
1215+ contentsFieldName :* p'@(cname :* tname :* _) = p
889
1216
890−instance OVERLAPPING_ FromTaggedObject'' arity U1 False where
1217+instance OVERLAPPING_ FromTaggedObject' arity U1 False where
1218+ -- Nullary constructors don't need a contents field
891− parseFromTaggedObject'' _ _ _ _ = Tagged (pure U1)
1219+ parseFromTaggedObject' _ _ = Tagged (pure U1)
892
1220
893--------------------------------------------------------------------------------
1221--------------------------------------------------------------------------------
894
1222
1223+-- | Constructors need to be decoded differently depending on whether they're
1224+-- a record or not. This distinction is made by 'ConsParseJSON'.
895class ConsFromJSON arity f where
1225class ConsFromJSON arity f where
1226+ consParseJSON
896− consParseJSON :: Options -> FromArgs arity a
1227+ :: ConName :* TypeName :* Options :* FromArgs arity a
897− -> Value -> Parser (f a)
1228+ -> Value -> Parser (f a)
898
1229
899class ConsFromJSON' arity f isRecord where
1230class ConsFromJSON' arity f isRecord where
1231+ consParseJSON'
900− consParseJSON' :: Options -> FromArgs arity a
1232+ :: ConName :* TypeName :* Options :* FromArgs arity a
901− -> Maybe Text -- ^ A dummy label
902− -- (Nothing to use proper label)
903− -> Value -> Tagged isRecord (Parser (f a))
1233+ -> Value -> Tagged isRecord (Parser (f a))
904
1234
905instance ( IsRecord f isRecord
1235instance ( IsRecord f isRecord
906 , ConsFromJSON' arity f isRecord
1236 , ConsFromJSON' arity f isRecord
907 ) => ConsFromJSON arity f where
1237 ) => ConsFromJSON arity f where
908− consParseJSON opts fargs v = let
1238+ consParseJSON p =
909− (v2,lab) = case (unwrapUnaryRecords opts,isUnary (undefined :: f a)) of
910− -- use a dummy object with a dummy label
911− (True,True) -> (object [(pack "dummy",v)], Just $ pack "dummy")
912− _ ->(v,Nothing)
913− in (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
1239+ (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
914− $ consParseJSON' opts fargs lab v2
1240+ . consParseJSON' p
915
1241
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))
916
1248
917−instance (FromRecord arity f) => ConsFromJSON' arity f True where
1249+instance RecordFromJSON arity f => ConsFromJSON' arity f True where
918− consParseJSON' opts fargs mlab = Tagged . withObject "record (:*:)"
1250+ consParseJSON' p@(cname :* tname :* _) =
919− (parseRecord opts fargs mlab)
1251+ Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
920
1252
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_
921−instance (GFromJSON arity f) => ConsFromJSON' arity f False where
1267+ GFromJSON arity f => ConsFromJSON' arity (S1 s f) False where
1268+ consParseJSON' (_ :* _ :* opts :* fargs) =
922− consParseJSON' opts fargs _ = Tagged . gParseJSON 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
923
1274
924--------------------------------------------------------------------------------
1275--------------------------------------------------------------------------------
925
1276
926−class FromRecord arity f where
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)
1284+
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
927− parseRecord :: Options -> FromArgs arity a
1290+ :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
928− -> Maybe Text -- ^ A dummy label
929− -- (Nothing to use proper label)
930− -> Object -> Parser (f a)
1291+ -> Object -> Parser (f a)
931−
1292+
932−instance ( FromRecord arity a
1293+instance ( FieldNames f
933− , FromRecord arity b
1294+ , RecordFromJSON' arity f
934− ) => FromRecord arity (a :*: b) where
1295+ ) => RecordFromJSON arity f where
935− parseRecord opts fargs _ obj =
1296+ recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
936− (:*:) <$> parseRecord opts fargs Nothing obj
1297+ \obj -> checkUnknown obj >> recordParseJSON' p obj
937− <*> parseRecord opts fargs Nothing obj
938−
939−instance ( Selector s
940− , GFromJSON arity a
941− ) => FromRecord arity (S1 s a) where
942− parseRecord opts fargs lab =
943− (<?> Key label) . gParseJSON opts fargs <=< (.: label)
944− where
945− label = fromMaybe defLabel lab
946− defLabel = pack . fieldLabelModifier opts $
947− selName (undefined :: t s a p)
948−
949−instance OVERLAPPING_ (Selector s, FromJSON a) =>
950− FromRecord arity (S1 s (K1 i (Maybe a))) where
951− parseRecord _ _ (Just lab) obj = (M1 . K1) <$> obj .:? lab
952− parseRecord opts _ Nothing obj = (M1 . K1) <$> obj .:? pack label
953 where
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
1322+
1323+instance 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
954− label = fieldLabelModifier opts $
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
955− selName (undefined :: t s (K1 i (Maybe a)) p)
1337+ sname = selName (undefined :: M1 _i s _f _p)
1338+
1339+-- Parse an Option like a Maybe.
1340+instance 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))
956
1346
957--------------------------------------------------------------------------------
1347--------------------------------------------------------------------------------
958
1348
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+
959−class FromProduct arity f where
1369+class ProductFromJSON arity f where
960− parseProduct :: Options -> FromArgs arity a
1370+ productParseJSON :: ConName :* TypeName :* Options :* FromArgs arity a
961 -> Array -> Int -> Int
1371 -> Array -> Int -> Int
962 -> Parser (f a)
1372 -> Parser (f a)
963
1373
964−instance ( FromProduct arity a
1374+instance ( ProductFromJSON arity a
965− , FromProduct arity b
1375+ , ProductFromJSON arity b
966− ) => FromProduct arity (a :*: b) where
1376+ ) => ProductFromJSON arity (a :*: b) where
967− parseProduct opts fargs arr ix len =
1377+ productParseJSON p arr ix len =
968− (:*:) <$> parseProduct opts fargs arr ix lenL
1378+ (:*:) <$> productParseJSON p arr ix lenL
969− <*> parseProduct opts fargs arr ixR lenR
1379+ <*> productParseJSON p arr ixR lenR
970 where
1380 where
971 lenL = len `unsafeShiftR` 1
1381 lenL = len `unsafeShiftR` 1
972 ixR = ix + lenL
1382 ixR = ix + lenL
973 lenR = len - lenL
1383 lenR = len - lenL
974
1384
975−instance (GFromJSON arity a) => FromProduct arity (S1 s a) where
1385+instance (GFromJSON arity a) => ProductFromJSON arity (S1 s a) where
976− parseProduct opts fargs arr ix _ =
1386+ productParseJSON (_ :* _ :* opts :* fargs) arr ix _ =
977− gParseJSON opts fargs $ V.unsafeIndex arr ix
1387+ M1 <$> gParseJSON opts fargs (V.unsafeIndex arr ix) <?> Index ix
978
1388
979--------------------------------------------------------------------------------
1389--------------------------------------------------------------------------------
980
1390
981class FromPair arity f where
1391class FromPair arity f where
1392+ -- The first component of the parameter tuple is the tag to match.
982− parsePair :: Options -> FromArgs arity a
1393+ parsePair :: Text :* TypeName :* Options :* FromArgs arity a
1394+ -> Value
983− -> Pair -> Maybe (Parser (f a))
1395+ -> Maybe (Parser (f a))
984
1396
985instance ( FromPair arity a
1397instance ( FromPair arity a
986 , FromPair arity b
1398 , FromPair arity b
987 ) => FromPair arity (a :+: b) where
1399 ) => FromPair arity (a :+: b) where
1400+ parsePair p pair =
988− parsePair opts fargs pair = (fmap L1 <$> parsePair opts fargs pair) <|>
1401+ (fmap L1 <$> parsePair p pair) <|>
989− (fmap R1 <$> parsePair opts fargs pair)
1402+ (fmap R1 <$> parsePair p pair)
990
1403
991instance ( Constructor c
1404instance ( Constructor c
992− , GFromJSON arity a
993 , ConsFromJSON arity a
1405 , ConsFromJSON arity a
994 ) => FromPair arity (C1 c a) where
1406 ) => FromPair arity (C1 c a) where
995− parsePair opts fargs (tag, value)
1407+ parsePair (tag :* p@(_ :* opts :* _)) v
996− | tag == tag' = Just $ gParseJSON opts fargs value
1408+ | tag == tag' = Just $ M1 <$> consParseJSON (cname :* p) v
997 | otherwise = Nothing
1409 | otherwise = Nothing
998− where
1410+ where
999− tag' = pack $ constructorTagModifier opts $
1411+ tag' = pack $ constructorTagModifier opts cname
1000− conName (undefined :: t c a p)
1412+ cname = conName (undefined :: M1 _i c _a _p)
1001
1413
1002--------------------------------------------------------------------------------
1414--------------------------------------------------------------------------------
1003
1415
1004class FromUntaggedValue arity f where
1416class FromUntaggedValue arity f where
1005− parseUntaggedValue :: Options -> FromArgs arity a
1417+ parseUntaggedValue :: TypeName :* Options :* FromArgs arity a
1418+ -> Value
1006− -> Value -> Parser (f a)
1419+ -> Parser (f a)
1007
1420
1008instance
1421instance
1009 ( FromUntaggedValue arity a
1422 ( FromUntaggedValue arity a
1010 , FromUntaggedValue arity b
1423 , FromUntaggedValue arity b
1011 ) => FromUntaggedValue arity (a :+: b)
1424 ) => FromUntaggedValue arity (a :+: b)
1012 where
1425 where
1013− parseUntaggedValue opts fargs value =
1426+ parseUntaggedValue p value =
1014− L1 <$> parseUntaggedValue opts fargs value <|>
1427+ L1 <$> parseUntaggedValue p value <|>
1015− R1 <$> parseUntaggedValue opts fargs value
1428+ R1 <$> parseUntaggedValue p value
1016
1429
1017instance OVERLAPPABLE_
1430instance OVERLAPPABLE_
1018− ( GFromJSON arity a
1431+ ( ConsFromJSON arity a
1019− , ConsFromJSON arity a
1432+ , Constructor c
1020 ) => FromUntaggedValue arity (C1 c a)
1433 ) => FromUntaggedValue arity (C1 c a)
1021 where
1434 where
1022− parseUntaggedValue = gParseJSON
1435+ parseUntaggedValue p = fmap M1 . consParseJSON (cname :* p)
1436+ where
1437+ cname = conName (undefined :: M1 _i c _f _p)
1023
1438
1024instance OVERLAPPING_
1439instance OVERLAPPING_
1025 ( Constructor c )
1440 ( Constructor c )
1026 => FromUntaggedValue arity (C1 c U1)
1441 => FromUntaggedValue arity (C1 c U1)
1027 where
1442 where
1028− parseUntaggedValue opts _ (String s)
1443+ parseUntaggedValue (tname :* opts :* _) v =
1029− | s == pack (constructorTagModifier opts (conName (undefined :: t c U1 p))) =
1444+ contextCons cname tname $ case v of
1445+ String tag
1030− pure $ M1 U1
1446+ | tag == tag' -> pure $ M1 U1
1031− | otherwise =
1447+ | otherwise -> fail_ tag
1032− fail $ "Invalid tag: " ++ unpack s
1448+ _ -> typeMismatch "String" v
1033− parseUntaggedValue _ _ v = typeMismatch (conName (undefined :: t c U1 p)) v
1449+ where
1034−
1450+ tag' = pack $ constructorTagModifier opts cname
1035−
1451+ cname = conName (undefined :: M1 _i c _f _p)
1036−--------------------------------------------------------------------------------
1452+ fail_ tag = fail $
1037−
1453+ "expected tag " ++ show tag' ++ ", but found tag " ++ show tag
1038−notFound :: Text -> Parser a
1039−notFound key = fail $ "The key \"" ++ unpack key ++ "\" was not found"
1040−{-# INLINE notFound #-}
1041
1454
1042-------------------------------------------------------------------------------
1455-------------------------------------------------------------------------------
1043-- Instances
1456-- Instances
@@ -1095,17 +1508,20 @@ instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
1095 parseJSON = parseJSON2
1508 parseJSON = parseJSON2
1096 {-# INLINE parseJSON #-}
1509 {-# INLINE parseJSON #-}
1097
1510
1098−
1511+instance FromJSON Void where
1512+ parseJSON _ = fail "Cannot parse Void"
1513+ {-# INLINE parseJSON #-}
1099
1514
1100instance FromJSON Bool where
1515instance FromJSON Bool where
1101− parseJSON = withBool "Bool" pure
1516+ parseJSON (Bool b) = pure b
1517+ parseJSON v = typeMismatch "Bool" v
1102 {-# INLINE parseJSON #-}
1518 {-# INLINE parseJSON #-}
1103
1519
1104instance FromJSONKey Bool where
1520instance FromJSONKey Bool where
1105 fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1521 fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1106 "true" -> pure True
1522 "true" -> pure True
1107 "false" -> pure False
1523 "false" -> pure False
1108− _ -> fail $ "Cannot parse key into Bool: " ++ T.unpack t
1524+ _ -> fail $ "cannot parse key " ++ show t ++ " into Bool"
1109
1525
1110instance FromJSON Ordering where
1526instance FromJSON Ordering where
1111 parseJSON = withText "Ordering" $ \s ->
1527 parseJSON = withText "Ordering" $ \s ->
@@ -1113,25 +1529,30 @@ instance FromJSON Ordering where
1113 "LT" -> return LT
1529 "LT" -> return LT
1114 "EQ" -> return EQ
1530 "EQ" -> return EQ
1115 "GT" -> return GT
1531 "GT" -> return GT
1532+ _ -> fail $ "parsing Ordering failed, unexpected " ++ show s ++
1116− _ -> fail "Parsing Ordering value failed: expected \"LT\", \"EQ\", or \"GT\""
1533+ " (expected \"LT\", \"EQ\", or \"GT\")"
1117
1534
1118instance FromJSON () where
1535instance FromJSON () where
1119 parseJSON = withArray "()" $ \v ->
1536 parseJSON = withArray "()" $ \v ->
1120 if V.null v
1537 if V.null v
1121 then pure ()
1538 then pure ()
1122− else fail "Expected an empty array"
1539+ else prependContext "()" $ fail "expected an empty array"
1123 {-# INLINE parseJSON #-}
1540 {-# INLINE parseJSON #-}
1124
1541
1125instance FromJSON Char where
1542instance FromJSON Char where
1126− parseJSON = withText "Char" $ \t ->
1543+ parseJSON = withText "Char" parseChar
1127− if T.compareLength t 1 == EQ
1128− then pure $ T.head t
1129− else fail "Expected a string of length 1"
1130 {-# INLINE parseJSON #-}
1544 {-# INLINE parseJSON #-}
1131
1545
1132− parseJSONList = withText "String" $ pure . T.unpack
1546+ parseJSONList (String s) = pure (T.unpack s)
1547+ parseJSONList v = typeMismatch "String" v
1133 {-# INLINE parseJSONList #-}
1548 {-# INLINE parseJSONList #-}
1134
1549
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+
1135instance FromJSON Double where
1556instance FromJSON Double where
1136 parseJSON = parseRealFloat "Double"
1557 parseJSON = parseRealFloat "Double"
1137 {-# INLINE parseJSON #-}
1558 {-# INLINE parseJSON #-}
@@ -1143,12 +1564,6 @@ instance FromJSONKey Double where
1143 "-Infinity" -> pure (negate 1/0)
1564 "-Infinity" -> pure (negate 1/0)
1144 _ -> Scientific.toRealFloat <$> parseScientificText t
1565 _ -> Scientific.toRealFloat <$> parseScientificText t
1145
1566
1146−instance FromJSON Number where
1147− parseJSON (Number s) = pure $ scientificToNumber s
1148− parseJSON Null = pure (D (0/0))
1149− parseJSON v = typeMismatch "Number" v
1150− {-# INLINE parseJSON #-}
1151−
1152instance FromJSON Float where
1567instance FromJSON Float where
1153 parseJSON = parseRealFloat "Float"
1568 parseJSON = parseRealFloat "Float"
1154 {-# INLINE parseJSON #-}
1569 {-# INLINE parseJSON #-}
@@ -1161,17 +1576,30 @@ instance FromJSONKey Float where
1161 _ -> Scientific.toRealFloat <$> parseScientificText t
1576 _ -> Scientific.toRealFloat <$> parseScientificText t
1162
1577
1163instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1578instance (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
1164− parseJSON = withObject "Rational" $ \obj ->
1589+ objParser = withObject "Rational" $ \obj -> do
1165− (%) <$> obj .: "numerator"
1590+ numerator <- obj .: "numerator"
1591+ denominator <- obj .: "denominator"
1592+ if denominator == 0
1593+ then fail "Ratio denominator was 0"
1166− <*> obj .: "denominator"
1594+ else pure $ numerator % denominator
1167− {-# INLINE parseJSON #-}
1595+ {-# INLINE parseJSON #-}
1168−
1596+
1169−-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
1597+-- | This instance includes a bounds check to prevent maliciously
1170−-- since an attacker could easily fill up the memory of the target
1598+-- large inputs to fill up the memory of the target system. You can
1171−-- system by specifying a scientific number with a big exponent like
1599+-- newtype 'Scientific' and provide your own instance using
1172−-- @1e1000000000@.
1600+-- 'withScientific' if you want to allow larger inputs.
1173instance HasResolution a => FromJSON (Fixed a) where
1601instance HasResolution a => FromJSON (Fixed a) where
1174− parseJSON = withScientific "Fixed" $ pure . realToFrac
1602+ parseJSON = prependContext "Fixed" . withBoundedScientific' (pure . realToFrac)
1175 {-# INLINE parseJSON #-}
1603 {-# INLINE parseJSON #-}
1176
1604
1177instance FromJSON Int where
1605instance FromJSON Int where
@@ -1181,10 +1609,10 @@ instance FromJSON Int where
1181instance FromJSONKey Int where
1609instance FromJSONKey Int where
1182 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1610 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1183
1611
1184−-- | /WARNING:/ Only parse Integers from trusted input since an
1612+-- | This instance includes a bounds check to prevent maliciously
1185−-- attacker could easily fill up the memory of the target system by
1613+-- large inputs to fill up the memory of the target system. You can
1186−-- specifying a scientific number with a big exponent like
1614+-- newtype 'Scientific' and provide your own instance using
1187−-- @1e1000000000@.
1615+-- 'withScientific' if you want to allow larger inputs.
1188instance FromJSON Integer where
1616instance FromJSON Integer where
1189 parseJSON = parseIntegral "Integer"
1617 parseJSON = parseIntegral "Integer"
1190 {-# INLINE parseJSON #-}
1618 {-# INLINE parseJSON #-}
@@ -1193,16 +1621,21 @@ instance FromJSONKey Integer where
1193 fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
1621 fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
1194
1622
1195instance FromJSON Natural where
1623instance FromJSON Natural where
1196− parseJSON = withScientific "Natural" $ \s ->
1624+ parseJSON value = do
1197− if Scientific.coefficient s < 0
1625+ integer <- parseIntegral "Natural" value
1198− then fail $ "Expected a Natural number but got the negative number: " <> show s
1626+ parseNatural integer
1199− else pure $ truncate s
1200
1627
1201instance FromJSONKey Natural where
1628instance FromJSONKey Natural where
1202− fromJSONKey = FromJSONKeyTextParser $ \t -> parseScientificText t >>= \s ->
1629+ fromJSONKey = FromJSONKeyTextParser $ \text -> do
1203− if Scientific.coefficient s < 0
1630+ integer <- parseIntegralText "Natural" text
1631+ parseNatural integer
1632+
1633+parseNatural :: Integer -> Parser Natural
1634+parseNatural integer =
1635+ if integer < 0 then
1204− then fail $ "Expected a Natural number but got the negative number: " <> show s
1636+ fail $ "parsing Natural failed, unexpected negative number " <> show integer
1205− else pure $ truncate s
1637+ else
1638+ pure $ fromIntegral integer
1206
1639
1207instance FromJSON Int8 where
1640instance FromJSON Int8 where
1208 parseJSON = parseBoundedIntegral "Int8"
1641 parseJSON = parseBoundedIntegral "Int8"
@@ -1267,6 +1700,9 @@ instance FromJSON Word64 where
1267instance FromJSONKey Word64 where
1700instance FromJSONKey Word64 where
1268 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
1701 fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
1269
1702
1703+instance FromJSON CTime where
1704+ parseJSON = fmap CTime . parseJSON
1705+ {-# INLINE parseJSON #-}
1270
1706
1271instance FromJSON Text where
1707instance FromJSON Text where
1272 parseJSON = withText "Text" pure
1708 parseJSON = withText "Text" pure
@@ -1296,17 +1732,17 @@ parseVersionText = go . readP_to_S parseVersion . unpack
1296 where
1732 where
1297 go [(v,[])] = return v
1733 go [(v,[])] = return v
1298 go (_ : xs) = go xs
1734 go (_ : xs) = go xs
1299− go _ = fail "could not parse Version"
1735+ go _ = fail "parsing Version failed"
1300
1736
1301-------------------------------------------------------------------------------
1737-------------------------------------------------------------------------------
1302-- semigroups NonEmpty
1738-- semigroups NonEmpty
1303-------------------------------------------------------------------------------
1739-------------------------------------------------------------------------------
1304
1740
1305instance FromJSON1 NonEmpty where
1741instance FromJSON1 NonEmpty where
1306− liftParseJSON p _ = withArray "NonEmpty a" $
1742+ liftParseJSON p _ = withArray "NonEmpty" $
1307 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1743 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1308 where
1744 where
1309− ne [] = fail "Expected a NonEmpty but got an empty list"
1745+ ne [] = fail "parsing NonEmpty failed, unexpected empty list"
1310 ne (x:xs) = pure (x :| xs)
1746 ne (x:xs) = pure (x :| xs)
1311 {-# INLINE liftParseJSON #-}
1747 {-# INLINE liftParseJSON #-}
1312
1748
@@ -1327,7 +1763,7 @@ instance FromJSON Scientific where
1327-------------------------------------------------------------------------------
1763-------------------------------------------------------------------------------
1328
1764
1329instance FromJSON1 DList.DList where
1765instance FromJSON1 DList.DList where
1330− liftParseJSON p _ = withArray "DList a" $
1766+ liftParseJSON p _ = withArray "DList" $
1331 fmap DList.fromList .
1767 fmap DList.fromList .
1332 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1768 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1333 {-# INLINE liftParseJSON #-}
1769 {-# INLINE liftParseJSON #-}
@@ -1354,7 +1790,7 @@ instance (FromJSON a) => FromJSON (Identity a) where
1354 parseJSONList = liftParseJSONList parseJSON parseJSONList
1790 parseJSONList = liftParseJSONList parseJSON parseJSONList
1355 {-# INLINE parseJSONList #-}
1791 {-# INLINE parseJSONList #-}
1356
1792
1357−instance (FromJSONKey a, FromJSON a) => FromJSONKey (Identity a) where
1793+instance (FromJSONKey a) => FromJSONKey (Identity a) where
1358 fromJSONKey = coerceFromJSONKeyFunction (fromJSONKey :: FromJSONKeyFunction a)
1794 fromJSONKey = coerceFromJSONKeyFunction (fromJSONKey :: FromJSONKeyFunction a)
1359 fromJSONKeyList = coerceFromJSONKeyFunction (fromJSONKeyList :: FromJSONKeyFunction [a])
1795 fromJSONKeyList = coerceFromJSONKeyFunction (fromJSONKeyList :: FromJSONKeyFunction [a])
1360
1796
@@ -1404,7 +1840,7 @@ instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Sum f g) where
1404 inr = "InR"
1840 inr = "InR"
1405
1841
1406 liftParseJSON _ _ _ = fail $
1842 liftParseJSON _ _ _ = fail $
1407− "expected an object with a single property " ++
1843+ "parsing Sum failed, expected an object with a single property " ++
1408 "where the property key should be either " ++
1844 "where the property key should be either " ++
1409 "\"InL\" or \"InR\""
1845 "\"InL\" or \"InR\""
1410 {-# INLINE liftParseJSON #-}
1846 {-# INLINE liftParseJSON #-}
@@ -1418,7 +1854,7 @@ instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Sum f g a) where
1418-------------------------------------------------------------------------------
1854-------------------------------------------------------------------------------
1419
1855
1420instance FromJSON1 Seq.Seq where
1856instance FromJSON1 Seq.Seq where
1421− liftParseJSON p _ = withArray "Seq a" $
1857+ liftParseJSON p _ = withArray "Seq" $
1422 fmap Seq.fromList .
1858 fmap Seq.fromList .
1423 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1859 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1424 {-# INLINE liftParseJSON #-}
1860 {-# INLINE liftParseJSON #-}
@@ -1452,15 +1888,15 @@ instance FromJSON a => FromJSON (IntMap.IntMap a) where
1452
1888
1453instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1889instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1454 liftParseJSON p _ = case fromJSONKey of
1890 liftParseJSON p _ = case fromJSONKey of
1455− FromJSONKeyCoerce _-> withObject "Map k v" $
1891+ FromJSONKeyCoerce _ -> withObject "Map" $
1456 fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1892 fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1457− FromJSONKeyText f -> withObject "Map k v" $
1893+ FromJSONKeyText f -> withObject "Map" $
1458 fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1894 fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)
1459− FromJSONKeyTextParser f -> withObject "Map k v" $
1895+ FromJSONKeyTextParser f -> withObject "Map" $
1460 H.foldrWithKey (\k v m -> M.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure M.empty)
1896 H.foldrWithKey (\k v m -> M.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure M.empty)
1461− FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1897+ FromJSONKeyValue f -> withArray "Map" $ \arr ->
1462− M.fromList <$> (Tr.sequence .
1898+ fmap M.fromList . Tr.sequence .
1463− zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr)
1899+ zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1464 {-# INLINE liftParseJSON #-}
1900 {-# INLINE liftParseJSON #-}
1465
1901
1466instance (FromJSONKey k, Ord k, FromJSON v) => FromJSON (M.Map k v) where
1902instance (FromJSONKey k, Ord k, FromJSON v) => FromJSON (M.Map k v) where
@@ -1480,12 +1916,24 @@ instance (FromJSON v) => FromJSON (Tree.Tree v) where
1480 parseJSON = parseJSON1
1916 parseJSON = parseJSON1
1481 {-# INLINE parseJSON #-}
1917 {-# INLINE parseJSON #-}
1482
1918
1919+-------------------------------------------------------------------------------
1920+-- uuid
1921+-------------------------------------------------------------------------------
1922+
1923+instance FromJSON UUID.UUID where
1924+ parseJSON = withText "UUID" $
1925+ maybe (fail "invalid UUID") pure . UUID.fromText
1926+
1927+instance FromJSONKey UUID.UUID where
1928+ fromJSONKey = FromJSONKeyTextParser $
1929+ maybe (fail "invalid UUID") pure . UUID.fromText
1930+
1483-------------------------------------------------------------------------------
1931-------------------------------------------------------------------------------
1484-- vector
1932-- vector
1485-------------------------------------------------------------------------------
1933-------------------------------------------------------------------------------
1486
1934
1487instance FromJSON1 Vector where
1935instance FromJSON1 Vector where
1488− liftParseJSON p _ = withArray "Vector a" $
1936+ liftParseJSON p _ = withArray "Vector" $
1489 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1937 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1490 {-# INLINE liftParseJSON #-}
1938 {-# INLINE liftParseJSON #-}
1491
1939
@@ -1499,14 +1947,14 @@ vectorParseJSON s = withArray s $ fmap V.convert . V.mapM (uncurry $ parseIndexe
1499{-# INLINE vectorParseJSON #-}
1947{-# INLINE vectorParseJSON #-}
1500
1948
1501instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1949instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1502− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
1950+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector"
1503
1951
1504instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1952instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1505− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
1953+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector"
1506 {-# INLINE parseJSON #-}
1954 {-# INLINE parseJSON #-}
1507
1955
1508instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
1956instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
1509− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
1957+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector"
1510 {-# INLINE parseJSON #-}
1958 {-# INLINE parseJSON #-}
1511
1959
1512-------------------------------------------------------------------------------
1960-------------------------------------------------------------------------------
@@ -1520,15 +1968,15 @@ instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
1520
1968
1521instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1969instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1522 liftParseJSON p _ = case fromJSONKey of
1970 liftParseJSON p _ = case fromJSONKey of
1523− FromJSONKeyCoerce _ -> withObject "HashMap ~Text v" $
1971+ FromJSONKeyCoerce _ -> withObject "HashMap ~Text" $
1524 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1972 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1525− FromJSONKeyText f -> withObject "HashMap k v" $
1973+ FromJSONKeyText f -> withObject "HashMap" $
1526 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1974 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1527− FromJSONKeyTextParser f -> withObject "HashMap k v" $
1975+ FromJSONKeyTextParser f -> withObject "HashMap" $
1528 H.foldrWithKey (\k v m -> H.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure H.empty)
1976 H.foldrWithKey (\k v m -> H.insert <$> f k <?> Key k <*> p v <?> Key k <*> m) (pure H.empty)
1529− FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1977+ FromJSONKeyValue f -> withArray "Map" $ \arr ->
1530− H.fromList <$> (Tr.sequence .
1978+ fmap H.fromList . Tr.sequence .
1531− zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr)
1979+ zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1532 where
1980 where
1533 uc :: Parser (H.HashMap Text v) -> Parser (H.HashMap k v)
1981 uc :: Parser (H.HashMap Text v) -> Parser (H.HashMap k v)
1534 uc = unsafeCoerce
1982 uc = unsafeCoerce
@@ -1549,11 +1997,33 @@ instance FromJSON DotNetTime where
1549 parseJSON = withText "DotNetTime" $ \t ->
1997 parseJSON = withText "DotNetTime" $ \t ->
1550 let (s,m) = T.splitAt (T.length t - 5) t
1998 let (s,m) = T.splitAt (T.length t - 5) t
1551 t' = T.concat [s,".",m]
1999 t' = T.concat [s,".",m]
1552− in case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
2000+ in case parseTimeM True defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
1553 Just d -> pure (DotNetTime d)
2001 Just d -> pure (DotNetTime d)
1554 _ -> fail "could not parse .NET time"
2002 _ -> fail "could not parse .NET time"
1555 {-# INLINE parseJSON #-}
2003 {-# INLINE parseJSON #-}
1556
2004
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+
1557-------------------------------------------------------------------------------
2027-------------------------------------------------------------------------------
1558-- time
2028-- time
1559-------------------------------------------------------------------------------
2029-------------------------------------------------------------------------------
@@ -1579,6 +2049,17 @@ instance FromJSONKey LocalTime where
1579 fromJSONKey = FromJSONKeyTextParser (Time.run Time.localTime)
2049 fromJSONKey = FromJSONKeyTextParser (Time.run Time.localTime)
1580
2050
1581
2051
2052+-- | Supported string formats:
2053+--
2054+-- @YYYY-MM-DD HH:MM Z@
2055+-- @YYYY-MM-DD HH:MM:SS Z@
2056+-- @YYYY-MM-DD HH:MM:SS.SSS Z@
2057+--
2058+-- The first space may instead be a @T@, and the second space is
2059+-- optional. The @Z@ represents UTC. The @Z@ may be replaced with a
2060+-- time zone offset of the form @+0000@ or @-08:00@, where the first
2061+-- two digits are hours, the @:@ is optional and the second two digits
2062+-- (also optional) are minutes.
1582instance FromJSON ZonedTime where
2063instance FromJSON ZonedTime where
1583 parseJSON = withText "ZonedTime" (Time.run Time.zonedTime)
2064 parseJSON = withText "ZonedTime" (Time.run Time.zonedTime)
1584
2065
@@ -1593,41 +2074,168 @@ instance FromJSONKey UTCTime where
1593 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
2074 fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
1594
2075
1595
2076
1596−-- | /WARNING:/ Only parse lengths of time from trusted input
2077+-- | This instance includes a bounds check to prevent maliciously
1597−-- since an attacker could easily fill up the memory of the target
2078+-- large inputs to fill up the memory of the target system. You can
1598−-- system by specifying a scientific number with a big exponent like
2079+-- newtype 'Scientific' and provide your own instance using
1599−-- @1e1000000000@.
2080+-- 'withScientific' if you want to allow larger inputs.
1600instance FromJSON NominalDiffTime where
2081instance FromJSON NominalDiffTime where
1601− parseJSON = withScientific "NominalDiffTime" $ pure . realToFrac
2082+ parseJSON = withBoundedScientific "NominalDiffTime" $ pure . realToFrac
1602 {-# INLINE parseJSON #-}
2083 {-# INLINE parseJSON #-}
1603
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
2092+ {-# INLINE parseJSON #-}
2093+
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+
1604-------------------------------------------------------------------------------
2128-------------------------------------------------------------------------------
1605-- base Monoid/Semigroup
2129-- base Monoid/Semigroup
1606-------------------------------------------------------------------------------
2130-------------------------------------------------------------------------------
1607
2131
1608−instance FromJSON1 Dual where
2132+instance FromJSON1 Monoid.Dual where
1609− liftParseJSON p _ = fmap Dual . p
2133+ liftParseJSON p _ = fmap Monoid.Dual . p
2134+ {-# INLINE liftParseJSON #-}
2135+
2136+instance FromJSON a => FromJSON (Monoid.Dual a) where
2137+ parseJSON = parseJSON1
2138+ {-# INLINE parseJSON #-}
2139+
2140+
2141+instance FromJSON1 Monoid.First where
2142+ liftParseJSON p p' = fmap Monoid.First . liftParseJSON p p'
2143+ {-# INLINE liftParseJSON #-}
2144+
2145+instance FromJSON a => FromJSON (Monoid.First a) where
2146+ parseJSON = parseJSON1
2147+ {-# INLINE parseJSON #-}
2148+
2149+
2150+instance FromJSON1 Monoid.Last where
2151+ liftParseJSON p p' = fmap Monoid.Last . liftParseJSON p p'
2152+ {-# INLINE liftParseJSON #-}
2153+
2154+instance FromJSON a => FromJSON (Monoid.Last a) where
2155+ parseJSON = parseJSON1
2156+ {-# INLINE parseJSON #-}
2157+
2158+
2159+instance FromJSON1 Semigroup.Min where
2160+ liftParseJSON p _ a = Semigroup.Min <$> p a
2161+ {-# INLINE liftParseJSON #-}
2162+
2163+ liftParseJSONList _ p a = fmap Semigroup.Min <$> p a
2164+ {-# INLINE liftParseJSONList #-}
2165+
2166+instance (FromJSON a) => FromJSON (Semigroup.Min a) where
2167+ parseJSON = parseJSON1
2168+ {-# INLINE parseJSON #-}
2169+
2170+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2171+ {-# INLINE parseJSONList #-}
2172+
2173+
2174+instance FromJSON1 Semigroup.Max where
2175+ liftParseJSON p _ a = Semigroup.Max <$> p a
2176+ {-# INLINE liftParseJSON #-}
2177+
2178+ liftParseJSONList _ p a = fmap Semigroup.Max <$> p a
2179+ {-# INLINE liftParseJSONList #-}
2180+
2181+instance (FromJSON a) => FromJSON (Semigroup.Max a) where
2182+ parseJSON = parseJSON1
2183+ {-# INLINE parseJSON #-}
2184+
2185+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2186+ {-# INLINE parseJSONList #-}
2187+
2188+
2189+instance FromJSON1 Semigroup.First where
2190+ liftParseJSON p _ a = Semigroup.First <$> p a
1610 {-# INLINE liftParseJSON #-}
2191 {-# INLINE liftParseJSON #-}
1611
2192
2193+ liftParseJSONList _ p a = fmap Semigroup.First <$> p a
2194+ {-# INLINE liftParseJSONList #-}
2195+
1612−instance FromJSON a => FromJSON (Dual a) where
2196+instance (FromJSON a) => FromJSON (Semigroup.First a) where
1613 parseJSON = parseJSON1
2197 parseJSON = parseJSON1
1614 {-# INLINE parseJSON #-}
2198 {-# INLINE parseJSON #-}
1615
2199
2200+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2201+ {-# INLINE parseJSONList #-}
2202+
1616
2203
1617−instance FromJSON1 First where
2204+instance FromJSON1 Semigroup.Last where
1618− liftParseJSON p p' = fmap First . liftParseJSON p p'
2205+ liftParseJSON p _ a = Semigroup.Last <$> p a
1619 {-# INLINE liftParseJSON #-}
2206 {-# INLINE liftParseJSON #-}
1620
2207
2208+ liftParseJSONList _ p a = fmap Semigroup.Last <$> p a
2209+ {-# INLINE liftParseJSONList #-}
2210+
1621−instance FromJSON a => FromJSON (First a) where
2211+instance (FromJSON a) => FromJSON (Semigroup.Last a) where
1622 parseJSON = parseJSON1
2212 parseJSON = parseJSON1
1623 {-# INLINE parseJSON #-}
2213 {-# INLINE parseJSON #-}
1624
2214
2215+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2216+ {-# INLINE parseJSONList #-}
1625
2217
2218+
2219+instance FromJSON1 Semigroup.WrappedMonoid where
2220+ liftParseJSON p _ a = Semigroup.WrapMonoid <$> p a
2221+ {-# INLINE liftParseJSON #-}
2222+
2223+ liftParseJSONList _ p a = fmap Semigroup.WrapMonoid <$> p a
2224+ {-# INLINE liftParseJSONList #-}
2225+
2226+instance (FromJSON a) => FromJSON (Semigroup.WrappedMonoid a) where
2227+ parseJSON = parseJSON1
2228+ {-# INLINE parseJSON #-}
2229+
2230+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2231+ {-# INLINE parseJSONList #-}
2232+
2233+
1626−instance FromJSON1 Last where
2234+instance FromJSON1 Semigroup.Option where
1627− liftParseJSON p p' = fmap Last . liftParseJSON p p'
2235+ liftParseJSON p p' = fmap Semigroup.Option . liftParseJSON p p'
1628 {-# INLINE liftParseJSON #-}
2236 {-# INLINE liftParseJSON #-}
1629
2237
1630−instance FromJSON a => FromJSON (Last a) where
2238+instance FromJSON a => FromJSON (Semigroup.Option a) where
1631 parseJSON = parseJSON1
2239 parseJSON = parseJSON1
1632 {-# INLINE parseJSON #-}
2240 {-# INLINE parseJSON #-}
1633
2241
@@ -1635,11 +2243,21 @@ instance FromJSON a => FromJSON (Last a) where
1635-- tagged
2243-- tagged
1636-------------------------------------------------------------------------------
2244-------------------------------------------------------------------------------
1637
2245
2246+instance FromJSON1 Proxy where
2247+ {-# INLINE liftParseJSON #-}
2248+ liftParseJSON _ _ = fromNull "Proxy" Proxy
2249+
1638instance FromJSON (Proxy a) where
2250instance FromJSON (Proxy a) where
1639 {-# INLINE parseJSON #-}
2251 {-# INLINE parseJSON #-}
1640− parseJSON Null = pure Proxy
2252+ parseJSON = fromNull "Proxy" Proxy
1641− parseJSON v = typeMismatch "Proxy" v
1642
2253
2254+fromNull :: String -> a -> Value -> Parser a
2255+fromNull _ a Null = pure a
2256+fromNull c _ v = prependContext c (typeMismatch "Null" v)
2257+
2258+instance FromJSON2 Tagged where
2259+ liftParseJSON2 _ _ p _ = fmap Tagged . p
2260+ {-# INLINE liftParseJSON2 #-}
1643
2261
1644instance FromJSON1 (Tagged a) where
2262instance FromJSON1 (Tagged a) where
1645 liftParseJSON p _ = fmap Tagged . p
2263 liftParseJSON p _ = fmap Tagged . p
@@ -1662,10 +2280,7 @@ instance (FromJSON a, FromJSON b, FromJSON c) => FromJSONKey (a,b,c)
1662instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSONKey (a,b,c,d)
2280instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSONKey (a,b,c,d)
1663
2281
1664instance FromJSONKey Char where
2282instance FromJSONKey Char where
1665− fromJSONKey = FromJSONKeyTextParser $ \t ->
2283+ fromJSONKey = FromJSONKeyTextParser parseChar
1666− if T.length t == 1
1667− then return (T.index t 0)
1668− else typeMismatch "Expected Char but String didn't contain exactly one character" (String t)
1669 fromJSONKeyList = FromJSONKeyText T.unpack
2284 fromJSONKeyList = FromJSONKeyText T.unpack
1670
2285
1671instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
2286instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
Data/Aeson/Types/Generic.hs+12−8
@@ -1,10 +1,11 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DeriveFunctor #-}
3{-# LANGUAGE EmptyDataDecls #-}
4{-# LANGUAGE EmptyDataDecls #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE KindSignatures #-}
7{-# LANGUAGE KindSignatures #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoImplicitPrelude #-}
8{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeOperators #-}
10{-# LANGUAGE TypeOperators #-}
10{-# LANGUAGE UndecidableInstances #-}
11{-# LANGUAGE UndecidableInstances #-}
@@ -25,7 +26,7 @@
25
26
26module Data.Aeson.Types.Generic
27module Data.Aeson.Types.Generic
27 (
28 (
28− IsRecord(..)
29+ IsRecord
29 , AllNullary
30 , AllNullary
30 , Tagged2(..)
31 , Tagged2(..)
31 , True
32 , True
@@ -34,9 +35,9 @@ module Data.Aeson.Types.Generic
34 , Zero
35 , Zero
35 , One
36 , One
36 , ProductSize(..)
37 , ProductSize(..)
38+ , (:*)(..)
37 ) where
39 ) where
38
40
39−import Prelude ()
40import Prelude.Compat
41import Prelude.Compat
41
42
42import GHC.Generics
43import GHC.Generics
@@ -44,12 +45,8 @@ import GHC.Generics
44--------------------------------------------------------------------------------
45--------------------------------------------------------------------------------
45
46
46class IsRecord (f :: * -> *) isRecord | f -> isRecord
47class IsRecord (f :: * -> *) isRecord | f -> isRecord
47− where
48− isUnary :: f a -> Bool
49− isUnary = const True
50
48
51instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
49instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
52− where isUnary = const False
53#if MIN_VERSION_base(4,9,0)
50#if MIN_VERSION_base(4,9,0)
54instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
51instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
55#else
52#else
@@ -61,7 +58,6 @@ instance IsRecord Par1 True
61instance IsRecord (Rec1 f) True
58instance IsRecord (Rec1 f) True
62instance IsRecord (f :.: g) True
59instance IsRecord (f :.: g) True
63instance IsRecord U1 False
60instance IsRecord U1 False
64− where isUnary = const False
65
61
66--------------------------------------------------------------------------------
62--------------------------------------------------------------------------------
67
63
@@ -80,6 +76,7 @@ instance AllNullary (Rec1 f) False
80instance AllNullary U1 True
76instance AllNullary U1 True
81
77
82newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
78newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
79+ deriving Functor
83
80
84--------------------------------------------------------------------------------
81--------------------------------------------------------------------------------
85
82
@@ -112,3 +109,10 @@ instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
112
109
113instance ProductSize (S1 s a) where
110instance ProductSize (S1 s a) where
114 productSize = Tagged2 1
111 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+175−26
@@ -1,8 +1,9 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE Rank2Types #-}
6{-# LANGUAGE Rank2Types #-}
5−{-# LANGUAGE RecordWildCards #-}
6#if __GLASGOW_HASKELL__ >= 800
7#if __GLASGOW_HASKELL__ >= 800
7-- a) THQ works on cross-compilers and unregisterised GHCs
8-- a) THQ works on cross-compilers and unregisterised GHCs
8-- b) may make compilation faster as no dynamic loading is ever needed (not sure about this)
9-- b) may make compilation faster as no dynamic loading is ever needed (not sure about this)
@@ -44,16 +45,33 @@ module Data.Aeson.Types.Internal
44 , parseEither
45 , parseEither
45 , parseMaybe
46 , parseMaybe
46 , modifyFailure
47 , modifyFailure
48+ , prependFailure
49+ , parserThrowError
50+ , parserCatchError
47 , formatError
51 , formatError
52+ , formatPath
53+ , formatRelativePath
48 , (<?>)
54 , (<?>)
49 -- * Constructors and accessors
55 -- * Constructors and accessors
50 , object
56 , object
51
57
52 -- * Generic and TH encoding configuration
58 -- * Generic and TH encoding configuration
53− , Options(..)
59+ , Options(
60+ fieldLabelModifier
61+ , constructorTagModifier
62+ , allNullaryToStringTag
63+ , omitNothingFields
64+ , sumEncoding
65+ , unwrapUnaryRecords
66+ , tagSingleConstructors
67+ , rejectUnknownFields
68+ )
69+
54 , SumEncoding(..)
70 , SumEncoding(..)
71+ , JSONKeyOptions(keyModifier)
55 , defaultOptions
72 , defaultOptions
56 , defaultTaggedObject
73 , defaultTaggedObject
74+ , defaultJSONKeyOptions
57
75
58 -- * Used for changing CamelCase names into something else.
76 -- * Used for changing CamelCase names into something else.
59 , camelTo
77 , camelTo
@@ -63,7 +81,6 @@ module Data.Aeson.Types.Internal
63 , DotNetTime(..)
81 , DotNetTime(..)
64 ) where
82 ) where
65
83
66−import Prelude ()
67import Prelude.Compat
84import Prelude.Compat
68
85
69import Control.Applicative (Alternative(..))
86import Control.Applicative (Alternative(..))
@@ -75,6 +92,7 @@ import Data.Data (Data)
75import Data.Foldable (foldl')
92import Data.Foldable (foldl')
76import Data.HashMap.Strict (HashMap)
93import Data.HashMap.Strict (HashMap)
77import Data.Hashable (Hashable(..))
94import Data.Hashable (Hashable(..))
95+import Data.List (intercalate)
78import Data.Scientific (Scientific)
96import Data.Scientific (Scientific)
79import Data.Semigroup (Semigroup((<>)))
97import Data.Semigroup (Semigroup((<>)))
80import Data.String (IsString(..))
98import Data.String (IsString(..))
@@ -83,6 +101,8 @@ import Data.Time (UTCTime)
83import Data.Time.Format (FormatTime)
101import Data.Time.Format (FormatTime)
84import Data.Typeable (Typeable)
102import Data.Typeable (Typeable)
85import Data.Vector (Vector)
103import Data.Vector (Vector)
104+import GHC.Generics (Generic)
105+import qualified Control.Monad as Monad
86import qualified Control.Monad.Fail as Fail
106import qualified Control.Monad.Fail as Fail
87import qualified Data.HashMap.Strict as H
107import qualified Data.HashMap.Strict as H
88import qualified Data.Scientific as S
108import qualified Data.Scientific as S
@@ -101,7 +121,7 @@ data JSONPathElement = Key Text
101 | Index {-# UNPACK #-} !Int
121 | Index {-# UNPACK #-} !Int
102 -- ^ JSON path element of an index into an
122 -- ^ JSON path element of an index into an
103 -- array, \"array[index]\".
123 -- array, \"array[index]\".
104− deriving (Eq, Show, Typeable)
124+ deriving (Eq, Show, Typeable, Ord)
105type JSONPath = [JSONPathElement]
125type JSONPath = [JSONPathElement]
106
126
107-- | The internal result of running a 'Parser'.
127-- | The internal result of running a 'Parser'.
@@ -136,7 +156,7 @@ instance Functor Result where
136 fmap _ (Error err) = Error err
156 fmap _ (Error err) = Error err
137 {-# INLINE fmap #-}
157 {-# INLINE fmap #-}
138
158
139−instance Monad IResult where
159+instance Monad.Monad IResult where
140 return = pure
160 return = pure
141 {-# INLINE return #-}
161 {-# INLINE return #-}
142
162
@@ -144,14 +164,16 @@ instance Monad IResult where
144 IError path err >>= _ = IError path err
164 IError path err >>= _ = IError path err
145 {-# INLINE (>>=) #-}
165 {-# INLINE (>>=) #-}
146
166
167+#if !(MIN_VERSION_base(4,13,0))
147 fail = Fail.fail
168 fail = Fail.fail
148 {-# INLINE fail #-}
169 {-# INLINE fail #-}
170+#endif
149
171
150instance Fail.MonadFail IResult where
172instance Fail.MonadFail IResult where
151 fail err = IError [] err
173 fail err = IError [] err
152 {-# INLINE fail #-}
174 {-# INLINE fail #-}
153
175
154−instance Monad Result where
176+instance Monad.Monad Result where
155 return = pure
177 return = pure
156 {-# INLINE return #-}
178 {-# INLINE return #-}
157
179
@@ -159,8 +181,10 @@ instance Monad Result where
159 Error err >>= _ = Error err
181 Error err >>= _ = Error err
160 {-# INLINE (>>=) #-}
182 {-# INLINE (>>=) #-}
161
183
184+#if !(MIN_VERSION_base(4,13,0))
162 fail = Fail.fail
185 fail = Fail.fail
163 {-# INLINE fail #-}
186 {-# INLINE fail #-}
187+#endif
164
188
165instance Fail.MonadFail Result where
189instance Fail.MonadFail Result where
166 fail err = Error err
190 fail err = Error err
@@ -257,7 +281,9 @@ type Failure f r = JSONPath -> String -> f r
257-- | Success continuation.
281-- | Success continuation.
258type Success a f r = a -> f r
282type Success a f r = a -> f r
259
283
260−-- | A JSON parser.
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.
261newtype Parser a = Parser {
287newtype Parser a = Parser {
262 runParser :: forall f r.
288 runParser :: forall f r.
263 JSONPath
289 JSONPath
@@ -266,14 +292,17 @@ newtype Parser a = Parser {
266 -> f r
292 -> f r
267 }
293 }
268
294
269−instance Monad Parser where
295+instance Monad.Monad Parser where
270 m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
296 m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
271 in runParser m path kf ks'
297 in runParser m path kf ks'
272 {-# INLINE (>>=) #-}
298 {-# INLINE (>>=) #-}
273 return = pure
299 return = pure
274 {-# INLINE return #-}
300 {-# INLINE return #-}
301+
302+#if !(MIN_VERSION_base(4,13,0))
275 fail = Fail.fail
303 fail = Fail.fail
276 {-# INLINE fail #-}
304 {-# INLINE fail #-}
305+#endif
277
306
278instance Fail.MonadFail Parser where
307instance Fail.MonadFail Parser where
279 fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
308 fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
@@ -316,8 +345,7 @@ instance Monoid (Parser a) where
316apP :: Parser (a -> b) -> Parser a -> Parser b
345apP :: Parser (a -> b) -> Parser a -> Parser b
317apP d e = do
346apP d e = do
318 b <- d
347 b <- d
319− a <- e
348+ b <$> e
320− return (b a)
321{-# INLINE apP #-}
349{-# INLINE apP #-}
322
350
323-- | A JSON \"object\" (key\/value map).
351-- | A JSON \"object\" (key\/value map).
@@ -333,7 +361,7 @@ data Value = Object !Object
333 | Number !Scientific
361 | Number !Scientific
334 | Bool !Bool
362 | Bool !Bool
335 | Null
363 | Null
336− deriving (Eq, Read, Show, Typeable, Data)
364+ deriving (Eq, Read, Show, Typeable, Data, Generic)
337
365
338-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
366-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
339-- serialization format as Microsoft .NET, whose
367-- serialization format as Microsoft .NET, whose
@@ -433,7 +461,17 @@ parseEither m v = runParser (m v) [] onError Right
433-- | Annotate an error message with a
461-- | Annotate an error message with a
434-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
462-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
435formatError :: JSONPath -> String -> String
463formatError :: JSONPath -> String -> String
436−formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
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
437 where
475 where
438 format :: String -> JSONPath -> String
476 format :: String -> JSONPath -> String
439 format pfx [] = pfx
477 format pfx [] = pfx
@@ -496,13 +534,40 @@ p <?> pathElem = Parser $ \path kf ks -> runParser p (pathElem:path) kf ks
496--
534--
497-- Since 0.6.2.0
535-- Since 0.6.2.0
498modifyFailure :: (String -> String) -> Parser a -> Parser a
536modifyFailure :: (String -> String) -> Parser a -> Parser a
499−modifyFailure f (Parser p) = Parser $ \path kf ks -> p path (\p' m -> kf p' (f m)) ks
537+modifyFailure f (Parser p) = Parser $ \path kf ks ->
538+ p path (\p' m -> kf p' (f m)) ks
539+
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+
549+-- | Throw a parser error with an additional path.
550+--
551+-- @since 1.2.1.0
552+parserThrowError :: JSONPath -> String -> Parser a
553+parserThrowError path' msg = Parser $ \path kf _ks ->
554+ kf (reverse path ++ path') msg
555+
556+-- | A handler function to handle previous errors and return to normal execution.
557+--
558+-- @since 1.2.1.0
559+parserCatchError :: Parser a -> (JSONPath -> String -> Parser a) -> Parser a
560+parserCatchError (Parser p) handler = Parser $ \path kf ks ->
561+ p path (\e msg -> runParser (handler e msg) path kf ks) ks
500
562
501--------------------------------------------------------------------------------
563--------------------------------------------------------------------------------
502-- Generic and TH encoding configuration
564-- Generic and TH encoding configuration
503--------------------------------------------------------------------------------
565--------------------------------------------------------------------------------
504
566
505-- | Options that specify how to encode\/decode your datatype to\/from JSON.
567-- | Options that specify how to encode\/decode your datatype to\/from JSON.
568+--
569+-- Options can be set using record syntax on 'defaultOptions' with the fields
570+-- below.
506data Options = Options
571data Options = Options
507 { fieldLabelModifier :: String -> String
572 { fieldLabelModifier :: String -> String
508 -- ^ Function applied to field labels.
573 -- ^ Function applied to field labels.
@@ -519,24 +584,67 @@ data Options = Options
519 -- ^ If 'True' record fields with a 'Nothing' value will be
584 -- ^ If 'True' record fields with a 'Nothing' value will be
520 -- omitted from the resulting object. If 'False' the resulting
585 -- omitted from the resulting object. If 'False' the resulting
521 -- object will include those fields mapping to @null@.
586 -- 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+ -- @
522 , sumEncoding :: SumEncoding
620 , sumEncoding :: SumEncoding
523 -- ^ Specifies how to encode constructors of a sum datatype.
621 -- ^ Specifies how to encode constructors of a sum datatype.
524 , unwrapUnaryRecords :: Bool
622 , unwrapUnaryRecords :: Bool
525 -- ^ Hide the field name when a record constructor has only one
623 -- ^ Hide the field name when a record constructor has only one
526 -- field, like a newtype.
624 -- field, like a newtype.
625+ , tagSingleConstructors :: Bool
626+ -- ^ Encode types with a single constructor as sums,
627+ -- 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.
527 }
632 }
528
633
529instance Show Options where
634instance Show Options where
530− show Options{..} = "Options {" ++
635+ show (Options f c a o s u t r) =
636+ "Options {"
637+ ++ intercalate ", "
531− "fieldLabelModifier =~ " ++
638+ [ "fieldLabelModifier =~ " ++ show (f "exampleField")
532− show (fieldLabelModifier "exampleField") ++ ", " ++
533− "constructorTagModifier =~ " ++
639+ , "constructorTagModifier =~ " ++ show (c "ExampleConstructor")
534− show (constructorTagModifier "ExampleConstructor") ++ ", " ++
535− "allNullaryToStringTag = " ++ show allNullaryToStringTag ++ ", " ++
640+ , "allNullaryToStringTag = " ++ show a
536− "omitNothingFields = " ++ show omitNothingFields ++ ", " ++
641+ , "omitNothingFields = " ++ show o
537− "sumEncoding = " ++ show sumEncoding ++ ", " ++
642+ , "sumEncoding = " ++ show s
538− "unwrapUnaryRecords = " ++ show unwrapUnaryRecords ++
643+ , "unwrapUnaryRecords = " ++ show u
644+ , "tagSingleConstructors = " ++ show t
645+ , "rejectUnknownFields = " ++ show r
646+ ]
539− "}"
647+ ++ "}"
540
648
541-- | Specifies how to encode constructors of a sum datatype.
649-- | Specifies how to encode constructors of a sum datatype.
542data SumEncoding =
650data SumEncoding =
@@ -554,18 +662,18 @@ data SumEncoding =
554 -- the 'contentsFieldName' field.
662 -- the 'contentsFieldName' field.
555 | UntaggedValue
663 | UntaggedValue
556 -- ^ Constructor names won't be encoded. Instead only the contents of the
664 -- ^ Constructor names won't be encoded. Instead only the contents of the
557− -- constructor will be encoded as if the type had single constructor. JSON
665+ -- constructor will be encoded as if the type had a single constructor. JSON
558 -- encodings have to be disjoint for decoding to work properly.
666 -- encodings have to be disjoint for decoding to work properly.
559 --
667 --
560 -- When decoding, constructors are tried in the order of definition. If some
668 -- When decoding, constructors are tried in the order of definition. If some
561 -- encodings overlap, the first one defined will succeed.
669 -- encodings overlap, the first one defined will succeed.
562 --
670 --
563− -- /Note:/ Nullary constructors are encoded as the string (using
671+ -- /Note:/ Nullary constructors are encoded as strings (using
564 -- 'constructorTagModifier'). Having a nullary constructor alongside a
672 -- 'constructorTagModifier'). Having a nullary constructor alongside a
565 -- single field constructor that encodes to a string leads to ambiguity.
673 -- single field constructor that encodes to a string leads to ambiguity.
566 --
674 --
567 -- /Note:/ Only the last error is kept when decoding, so in the case of
675 -- /Note:/ Only the last error is kept when decoding, so in the case of
568− -- mailformed JSON, only an error for the last constructor will be reported.
676+ -- malformed JSON, only an error for the last constructor will be reported.
569 | ObjectWithSingleField
677 | ObjectWithSingleField
570 -- ^ A constructor will be encoded to an object with a single
678 -- ^ A constructor will be encoded to an object with a single
571 -- field named after the constructor tag (modified by the
679 -- field named after the constructor tag (modified by the
@@ -578,6 +686,33 @@ data SumEncoding =
578 -- contents of the constructor.
686 -- contents of the constructor.
579 deriving (Eq, Show)
687 deriving (Eq, Show)
580
688
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+
581-- | Default encoding 'Options':
716-- | Default encoding 'Options':
582--
717--
583-- @
718-- @
@@ -588,6 +723,8 @@ data SumEncoding =
588-- , 'omitNothingFields' = False
723-- , 'omitNothingFields' = False
589-- , 'sumEncoding' = 'defaultTaggedObject'
724-- , 'sumEncoding' = 'defaultTaggedObject'
590-- , 'unwrapUnaryRecords' = False
725-- , 'unwrapUnaryRecords' = False
726+-- , 'tagSingleConstructors' = False
727+-- , 'rejectUnknownFields' = False
591-- }
728-- }
592-- @
729-- @
593defaultOptions :: Options
730defaultOptions :: Options
@@ -598,6 +735,8 @@ defaultOptions = Options
598 , omitNothingFields = False
735 , omitNothingFields = False
599 , sumEncoding = defaultTaggedObject
736 , sumEncoding = defaultTaggedObject
600 , unwrapUnaryRecords = False
737 , unwrapUnaryRecords = False
738+ , tagSingleConstructors = False
739+ , rejectUnknownFields = False
601 }
740 }
602
741
603-- | Default 'TaggedObject' 'SumEncoding' options:
742-- | Default 'TaggedObject' 'SumEncoding' options:
@@ -614,6 +753,16 @@ defaultTaggedObject = TaggedObject
614 , contentsFieldName = "contents"
753 , contentsFieldName = "contents"
615 }
754 }
616
755
756+-- | Default 'JSONKeyOptions':
757+--
758+-- @
759+-- defaultJSONKeyOptions = 'JSONKeyOptions'
760+-- { 'keyModifier' = 'id'
761+-- }
762+-- @
763+defaultJSONKeyOptions :: JSONKeyOptions
764+defaultJSONKeyOptions = JSONKeyOptions id
765+
617-- | Converts from CamelCase to another lower case, interspersing
766-- | Converts from CamelCase to another lower case, interspersing
618-- the character between all capital letters and their previous
767-- the character between all capital letters and their previous
619-- entries, except those capital letters that appear together,
768-- entries, except those capital letters that appear together,
Data/Aeson/Types/ToJSON.hs+884−695
@@ -1,25 +1,21 @@
1−{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
3−{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE EmptyDataDecls #-}
5{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE GADTs #-}
7{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoImplicitPrelude #-}
9−{-# LANGUAGE NamedFieldPuns #-}
10{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
11{-# LANGUAGE PatternGuards #-}
10{-# LANGUAGE PatternGuards #-}
11+{-# LANGUAGE PolyKinds #-}
12{-# LANGUAGE RecordWildCards #-}
12{-# LANGUAGE RecordWildCards #-}
13{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE TypeSynonymInstances #-}
16{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
17
16
18−#if __GLASGOW_HASKELL__ >= 706
19−{-# LANGUAGE PolyKinds #-}
20−#endif
21−
22#include "overlapping-compat.h"
17#include "overlapping-compat.h"
18+#include "incoherent-compat.h"
23
19
24-- TODO: Drop this when we remove support for Data.Attoparsec.Number
20-- TODO: Drop this when we remove support for Data.Attoparsec.Number
25{-# OPTIONS_GHC -fno-warn-deprecations #-}
21{-# OPTIONS_GHC -fno-warn-deprecations #-}
@@ -37,7 +33,6 @@ module Data.Aeson.Types.ToJSON
37 , toEncoding2
33 , toEncoding2
38 -- * Generic JSON classes
34 -- * Generic JSON classes
39 , GToJSON(..)
35 , GToJSON(..)
40− , GToEncoding(..)
41 , ToArgs(..)
36 , ToArgs(..)
42 , genericToJSON
37 , genericToJSON
43 , genericToEncoding
38 , genericToEncoding
@@ -48,55 +43,63 @@ module Data.Aeson.Types.ToJSON
48 , ToJSONKeyFunction(..)
43 , ToJSONKeyFunction(..)
49 , toJSONKeyText
44 , toJSONKeyText
50 , contramapToJSONKeyFunction
45 , contramapToJSONKeyFunction
46+
47+ , GToJSONKey()
48+ , genericToJSONKey
49+
51 -- * Object key-value pairs
50 -- * Object key-value pairs
52 , KeyValue(..)
51 , KeyValue(..)
52+ , KeyValuePair(..)
53+ , FromPairs(..)
53 -- * Functions needed for documentation
54 -- * Functions needed for documentation
54 -- * Encoding functions
55 -- * Encoding functions
55 , listEncoding
56 , listEncoding
56 , listValue
57 , listValue
57 ) where
58 ) where
58
59
59−import Prelude ()
60import Prelude.Compat
60import Prelude.Compat
61
61
62import Control.Applicative (Const(..))
62import Control.Applicative (Const(..))
63import Control.Monad.ST (ST)
63import Control.Monad.ST (ST)
64import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
64import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
65−import Data.Aeson.Encoding.Internal ((>*<), (><), tuple)
65+import Data.Aeson.Encoding.Internal ((>*<))
66import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
66import 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)
68import Data.Aeson.Types.Internal
68import Data.Aeson.Types.Internal
69import Data.Attoparsec.Number (Number(..))
69import Data.Attoparsec.Number (Number(..))
70import Data.Bits (unsafeShiftR)
70import Data.Bits (unsafeShiftR)
71import Data.DList (DList)
71import Data.DList (DList)
72−import Data.Fixed (Fixed, HasResolution)
72+import Data.Fixed (Fixed, HasResolution, Nano)
73import Data.Foldable (toList)
73import Data.Foldable (toList)
74import Data.Functor.Compose (Compose(..))
74import Data.Functor.Compose (Compose(..))
75+import Data.Functor.Contravariant (Contravariant (..))
75import Data.Functor.Identity (Identity(..))
76import Data.Functor.Identity (Identity(..))
76import Data.Functor.Product (Product(..))
77import Data.Functor.Product (Product(..))
77import Data.Functor.Sum (Sum(..))
78import Data.Functor.Sum (Sum(..))
78import Data.Int (Int16, Int32, Int64, Int8)
79import Data.Int (Int16, Int32, Int64, Int8)
79import Data.List (intersperse)
80import Data.List (intersperse)
80import Data.List.NonEmpty (NonEmpty(..))
81import Data.List.NonEmpty (NonEmpty(..))
81−import Data.Monoid ((<>), Dual(..), First(..), Last(..))
82+import Data.Semigroup ((<>))
82import Data.Proxy (Proxy(..))
83import Data.Proxy (Proxy(..))
83import Data.Ratio (Ratio, denominator, numerator)
84import Data.Ratio (Ratio, denominator, numerator)
84import Data.Scientific (Scientific)
85import Data.Scientific (Scientific)
85import Data.Tagged (Tagged(..))
86import Data.Tagged (Tagged(..))
86import Data.Text (Text, pack)
87import Data.Text (Text, pack)
87−import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
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 (..))
88−import Data.Time.Format (FormatTime, formatTime)
92+import Data.Time.Format.Compat (FormatTime, formatTime, defaultTimeLocale)
89−import Data.Time.Locale.Compat (defaultTimeLocale)
90import Data.Vector (Vector)
93import Data.Vector (Vector)
91import Data.Version (Version, showVersion)
94import Data.Version (Version, showVersion)
95+import Data.Void (Void, absurd)
92import Data.Word (Word16, Word32, Word64, Word8)
96import Data.Word (Word16, Word32, Word64, Word8)
93import Foreign.Storable (Storable)
97import Foreign.Storable (Storable)
98+import Foreign.C.Types (CTime (..))
94import GHC.Generics
99import GHC.Generics
95import Numeric.Natural (Natural)
100import Numeric.Natural (Natural)
96import qualified Data.Aeson.Encoding as E
101import qualified Data.Aeson.Encoding as E
97−-- We need internal here for generic deriving
98−import qualified Data.Aeson.Encoding.Internal as E (InArray, closeCurly, colon, comma, econcat, empty, openCurly, retagEncoding, tuple, wrapObject)
102+import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
99−import qualified Data.ByteString as S
100import qualified Data.ByteString.Lazy as L
103import qualified Data.ByteString.Lazy as L
101import qualified Data.DList as DList
104import qualified Data.DList as DList
102import qualified Data.HashMap.Strict as H
105import qualified Data.HashMap.Strict as H
@@ -105,13 +108,16 @@ import qualified Data.IntMap as IntMap
105import qualified Data.IntSet as IntSet
108import qualified Data.IntSet as IntSet
106import qualified Data.List.NonEmpty as NE
109import qualified Data.List.NonEmpty as NE
107import qualified Data.Map as M
110import qualified Data.Map as M
111+import qualified Data.Monoid as Monoid
108import qualified Data.Scientific as Scientific
112import qualified Data.Scientific as Scientific
113+import qualified Data.Semigroup as Semigroup
109import qualified Data.Sequence as Seq
114import qualified Data.Sequence as Seq
110import qualified Data.Set as Set
115import qualified Data.Set as Set
111import qualified Data.Text as T
116import qualified Data.Text as T
112import qualified Data.Text.Encoding as T
117import qualified Data.Text.Encoding as T
113import qualified Data.Text.Lazy as LT
118import qualified Data.Text.Lazy as LT
114import qualified Data.Tree as Tree
119import qualified Data.Tree as Tree
120+import qualified Data.UUID.Types as UUID
115import qualified Data.Vector as V
121import qualified Data.Vector as V
116import qualified Data.Vector.Generic as VG
122import qualified Data.Vector.Generic as VG
117import qualified Data.Vector.Mutable as VM
123import qualified Data.Vector.Mutable as VM
@@ -119,12 +125,19 @@ import qualified Data.Vector.Primitive as VP
119import qualified Data.Vector.Storable as VS
125import qualified Data.Vector.Storable as VS
120import qualified Data.Vector.Unboxed as VU
126import qualified Data.Vector.Unboxed as VU
121
127
122−#if !(MIN_VERSION_bytestring(0,10,0))
128+import qualified Data.Aeson.Encoding.Builder as EB
123−import Foreign.ForeignPtr (withForeignPtr)
129+import qualified Data.ByteString.Builder as B
124−import Foreign.Marshal.Utils (copyBytes)
130+
125−import Foreign.Ptr (plusPtr)
131+import qualified GHC.Exts as Exts
126−import qualified Data.ByteString.Internal as S
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
127−import qualified Data.ByteString.Lazy.Internal as L
140+import qualified Data.Primitive.PrimArray as PM
128#endif
141#endif
129
142
130toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
143toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
@@ -143,19 +156,16 @@ realFloatToJSON d
143
156
144-- | Class of generic representation types that can be converted to
157-- | Class of generic representation types that can be converted to
145-- JSON.
158-- JSON.
146−class GToJSON arity f where
159+class GToJSON enc arity f where
147 -- | This method (applied to 'defaultOptions') is used as the
160 -- | This method (applied to 'defaultOptions') is used as the
148− -- default generic implementation of 'toJSON' (if the @arity@ is 'Zero')
161+ -- default generic implementation of 'toJSON'
162+ -- (with @enc ~ 'Value'@ and @arity ~ 'Zero'@)
149− -- or 'liftToJSON' (if the @arity@ is 'One').
163+ -- and 'liftToJSON' (if the @arity@ is 'One').
150− gToJSON :: Options -> ToArgs Value arity a -> f a -> Value
164+ --
151−
152−-- | Class of generic representation types that can be converted to
153−-- a JSON 'Encoding'.
154−class GToEncoding arity f where
155− -- | This method (applied to 'defaultOptions') can be used as the
156− -- default generic implementation of 'toEncoding' (if the @arity@ is 'Zero')
165+ -- It also provides a generic implementation of 'toEncoding'
166+ -- (with @enc ~ 'Encoding'@ and @arity ~ 'Zero'@)
157− -- or 'liftToEncoding' (if the @arity@ is 'One').
167+ -- and 'liftToEncoding' (if the @arity@ is 'One').
158− gToEncoding :: Options -> ToArgs Encoding arity a -> f a -> Encoding
168+ gToJSON :: Options -> ToArgs enc arity a -> f a -> enc
159
169
160-- | A 'ToArgs' value either stores nothing (for 'ToJSON') or it stores the two
170-- | A 'ToArgs' value either stores nothing (for 'ToJSON') or it stores the two
161-- function arguments that encode occurrences of the type parameter (for
171-- function arguments that encode occurrences of the type parameter (for
@@ -167,14 +177,14 @@ data ToArgs res arity a where
167-- | A configurable generic JSON creator. This function applied to
177-- | A configurable generic JSON creator. This function applied to
168-- 'defaultOptions' is used as the default for 'toJSON' when the type
178-- 'defaultOptions' is used as the default for 'toJSON' when the type
169-- is an instance of 'Generic'.
179-- is an instance of 'Generic'.
170−genericToJSON :: (Generic a, GToJSON Zero (Rep a))
180+genericToJSON :: (Generic a, GToJSON Value Zero (Rep a))
171 => Options -> a -> Value
181 => Options -> a -> Value
172genericToJSON opts = gToJSON opts NoToArgs . from
182genericToJSON opts = gToJSON opts NoToArgs . from
173
183
174-- | A configurable generic JSON creator. This function applied to
184-- | A configurable generic JSON creator. This function applied to
175-- 'defaultOptions' is used as the default for 'liftToJSON' when the type
185-- 'defaultOptions' is used as the default for 'liftToJSON' when the type
176-- is an instance of 'Generic1'.
186-- is an instance of 'Generic1'.
177−genericLiftToJSON :: (Generic1 f, GToJSON One (Rep1 f))
187+genericLiftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))
178 => Options -> (a -> Value) -> ([a] -> Value)
188 => Options -> (a -> Value) -> ([a] -> Value)
179 -> f a -> Value
189 -> f a -> Value
180genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1
190genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1
@@ -182,17 +192,17 @@ genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1
182-- | A configurable generic JSON encoder. This function applied to
192-- | A configurable generic JSON encoder. This function applied to
183-- 'defaultOptions' is used as the default for 'toEncoding' when the type
193-- 'defaultOptions' is used as the default for 'toEncoding' when the type
184-- is an instance of 'Generic'.
194-- is an instance of 'Generic'.
185−genericToEncoding :: (Generic a, GToEncoding Zero (Rep a))
195+genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a))
186 => Options -> a -> Encoding
196 => Options -> a -> Encoding
187−genericToEncoding opts = gToEncoding opts NoToArgs . from
197+genericToEncoding opts = gToJSON opts NoToArgs . from
188
198
189-- | A configurable generic JSON encoder. This function applied to
199-- | A configurable generic JSON encoder. This function applied to
190-- 'defaultOptions' is used as the default for 'liftToEncoding' when the type
200-- 'defaultOptions' is used as the default for 'liftToEncoding' when the type
191-- is an instance of 'Generic1'.
201-- is an instance of 'Generic1'.
192−genericLiftToEncoding :: (Generic1 f, GToEncoding One (Rep1 f))
202+genericLiftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))
193 => Options -> (a -> Encoding) -> ([a] -> Encoding)
203 => Options -> (a -> Encoding) -> ([a] -> Encoding)
194 -> f a -> Encoding
204 -> f a -> Encoding
195−genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
205+genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1
196
206
197-------------------------------------------------------------------------------
207-------------------------------------------------------------------------------
198-- Class
208-- Class
@@ -200,6 +210,9 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
200
210
201-- | A type that can be converted to JSON.
211-- | A type that can be converted to JSON.
202--
212--
213+-- Instances in general /must/ specify 'toJSON' and /should/ (but don't need
214+-- to) specify 'toEncoding'.
215+--
203-- An example type and instance:
216-- An example type and instance:
204--
217--
205-- @
218-- @
@@ -208,10 +221,10 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
208--
221--
209-- data Coord = Coord { x :: Double, y :: Double }
222-- data Coord = Coord { x :: Double, y :: Double }
210--
223--
211−-- instance ToJSON Coord where
224+-- instance 'ToJSON' Coord where
212−-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
225+-- 'toJSON' (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
213--
226--
214−-- toEncoding (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
227+-- 'toEncoding' (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
215-- @
228-- @
216--
229--
217-- Instead of manually writing your 'ToJSON' instance, there are two options
230-- Instead of manually writing your 'ToJSON' instance, there are two options
@@ -219,17 +232,15 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
219--
232--
220-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
233-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
221-- instance at compile time. The generated instance is optimized for your type
234-- instance at compile time. The generated instance is optimized for your type
222−-- so will probably be more efficient than the following two options:
235+-- so it will probably be more efficient than the following option.
223--
236--
224-- * The compiler can provide a default generic implementation for
237-- * The compiler can provide a default generic implementation for
225-- 'toJSON'.
238-- 'toJSON'.
226--
239--
227-- To use the second, simply add a @deriving 'Generic'@ clause to your
240-- To use the second, simply add a @deriving 'Generic'@ clause to your
228−-- datatype and declare a 'ToJSON' instance for your datatype without giving
241+-- datatype and declare a 'ToJSON' instance. If you require nothing other than
229−-- definitions for 'toJSON' or 'toEncoding'.
242+-- 'defaultOptions', it is sufficient to write (and this is the only
230−--
243+-- alternative where the default 'toJSON' implementation is sufficient):
231−-- For example, the previous example can be simplified to a more
232−-- minimal instance:
233--
244--
234-- @
245-- @
235-- {-\# LANGUAGE DeriveGeneric \#-}
246-- {-\# LANGUAGE DeriveGeneric \#-}
@@ -238,35 +249,46 @@ genericLiftToEncoding opts te tel = gToEncoding opts (To1Args te tel) . from1
238--
249--
239-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
250-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
240--
251--
241−-- instance ToJSON Coord where
252+-- instance 'ToJSON' Coord where
242−-- toEncoding = 'genericToEncoding' 'defaultOptions'
253+-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
243-- @
254-- @
244--
255--
245−-- Why do we provide an implementation for 'toEncoding' here? The
256+-- If on the other hand you wish to customize the generic decoding, you have
246−-- 'toEncoding' function is a relatively new addition to this class.
257+-- to implement both methods:
247−-- To allow users of older versions of this library to upgrade without
248−-- having to edit all of their instances or encounter surprising
249−-- incompatibilities, the default implementation of 'toEncoding' uses
250−-- 'toJSON'. This produces correct results, but since it performs an
251−-- intermediate conversion to a 'Value', it will be less efficient
252−-- than directly emitting an 'Encoding'. Our one-liner definition of
253−-- 'toEncoding' above bypasses the intermediate 'Value'.
254−--
255−-- If @DefaultSignatures@ doesn't give exactly the results you want,
256−-- you can customize the generic encoding with only a tiny amount of
257−-- effort, using 'genericToJSON' and 'genericToEncoding' with your
258−-- preferred 'Options':
259--
258--
260-- @
259-- @
260+-- customOptions = 'defaultOptions'
261+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
262+-- }
263+--
261−-- instance ToJSON Coord where
264+-- instance 'ToJSON' Coord where
262−-- toJSON = 'genericToJSON' 'defaultOptions'
265+-- 'toJSON' = 'genericToJSON' customOptions
263−-- toEncoding = 'genericToEncoding' 'defaultOptions'
266+-- 'toEncoding' = 'genericToEncoding' customOptions
264-- @
267-- @
268+--
269+-- Previous versions of this library only had the 'toJSON' method. Adding
270+-- 'toEncoding' had two reasons:
271+--
272+-- 1. toEncoding is more efficient for the common case that the output of
273+-- 'toJSON' is directly serialized to a @ByteString@.
274+-- Further, expressing either method in terms of the other would be
275+-- non-optimal.
276+--
277+-- 2. The choice of defaults allows a smooth transition for existing users:
278+-- Existing instances that do not define 'toEncoding' still
279+-- compile and have the correct semantics. This is ensured by making
280+-- the default implementation of 'toEncoding' use 'toJSON'. This produces
281+-- correct results, but since it performs an intermediate conversion to a
282+-- 'Value', it will be less efficient than directly emitting an 'Encoding'.
283+-- (this also means that specifying nothing more than
284+-- @instance ToJSON Coord@ would be sufficient as a generically decoding
285+-- instance, but there probably exists no good reason to not specify
286+-- 'toEncoding' in new instances.)
265class ToJSON a where
287class ToJSON a where
266 -- | Convert a Haskell value to a JSON-friendly intermediate type.
288 -- | Convert a Haskell value to a JSON-friendly intermediate type.
267 toJSON :: a -> Value
289 toJSON :: a -> Value
268
290
269− default toJSON :: (Generic a, GToJSON Zero (Rep a)) => a -> Value
291+ default toJSON :: (Generic a, GToJSON Value Zero (Rep a)) => a -> Value
270 toJSON = genericToJSON defaultOptions
292 toJSON = genericToJSON defaultOptions
271
293
272 -- | Encode a Haskell value as JSON.
294 -- | Encode a Haskell value as JSON.
@@ -283,8 +305,8 @@ class ToJSON a where
283 -- extension, and then have GHC generate a method body as follows.
305 -- extension, and then have GHC generate a method body as follows.
284 --
306 --
285 -- @
307 -- @
286− -- instance ToJSON Coord where
308+ -- instance 'ToJSON' Coord where
287− -- toEncoding = 'genericToEncoding' 'defaultOptions'
309+ -- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
288 -- @
310 -- @
289
311
290 toEncoding :: a -> Encoding
312 toEncoding :: a -> Encoding
@@ -316,6 +338,13 @@ instance KeyValue Pair where
316 name .= value = (name, toJSON value)
338 name .= value = (name, toJSON value)
317 {-# INLINE (.=) #-}
339 {-# INLINE (.=) #-}
318
340
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+
319-------------------------------------------------------------------------------
348-------------------------------------------------------------------------------
320-- Classes and types for map keys
349-- Classes and types for map keys
321-------------------------------------------------------------------------------
350-------------------------------------------------------------------------------
@@ -373,7 +402,7 @@ instance KeyValue Pair where
373-- newtype wrapper around 'Text'. The recommended approach is to use
402-- newtype wrapper around 'Text'. The recommended approach is to use
374-- generalized newtype deriving:
403-- generalized newtype deriving:
375--
404--
376−-- > newtype RecordId = RecordId { getRecordId :: Text}
405+-- > newtype RecordId = RecordId { getRecordId :: Text }
377-- > deriving (Eq,Ord,ToJSONKey)
406-- > deriving (Eq,Ord,ToJSONKey)
378--
407--
379-- Then we may write:
408-- Then we may write:
@@ -388,8 +417,18 @@ instance KeyValue Pair where
388--
417--
389-- It is possible to get the 'ToJSONKey' instance for free as we did
418-- It is possible to get the 'ToJSONKey' instance for free as we did
390-- with 'Foo'. However, in this case, we have a natural way to go to
419-- with 'Foo'. However, in this case, we have a natural way to go to
391−-- and from 'Text' that does not require any escape sequences. So, in
420+-- and from 'Text' that does not require any escape sequences. So
392−-- this example, 'ToJSONKeyText' will be used instead of 'ToJSONKeyValue'.
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+--
393-- The 'Show' instance can be used to help write 'ToJSONKey':
432-- The 'Show' instance can be used to help write 'ToJSONKey':
394--
433--
395-- > instance ToJSONKey Color where
434-- > instance ToJSONKey Color where
@@ -464,16 +503,42 @@ toJSONKeyTextEnc e = ToJSONKeyText tot e
464 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
503 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
465 tot = T.dropAround (== '"')
504 tot = T.dropAround (== '"')
466 . T.decodeLatin1
505 . T.decodeLatin1
467− . lazyToStrictByteString
506+ . L.toStrict
468 . E.encodingToLazyByteString
507 . E.encodingToLazyByteString
469 . e
508 . e
470
509
510+instance Contravariant ToJSONKeyFunction where
511+ contramap = contramapToJSONKeyFunction
512+
471-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
513-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
472contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
514contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
473contramapToJSONKeyFunction h x = case x of
515contramapToJSONKeyFunction h x = case x of
474 ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
516 ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
475 ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
517 ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
476
518
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+
477-------------------------------------------------------------------------------
542-------------------------------------------------------------------------------
478-- Lifings of FromJSON and ToJSON to unary and binary type constructors
543-- Lifings of FromJSON and ToJSON to unary and binary type constructors
479-------------------------------------------------------------------------------
544-------------------------------------------------------------------------------
@@ -486,7 +551,7 @@ contramapToJSONKeyFunction h x = case x of
486--
551--
487-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
552-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
488-- instance at compile time. The generated instance is optimized for your type
553-- instance at compile time. The generated instance is optimized for your type
489−-- so will probably be more efficient than the following two options:
554+-- so it will probably be more efficient than the following option.
490--
555--
491-- * The compiler can provide a default generic implementation for
556-- * The compiler can provide a default generic implementation for
492-- 'toJSON1'.
557-- 'toJSON1'.
@@ -504,23 +569,29 @@ contramapToJSONKeyFunction h x = case x of
504--
569--
505-- data Pair = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
570-- data Pair = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
506--
571--
507−-- instance ToJSON a => ToJSON1 (Pair a)
572+-- instance 'ToJSON' a => 'ToJSON1' (Pair a)
508-- @
573-- @
509--
574--
510−-- If @DefaultSignatures@ doesn't give exactly the results you want,
575+-- If the default implementation doesn't give exactly the results you want,
511-- you can customize the generic encoding with only a tiny amount of
576-- you can customize the generic encoding with only a tiny amount of
512-- effort, using 'genericLiftToJSON' and 'genericLiftToEncoding' with
577-- effort, using 'genericLiftToJSON' and 'genericLiftToEncoding' with
513-- your preferred 'Options':
578-- your preferred 'Options':
514--
579--
515-- @
580-- @
581+-- customOptions = 'defaultOptions'
582+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
583+-- }
584+--
516−-- instance ToJSON a => ToJSON1 (Pair a) where
585+-- instance 'ToJSON' a => 'ToJSON1' (Pair a) where
517−-- liftToJSON = 'genericLiftToJSON' 'defaultOptions'
586+-- 'liftToJSON' = 'genericLiftToJSON' customOptions
518−-- liftToEncoding = 'genericLiftToEncoding' 'defaultOptions'
587+-- 'liftToEncoding' = 'genericLiftToEncoding' customOptions
519-- @
588-- @
589+--
590+-- See also 'ToJSON'.
520class ToJSON1 f where
591class ToJSON1 f where
521 liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value
592 liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value
522
593
523− default liftToJSON :: (Generic1 f, GToJSON One (Rep1 f))
594+ default liftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))
524 => (a -> Value) -> ([a] -> Value) -> f a -> Value
595 => (a -> Value) -> ([a] -> Value) -> f a -> Value
525 liftToJSON = genericLiftToJSON defaultOptions
596 liftToJSON = genericLiftToJSON defaultOptions
526
597
@@ -529,7 +600,7 @@ class ToJSON1 f where
529
600
530 liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
601 liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
531
602
532− default liftToEncoding :: (Generic1 f, GToEncoding One (Rep1 f))
603+ default liftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))
533 => (a -> Encoding) -> ([a] -> Encoding)
604 => (a -> Encoding) -> ([a] -> Encoding)
534 -> f a -> Encoding
605 -> f a -> Encoding
535 liftToEncoding = genericLiftToEncoding defaultOptions
606 liftToEncoding = genericLiftToEncoding defaultOptions
@@ -583,7 +654,7 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
583-- @
654-- @
584-- newtype F a = F [a]
655-- newtype F a = F [a]
585--
656--
586−-- -- This instance encodes String as an array of chars
657+-- -- This instance encodes 'String' as an array of chars
587-- instance 'ToJSON1' F where
658-- instance 'ToJSON1' F where
588-- 'liftToJSON' tj _ (F xs) = 'liftToJSON' tj ('listValue' tj) xs
659-- 'liftToJSON' tj _ (F xs) = 'liftToJSON' tj ('listValue' tj) xs
589-- 'liftToEncoding' te _ (F xs) = 'liftToEncoding' te ('listEncoding' te) xs
660-- 'liftToEncoding' te _ (F xs) = 'liftToEncoding' te ('listEncoding' te) xs
@@ -614,6 +685,11 @@ instance ToJSON1 [] where
614 {-# INLINE liftToEncoding #-}
685 {-# INLINE liftToEncoding #-}
615
686
616instance (ToJSON a) => ToJSON [a] where
687instance (ToJSON a) => ToJSON [a] where
688+ {-# SPECIALIZE instance ToJSON String #-}
689+ {-# SPECIALIZE instance ToJSON [String] #-}
690+ {-# SPECIALIZE instance ToJSON [Array] #-}
691+ {-# SPECIALIZE instance ToJSON [Object] #-}
692+
617 toJSON = toJSON1
693 toJSON = toJSON1
618 {-# INLINE toJSON #-}
694 {-# INLINE toJSON #-}
619
695
@@ -624,39 +700,80 @@ instance (ToJSON a) => ToJSON [a] where
624-- Generic toJSON / toEncoding
700-- Generic toJSON / toEncoding
625-------------------------------------------------------------------------------
701-------------------------------------------------------------------------------
626
702
627−--------------------------------------------------------------------------------
628−-- Generic toJSON
629−
630−instance OVERLAPPABLE_ (GToJSON arity a) => GToJSON arity (M1 i c a) where
703+instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where
631 -- Meta-information, which is not handled elsewhere, is ignored:
704 -- Meta-information, which is not handled elsewhere, is ignored:
632 gToJSON opts targs = gToJSON opts targs . unM1
705 gToJSON opts targs = gToJSON opts targs . unM1
706+ {-# INLINE gToJSON #-}
633
707
634−instance (ToJSON a) => GToJSON arity (K1 i a) where
708+instance GToJSON enc One Par1 where
635− -- Constant values are encoded using their ToJSON instance:
636− gToJSON _opts _ = toJSON . unK1
637−
638−instance GToJSON One Par1 where
639 -- Direct occurrences of the last type parameter are encoded with the
709 -- Direct occurrences of the last type parameter are encoded with the
640 -- function passed in as an argument:
710 -- function passed in as an argument:
641 gToJSON _opts (To1Args tj _) = tj . unPar1
711 gToJSON _opts (To1Args tj _) = tj . unPar1
712+ {-# INLINE gToJSON #-}
713+
714+instance ( ConsToJSON enc arity a
715+ , AllNullary (C1 c a) allNullary
716+ , SumToJSON enc arity (C1 c a) allNullary
717+ ) => GToJSON enc arity (D1 d (C1 c a)) where
718+ -- The option 'tagSingleConstructors' determines whether to wrap
719+ -- a single-constructor type.
720+ gToJSON opts targs
721+ | tagSingleConstructors opts = (unTagged :: Tagged allNullary enc -> enc)
722+ . sumToJSON opts targs
723+ . unM1
724+ | otherwise = consToJSON opts targs . unM1 . unM1
725+ {-# INLINE gToJSON #-}
726+
727+instance (ConsToJSON enc arity a) => GToJSON enc arity (C1 c a) where
728+ -- Constructors need to be encoded differently depending on whether they're
729+ -- a record or not. This distinction is made by 'consToJSON':
730+ gToJSON opts targs = consToJSON opts targs . unM1
731+ {-# INLINE gToJSON #-}
642
732
733+instance ( AllNullary (a :+: b) allNullary
734+ , SumToJSON enc arity (a :+: b) allNullary
735+ ) => GToJSON enc arity (a :+: b)
736+ where
737+ -- If all constructors of a sum datatype are nullary and the
738+ -- 'allNullaryToStringTag' option is set they are encoded to
739+ -- strings. This distinction is made by 'sumToJSON':
740+ gToJSON opts targs = (unTagged :: Tagged allNullary enc -> enc)
741+ . sumToJSON opts targs
742+ {-# INLINE gToJSON #-}
743+
744+--------------------------------------------------------------------------------
745+-- Generic toJSON
746+
747+-- Note: Refactoring 'ToJSON a' to 'ToJSON enc a' (and 'ToJSON1' similarly) is
748+-- possible but makes error messages a bit harder to understand for missing
749+-- instances.
750+
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+
757+instance ToJSON a => GToJSON Value arity (K1 i a) where
758+ -- Constant values are encoded using their ToJSON instance:
759+ gToJSON _opts _ = toJSON . unK1
760+ {-# INLINE gToJSON #-}
761+
643−instance (ToJSON1 f) => GToJSON One (Rec1 f) where
762+instance ToJSON1 f => GToJSON Value One (Rec1 f) where
644 -- Recursive occurrences of the last type parameter are encoded using their
763 -- Recursive occurrences of the last type parameter are encoded using their
645 -- ToJSON1 instance:
764 -- ToJSON1 instance:
646 gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
765 gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
766+ {-# INLINE gToJSON #-}
647
767
648−instance GToJSON arity U1 where
768+instance GToJSON Value arity U1 where
649 -- Empty constructors are encoded to an empty array:
769 -- Empty constructors are encoded to an empty array:
650 gToJSON _opts _ _ = emptyArray
770 gToJSON _opts _ _ = emptyArray
651−
771+ {-# INLINE gToJSON #-}
652−instance (ConsToJSON arity a) => GToJSON arity (C1 c a) where
653− -- Constructors need to be encoded differently depending on whether they're
654− -- a record or not. This distinction is made by 'consToJSON':
655− gToJSON opts targs = consToJSON opts targs . unM1
656
772
657instance ( WriteProduct arity a, WriteProduct arity b
773instance ( WriteProduct arity a, WriteProduct arity b
658 , ProductSize a, ProductSize b
774 , ProductSize a, ProductSize b
659− ) => GToJSON arity (a :*: b) where
775+ ) => GToJSON Value arity (a :*: b)
776+ where
660 -- Products are encoded to an array. Here we allocate a mutable vector of
777 -- Products are encoded to an array. Here we allocate a mutable vector of
661 -- the same size as the product and write the product's elements to it using
778 -- the same size as the product and write the product's elements to it using
662 -- 'writeProduct':
779 -- 'writeProduct':
@@ -668,238 +785,175 @@ instance ( WriteProduct arity a, WriteProduct arity b
668 where
785 where
669 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
786 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
670 productSize
787 productSize
788+ {-# INLINE gToJSON #-}
671
789
672−instance ( AllNullary (a :+: b) allNullary
790+instance ( ToJSON1 f
673− , SumToJSON arity (a :+: b) allNullary
791+ , GToJSON Value One g
674− ) => GToJSON arity (a :+: b) where
792+ ) => GToJSON Value One (f :.: g)
675− -- If all constructors of a sum datatype are nullary and the
793+ where
676− -- 'allNullaryToStringTag' option is set they are encoded to
677− -- strings. This distinction is made by 'sumToJSON':
678− gToJSON opts targs = (unTagged :: Tagged allNullary Value -> Value)
679− . sumToJSON opts targs
680−
681−instance (ToJSON1 f, GToJSON One g) => GToJSON One (f :.: g) where
682 -- If an occurrence of the last type parameter is nested inside two
794 -- If an occurrence of the last type parameter is nested inside two
683 -- composed types, it is encoded by using the outermost type's ToJSON1
795 -- composed types, it is encoded by using the outermost type's ToJSON1
684 -- instance to generically encode the innermost type:
796 -- instance to generically encode the innermost type:
685 gToJSON opts targs =
797 gToJSON opts targs =
686 let gtj = gToJSON opts targs in
798 let gtj = gToJSON opts targs in
687 liftToJSON gtj (listValue gtj) . unComp1
799 liftToJSON gtj (listValue gtj) . unComp1
800+ {-# INLINE gToJSON #-}
688
801
689--------------------------------------------------------------------------------
802--------------------------------------------------------------------------------
690-- Generic toEncoding
803-- Generic toEncoding
691
804
692−instance OVERLAPPABLE_ (GToEncoding arity a) => GToEncoding arity (M1 i c a) where
693− -- Meta-information, which is not handled elsewhere, is ignored:
694− gToEncoding opts targs = gToEncoding opts targs . unM1
695−
696−instance (ToJSON a) => GToEncoding arity (K1 i a) where
805+instance ToJSON a => GToJSON Encoding arity (K1 i a) where
697 -- Constant values are encoded using their ToJSON instance:
806 -- Constant values are encoded using their ToJSON instance:
698− gToEncoding _opts _ = toEncoding . unK1
807+ gToJSON _opts _ = toEncoding . unK1
699−
808+ {-# INLINE gToJSON #-}
700−instance GToEncoding One Par1 where
701− -- Direct occurrences of the last type parameter are encoded with the
702− -- function passed in as an argument:
703− gToEncoding _opts (To1Args te _) = te . unPar1
704
809
705−instance (ToJSON1 f) => GToEncoding One (Rec1 f) where
810+instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where
706 -- Recursive occurrences of the last type parameter are encoded using their
811 -- Recursive occurrences of the last type parameter are encoded using their
707 -- ToEncoding1 instance:
812 -- ToEncoding1 instance:
708− gToEncoding _opts (To1Args te tel) = liftToEncoding te tel . unRec1
813+ gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1
814+ {-# INLINE gToJSON #-}
709
815
710−instance GToEncoding arity U1 where
816+instance GToJSON Encoding arity U1 where
711 -- Empty constructors are encoded to an empty array:
817 -- Empty constructors are encoded to an empty array:
712− gToEncoding _opts _ _ = E.emptyArray_
818+ gToJSON _opts _ _ = E.emptyArray_
713−
819+ {-# INLINE gToJSON #-}
714−instance (ConsToEncoding arity a) => GToEncoding arity (C1 c a) where
715− -- Constructors need to be encoded differently depending on whether they're
716− -- a record or not. This distinction is made by 'consToEncoding':
717− gToEncoding opts targs = consToEncoding opts targs . unM1
718
820
719instance ( EncodeProduct arity a
821instance ( EncodeProduct arity a
720 , EncodeProduct arity b
822 , EncodeProduct arity b
721− ) => GToEncoding arity (a :*: b) where
823+ ) => GToJSON Encoding arity (a :*: b)
824+ where
722 -- Products are encoded to an array. Here we allocate a mutable vector of
825 -- Products are encoded to an array. Here we allocate a mutable vector of
723 -- the same size as the product and write the product's elements to it using
826 -- the same size as the product and write the product's elements to it using
724 -- 'encodeProduct':
827 -- 'encodeProduct':
725− gToEncoding opts targs p = E.tuple $ encodeProduct opts targs p
828+ gToJSON opts targs p = E.list E.retagEncoding [encodeProduct opts targs p]
829+ {-# INLINE gToJSON #-}
726
830
727−instance ( AllNullary (a :+: b) allNullary
831+instance ( ToJSON1 f
728− , SumToEncoding arity (a :+: b) allNullary
832+ , GToJSON Encoding One g
729− ) => GToEncoding arity (a :+: b) where
833+ ) => GToJSON Encoding One (f :.: g)
730− -- If all constructors of a sum datatype are nullary and the
834+ where
731− -- 'allNullaryToStringTag' option is set they are encoded to
732− -- strings. This distinction is made by 'sumToEncoding':
733− gToEncoding opts targs
734− = (unTagged :: Tagged allNullary Encoding -> Encoding)
735− . sumToEncoding opts targs
736−
737−instance (ToJSON1 f, GToEncoding One g) => GToEncoding One (f :.: g) where
738 -- If an occurrence of the last type parameter is nested inside two
835 -- If an occurrence of the last type parameter is nested inside two
739 -- composed types, it is encoded by using the outermost type's ToJSON1
836 -- composed types, it is encoded by using the outermost type's ToJSON1
740 -- instance to generically encode the innermost type:
837 -- instance to generically encode the innermost type:
741− gToEncoding opts targs =
838+ gToJSON opts targs =
742− let gte = gToEncoding opts targs in
839+ let gte = gToJSON opts targs in
743 liftToEncoding gte (listEncoding gte) . unComp1
840 liftToEncoding gte (listEncoding gte) . unComp1
841+ {-# INLINE gToJSON #-}
744
842
745--------------------------------------------------------------------------------
843--------------------------------------------------------------------------------
746
844
747−class SumToJSON arity f allNullary where
845+class SumToJSON enc arity f allNullary where
748− sumToJSON :: Options -> ToArgs Value arity a
846+ sumToJSON :: Options -> ToArgs enc arity a
749− -> f a -> Tagged allNullary Value
847+ -> f a -> Tagged allNullary enc
750−
848+
751−instance ( GetConName f
849+instance ( GetConName f
752− , TaggedObjectPairs arity f
850+ , FromString enc
753− , ObjectWithSingleFieldObj arity f
851+ , TaggedObject enc arity f
754− , TwoElemArrayObj arity f
852+ , SumToJSON' ObjectWithSingleField enc arity f
755− , UntaggedValueObj arity f
853+ , SumToJSON' TwoElemArray enc arity f
854+ , SumToJSON' UntaggedValue enc arity f
756− ) => SumToJSON arity f True where
855+ ) => SumToJSON enc arity f True
856+ where
757 sumToJSON opts targs
857 sumToJSON opts targs
758− | allNullaryToStringTag opts = Tagged . String . pack
858+ | allNullaryToStringTag opts = Tagged . fromString
759 . constructorTagModifier opts . getConName
859 . constructorTagModifier opts . getConName
760 | otherwise = Tagged . nonAllNullarySumToJSON opts targs
860 | otherwise = Tagged . nonAllNullarySumToJSON opts targs
761
861
762−instance ( TwoElemArrayObj arity f
862+instance ( TaggedObject enc arity f
763− , TaggedObjectPairs arity f
863+ , SumToJSON' ObjectWithSingleField enc arity f
764− , ObjectWithSingleFieldObj arity f
864+ , SumToJSON' TwoElemArray enc arity f
765− , UntaggedValueObj arity f
865+ , SumToJSON' UntaggedValue enc arity f
766− ) => SumToJSON arity f False where
866+ ) => SumToJSON enc arity f False
867+ where
767 sumToJSON opts targs = Tagged . nonAllNullarySumToJSON opts targs
868 sumToJSON opts targs = Tagged . nonAllNullarySumToJSON opts targs
768
869
769−nonAllNullarySumToJSON :: ( TwoElemArrayObj arity f
870+nonAllNullarySumToJSON :: ( TaggedObject enc arity f
770− , TaggedObjectPairs arity f
871+ , SumToJSON' ObjectWithSingleField enc arity f
771− , ObjectWithSingleFieldObj arity f
872+ , SumToJSON' TwoElemArray enc arity f
772− , UntaggedValueObj arity f
873+ , SumToJSON' UntaggedValue enc arity f
773− ) => Options -> ToArgs Value arity a
874+ ) => Options -> ToArgs enc arity a
774− -> f a -> Value
875+ -> f a -> enc
775nonAllNullarySumToJSON opts targs =
876nonAllNullarySumToJSON opts targs =
776 case sumEncoding opts of
877 case sumEncoding opts of
777− TaggedObject{..} ->
778− object . taggedObjectPairs opts targs tagFieldName contentsFieldName
779− ObjectWithSingleField -> Object . objectWithSingleFieldObj opts targs
780− TwoElemArray -> Array . twoElemArrayObj opts targs
781− UntaggedValue -> untaggedValueObj opts targs
782−
783−--------------------------------------------------------------------------------
784
878
785−class SumToEncoding arity f allNullary where
786− sumToEncoding :: Options -> ToArgs Encoding arity a
787− -> f a -> Tagged allNullary Encoding
788−
789−instance ( GetConName f
790− , TaggedObjectEnc arity f
791− , ObjectWithSingleFieldEnc arity f
792− , TwoElemArrayEnc arity f
793− , UntaggedValueEnc arity f
794− ) => SumToEncoding arity f True where
795− sumToEncoding opts targs
796− | allNullaryToStringTag opts = Tagged . toEncoding .
797− constructorTagModifier opts . getConName
798− | otherwise = Tagged . nonAllNullarySumToEncoding opts targs
799−
800−instance ( TwoElemArrayEnc arity f
801− , TaggedObjectEnc arity f
802− , ObjectWithSingleFieldEnc arity f
803− , UntaggedValueEnc arity f
804− ) => SumToEncoding arity f False where
805− sumToEncoding opts targs = Tagged . nonAllNullarySumToEncoding opts targs
806−
807−nonAllNullarySumToEncoding :: ( TwoElemArrayEnc arity f
808− , TaggedObjectEnc arity f
809− , ObjectWithSingleFieldEnc arity f
810− , UntaggedValueEnc arity f
811− ) => Options -> ToArgs Encoding arity a
812− -> f a -> Encoding
813−nonAllNullarySumToEncoding opts targs =
814− case sumEncoding opts of
815 TaggedObject{..} ->
879 TaggedObject{..} ->
816− taggedObjectEnc opts targs tagFieldName contentsFieldName
880+ taggedObject opts targs tagFieldName contentsFieldName
817− ObjectWithSingleField -> objectWithSingleFieldEnc opts targs
818− TwoElemArray -> twoElemArrayEnc opts targs
819− UntaggedValue -> untaggedValueEnc opts targs
820
881
821−--------------------------------------------------------------------------------
882+ ObjectWithSingleField ->
883+ (unTagged :: Tagged ObjectWithSingleField enc -> enc)
884+ . sumToJSON' opts targs
822
885
823−class TaggedObjectPairs arity f where
886+ TwoElemArray ->
824− taggedObjectPairs :: Options -> ToArgs Value arity a
825− -> String -> String
826− -> f a -> [Pair]
827−
828−instance ( TaggedObjectPairs arity a
829− , TaggedObjectPairs arity b
830− ) => TaggedObjectPairs arity (a :+: b) where
831− taggedObjectPairs opts targs tagFieldName contentsFieldName (L1 x) =
832− taggedObjectPairs opts targs tagFieldName contentsFieldName x
833− taggedObjectPairs opts targs tagFieldName contentsFieldName (R1 x) =
834− taggedObjectPairs opts targs tagFieldName contentsFieldName x
835−
836−instance ( IsRecord a isRecord
837− , TaggedObjectPairs' arity a isRecord
838− , Constructor c
839− ) => TaggedObjectPairs arity (C1 c a) where
840− taggedObjectPairs opts targs tagFieldName contentsFieldName =
841− (pack tagFieldName .= constructorTagModifier opts
842− (conName (undefined :: t c a p)) :) .
843− (unTagged :: Tagged isRecord [Pair] -> [Pair]) .
887+ (unTagged :: Tagged TwoElemArray enc -> enc)
844− taggedObjectPairs' opts targs contentsFieldName . unM1
888+ . sumToJSON' opts targs
845
889
846−class TaggedObjectPairs' arity f isRecord where
890+ UntaggedValue ->
847− taggedObjectPairs' :: Options -> ToArgs Value arity a
891+ (unTagged :: Tagged UntaggedValue enc -> enc)
848− -> String -> f a -> Tagged isRecord [Pair]
892+ . sumToJSON' opts targs
849
893
850−instance OVERLAPPING_ TaggedObjectPairs' arity U1 False where
894+--------------------------------------------------------------------------------
851− taggedObjectPairs' _ _ _ _ = Tagged []
852
895
853−instance (RecordToPairs arity f) => TaggedObjectPairs' arity f True where
896+class FromString enc where
854− taggedObjectPairs' opts targs _ =
897+ fromString :: String -> enc
855− Tagged . toList . recordToPairs opts targs
856
898
857−instance (GToJSON arity f) => TaggedObjectPairs' arity f False where
899+instance FromString Encoding where
858− taggedObjectPairs' opts targs contentsFieldName =
900+ fromString = toEncoding
859− Tagged . (:[]) . (pack contentsFieldName .=) . gToJSON opts targs
901+
902+instance FromString Value where
903+ fromString = String . pack
860
904
861--------------------------------------------------------------------------------
905--------------------------------------------------------------------------------
862
906
863−class TaggedObjectEnc arity f where
907+class TaggedObject enc arity f where
864− taggedObjectEnc :: Options -> ToArgs Encoding arity a
908+ taggedObject :: Options -> ToArgs enc arity a
865− -> String -> String
909+ -> String -> String
866− -> f a -> Encoding
910+ -> f a -> enc
867−
911+
868−instance ( TaggedObjectEnc arity a
912+instance ( TaggedObject enc arity a
869− , TaggedObjectEnc arity b
913+ , TaggedObject enc arity b
870− ) => TaggedObjectEnc arity (a :+: b) where
914+ ) => TaggedObject enc arity (a :+: b)
915+ where
871− taggedObjectEnc opts targs tagFieldName contentsFieldName (L1 x) =
916+ taggedObject opts targs tagFieldName contentsFieldName (L1 x) =
872− taggedObjectEnc opts targs tagFieldName contentsFieldName x
917+ taggedObject opts targs tagFieldName contentsFieldName x
873− taggedObjectEnc opts targs tagFieldName contentsFieldName (R1 x) =
918+ taggedObject opts targs tagFieldName contentsFieldName (R1 x) =
874− taggedObjectEnc opts targs tagFieldName contentsFieldName x
919+ taggedObject opts targs tagFieldName contentsFieldName x
875−
920+
876−instance ( IsRecord a isRecord
921+instance ( IsRecord a isRecord
877− , TaggedObjectEnc' arity a isRecord
922+ , TaggedObject' enc pairs arity a isRecord
923+ , FromPairs enc pairs
924+ , FromString enc
925+ , KeyValuePair enc pairs
878 , Constructor c
926 , Constructor c
879− ) => TaggedObjectEnc arity (C1 c a) where
927+ ) => TaggedObject enc arity (C1 c a)
928+ where
880− taggedObjectEnc opts targs tagFieldName contentsFieldName v =
929+ taggedObject opts targs tagFieldName contentsFieldName =
881− E.wrapObject $
930+ fromPairs . mappend tag . contents
882− (E.string tagFieldName ><
931+ where
883− E.colon ><
932+ tag = tagFieldName `pair`
884− toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))) ><
933+ (fromString (constructorTagModifier opts (conName (undefined :: t c a p)))
934+ :: enc)
935+ contents =
885− ((unTagged :: Tagged isRecord Encoding -> Encoding) .
936+ (unTagged :: Tagged isRecord pairs -> pairs) .
886− taggedObjectEnc' opts targs contentsFieldName . unM1 $ v)
937+ taggedObject' opts targs contentsFieldName . unM1
887−
938+
888−class TaggedObjectEnc' arity f isRecord where
939+class TaggedObject' enc pairs arity f isRecord where
889− taggedObjectEnc' :: Options -> ToArgs Encoding arity a
940+ taggedObject' :: Options -> ToArgs enc arity a
890− -> String -> f a -> Tagged isRecord Encoding
941+ -> String -> f a -> Tagged isRecord pairs
891−
942+
943+instance ( GToJSON enc arity f
944+ , KeyValuePair enc pairs
945+ ) => TaggedObject' enc pairs arity f False
946+ where
947+ taggedObject' opts targs contentsFieldName =
948+ Tagged . (contentsFieldName `pair`) . gToJSON opts targs
949+
892−instance OVERLAPPING_ TaggedObjectEnc' arity U1 False where
950+instance OVERLAPPING_ Monoid pairs => TaggedObject' enc pairs arity U1 False where
893− taggedObjectEnc' _ _ _ _ = Tagged E.empty
951+ taggedObject' _ _ _ _ = Tagged mempty
894−
952+
895−instance (RecordToEncoding arity f) => TaggedObjectEnc' arity f True where
953+instance ( RecordToPairs enc pairs arity f
896− taggedObjectEnc' opts targs _ = Tagged . (E.comma ><) . fst
954+ ) => TaggedObject' enc pairs arity f True
897− . recordToEncoding opts targs
955+ where
898−
956+ taggedObject' opts targs _ = Tagged . recordToPairs opts targs
899−instance (GToEncoding arity f) => TaggedObjectEnc' arity f False where
900− taggedObjectEnc' opts targs contentsFieldName =
901− Tagged . (\z -> E.comma >< toEncoding contentsFieldName >< E.colon >< z) .
902− gToEncoding opts targs
903
957
904--------------------------------------------------------------------------------
958--------------------------------------------------------------------------------
905
959
@@ -914,23 +968,38 @@ instance (GetConName a, GetConName b) => GetConName (a :+: b) where
914instance (Constructor c) => GetConName (C1 c a) where
968instance (Constructor c) => GetConName (C1 c a) where
915 getConName = conName
969 getConName = conName
916
970
971+-- For genericToJSONKey
972+instance GetConName a => GetConName (D1 d a) where
973+ getConName (M1 x) = getConName x
974+
917--------------------------------------------------------------------------------
975--------------------------------------------------------------------------------
918
976
919−class TwoElemArrayObj arity f where
977+-- Reflection of SumEncoding variants
920− twoElemArrayObj :: Options -> ToArgs Value arity a
978+
921− -> f a -> V.Vector Value
979+data ObjectWithSingleField
980+data TwoElemArray
981+data UntaggedValue
982+
983+--------------------------------------------------------------------------------
922
984
985+class SumToJSON' s enc arity f where
986+ sumToJSON' :: Options -> ToArgs enc arity a
987+ -> f a -> Tagged s enc
988+
923−instance ( TwoElemArrayObj arity a
989+instance ( SumToJSON' s enc arity a
924− , TwoElemArrayObj arity b
990+ , SumToJSON' s enc arity b
925− ) => TwoElemArrayObj arity (a :+: b) where
991+ ) => SumToJSON' s enc arity (a :+: b)
992+ where
926− twoElemArrayObj opts targs (L1 x) = twoElemArrayObj opts targs x
993+ sumToJSON' opts targs (L1 x) = sumToJSON' opts targs x
927− twoElemArrayObj opts targs (R1 x) = twoElemArrayObj opts targs x
994+ sumToJSON' opts targs (R1 x) = sumToJSON' opts targs x
995+
996+--------------------------------------------------------------------------------
928
997
929−instance ( GToJSON arity a
998+instance ( GToJSON Value arity a
930− , ConsToJSON arity a
999+ , ConsToJSON Value arity a
931 , Constructor c
1000 , Constructor c
932− ) => TwoElemArrayObj arity (C1 c a) where
1001+ ) => SumToJSON' TwoElemArray Value arity (C1 c a) where
933− twoElemArrayObj opts targs x = V.create $ do
1002+ sumToJSON' opts targs x = Tagged $ Array $ V.create $ do
934 mv <- VM.unsafeNew 2
1003 mv <- VM.unsafeNew 2
935 VM.unsafeWrite mv 0 $ String $ pack $ constructorTagModifier opts
1004 VM.unsafeWrite mv 0 $ String $ pack $ constructorTagModifier opts
936 $ conName (undefined :: t c a p)
1005 $ conName (undefined :: t c a p)
@@ -939,148 +1008,121 @@ instance ( GToJSON arity a
939
1008
940--------------------------------------------------------------------------------
1009--------------------------------------------------------------------------------
941
1010
942−class TwoElemArrayEnc arity f where
943− twoElemArrayEnc :: Options -> ToArgs Encoding arity a
944− -> f a -> Encoding
945−
946−instance ( TwoElemArrayEnc arity a
1011+instance ( GToJSON Encoding arity a
947− , TwoElemArrayEnc arity b
1012+ , ConsToJSON Encoding arity a
948− ) => TwoElemArrayEnc arity (a :+: b) where
949− twoElemArrayEnc opts targs (L1 x) = twoElemArrayEnc opts targs x
950− twoElemArrayEnc opts targs (R1 x) = twoElemArrayEnc opts targs x
951−
952−instance ( GToEncoding arity a
953− , ConsToEncoding arity a
954 , Constructor c
1013 , Constructor c
955− ) => TwoElemArrayEnc arity (C1 c a) where
1014+ ) => SumToJSON' TwoElemArray Encoding arity (C1 c a)
956− twoElemArrayEnc opts targs x = E.tuple $
1015+ where
1016+ sumToJSON' opts targs x = Tagged $ E.list id
957− toEncoding (constructorTagModifier opts (conName (undefined :: t c a p))) >*<
1017+ [ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))
958− gToEncoding opts targs x
1018+ , gToJSON opts targs x
1019+ ]
959
1020
960--------------------------------------------------------------------------------
1021--------------------------------------------------------------------------------
961
1022
962−class ConsToJSON arity f where
1023+class ConsToJSON enc arity f where
963− consToJSON :: Options -> ToArgs Value arity a
1024+ consToJSON :: Options -> ToArgs enc arity a
964− -> f a -> Value
1025+ -> f a -> enc
965
1026
966−class ConsToJSON' arity f isRecord where
1027+class ConsToJSON' enc arity f isRecord where
967− consToJSON' :: Options -> ToArgs Value arity a
1028+ consToJSON' :: Options -> ToArgs enc arity a
968− -> Bool -- ^ Are we a record with one field?
969− -> f a -> Tagged isRecord Value
1029+ -> f a -> Tagged isRecord enc
970
1030
971−instance ( IsRecord f isRecord
1031+instance ( IsRecord f isRecord
972− , ConsToJSON' arity f isRecord
1032+ , ConsToJSON' enc arity f isRecord
973− ) => ConsToJSON arity f where
1033+ ) => ConsToJSON enc arity f
1034+ where
974 consToJSON opts targs =
1035 consToJSON opts targs =
975− (unTagged :: Tagged isRecord Value -> Value)
1036+ (unTagged :: Tagged isRecord enc -> enc)
976− . consToJSON' opts targs (isUnary (undefined :: f a))
1037+ . consToJSON' opts targs
977−
1038+ {-# INLINE consToJSON #-}
978−instance (RecordToPairs arity f) => ConsToJSON' arity f True where
979− consToJSON' opts targs isUn f = let
980− vals = toList $ recordToPairs opts targs f
981− in case (unwrapUnaryRecords opts,isUn,vals) of
982− (True,True,[(_,val)]) -> Tagged val
983− _ -> Tagged $ object vals
984−
985−instance GToJSON arity f => ConsToJSON' arity f False where
986− consToJSON' opts targs _ = Tagged . gToJSON opts targs
987−
988−--------------------------------------------------------------------------------
989
1039
990−class ConsToEncoding arity f where
1040+instance OVERLAPPING_
991− consToEncoding :: Options -> ToArgs Encoding arity a
1041+ ( RecordToPairs enc pairs arity (S1 s f)
992− -> f a -> Encoding
1042+ , FromPairs enc pairs
993−
1043+ , GToJSON enc arity f
994−class ConsToEncoding' arity f isRecord where
1044+ ) => ConsToJSON' enc arity (S1 s f) True
995− consToEncoding' :: Options -> ToArgs Encoding arity a
1045+ where
996− -> Bool -- ^ Are we a record with one field?
1046+ consToJSON' opts targs
997− -> f a -> Tagged isRecord Encoding
1047+ | unwrapUnaryRecords opts = Tagged . gToJSON opts targs
1048+ | otherwise = Tagged . fromPairs . recordToPairs opts targs
1049+ {-# INLINE consToJSON' #-}
1050+
1051+instance ( RecordToPairs enc pairs arity f
1052+ , FromPairs enc pairs
1053+ ) => ConsToJSON' enc arity f True
1054+ where
1055+ consToJSON' opts targs = Tagged . fromPairs . recordToPairs opts targs
1056+ {-# INLINE consToJSON' #-}
998
1057
999−instance ( IsRecord f isRecord
1058+instance GToJSON enc arity f => ConsToJSON' enc arity f False where
1000− , ConsToEncoding' arity f isRecord
1001− ) => ConsToEncoding arity f where
1002− consToEncoding opts targs =
1003− (unTagged :: Tagged isRecord Encoding -> Encoding)
1004− . consToEncoding' opts targs (isUnary (undefined :: f a))
1005−
1006−instance (RecordToEncoding arity f) => ConsToEncoding' arity f True where
1007− consToEncoding' opts targs isUn x =
1008− let (enc, mbVal) = recordToEncoding opts targs x
1009− in case (unwrapUnaryRecords opts, isUn, mbVal) of
1010− (True, True, Just val) -> Tagged val
1011− _ -> Tagged $ E.wrapObject enc
1012−
1013−instance GToEncoding arity f => ConsToEncoding' arity f False where
1014− consToEncoding' opts targs _ = Tagged . gToEncoding opts targs
1059+ consToJSON' opts targs = Tagged . gToJSON opts targs
1060+ {-# INLINE consToJSON' #-}
1015
1061
1016--------------------------------------------------------------------------------
1062--------------------------------------------------------------------------------
1017
1063
1018−class RecordToPairs arity f where
1064+class RecordToPairs enc pairs arity f where
1065+ -- 1st element: whole thing
1066+ -- 2nd element: in case the record has only 1 field, just the value
1067+ -- of the field (without the key); 'Nothing' otherwise
1019− recordToPairs :: Options -> ToArgs Value arity a
1068+ recordToPairs :: Options -> ToArgs enc arity a
1020− -> f a -> DList Pair
1069+ -> f a -> pairs
1021−
1022−instance ( RecordToPairs arity a
1023− , RecordToPairs arity b
1024− ) => RecordToPairs arity (a :*: b) where
1025− recordToPairs opts targs (a :*: b) = recordToPairs opts targs a <>
1026− recordToPairs opts targs b
1027
1070
1071+instance ( Monoid pairs
1072+ , RecordToPairs enc pairs arity a
1073+ , RecordToPairs enc pairs arity b
1074+ ) => RecordToPairs enc pairs arity (a :*: b)
1075+ where
1076+ recordToPairs opts (targs :: ToArgs enc arity p) (a :*: b) =
1077+ pairsOf a `mappend` pairsOf b
1078+ where
1079+ pairsOf :: (RecordToPairs enc pairs arity f) => f p -> pairs
1080+ pairsOf = recordToPairs opts targs
1081+ {-# INLINE recordToPairs #-}
1082+
1083+instance ( Selector s
1084+ , GToJSON enc arity a
1085+ , KeyValuePair enc pairs
1028−instance (Selector s, GToJSON arity a) => RecordToPairs arity (S1 s a) where
1086+ ) => RecordToPairs enc pairs arity (S1 s a)
1087+ where
1029 recordToPairs = fieldToPair
1088 recordToPairs = fieldToPair
1030−
1089+ {-# INLINE recordToPairs #-}
1090+
1091+instance INCOHERENT_
1031−instance OVERLAPPING_ (Selector s, ToJSON a) =>
1092+ ( Selector s
1093+ , GToJSON enc arity (K1 i (Maybe a))
1094+ , KeyValuePair enc pairs
1095+ , Monoid pairs
1032− RecordToPairs arity (S1 s (K1 i (Maybe a))) where
1096+ ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))
1097+ where
1033 recordToPairs opts _ (M1 k1) | omitNothingFields opts
1098 recordToPairs opts _ (M1 k1) | omitNothingFields opts
1034− , K1 Nothing <- k1 = DList.empty
1099+ , K1 Nothing <- k1 = mempty
1035 recordToPairs opts targs m1 = fieldToPair opts targs m1
1100 recordToPairs opts targs m1 = fieldToPair opts targs m1
1036−
1101+ {-# INLINE recordToPairs #-}
1037−fieldToPair :: (Selector s, GToJSON arity a)
1102+
1038− => Options -> ToArgs Value arity p
1103+instance INCOHERENT_
1039− -> S1 s a p -> DList Pair
1104+ ( Selector s
1040−fieldToPair opts targs m1 = pure ( pack $ fieldLabelModifier opts $ selName m1
1105+ , GToJSON enc arity (K1 i (Maybe a))
1041− , gToJSON opts targs (unM1 m1)
1106+ , KeyValuePair enc pairs
1042− )
1107+ , Monoid pairs
1043−
1108+ ) => RecordToPairs enc pairs arity (S1 s (K1 i (Semigroup.Option a)))
1044−--------------------------------------------------------------------------------
1109+ where
1045−
1110+ recordToPairs opts targs = recordToPairs opts targs . unwrap
1046−class RecordToEncoding arity f where
1111+ where
1047− -- 1st element: whole thing
1112+ unwrap :: S1 s (K1 i (Semigroup.Option a)) p -> S1 s (K1 i (Maybe a)) p
1048− -- 2nd element: in case the record has only 1 field, just the value
1113+ unwrap (M1 (K1 (Semigroup.Option a))) = M1 (K1 a)
1049− -- of the field (without the key); 'Nothing' otherwise
1114+ {-# INLINE recordToPairs #-}
1050− recordToEncoding :: Options -> ToArgs Encoding arity a
1115+
1051− -> f a -> (Encoding, Maybe Encoding)
1116+fieldToPair :: (Selector s
1052−
1117+ , GToJSON enc arity a
1053−instance ( RecordToEncoding arity a
1118+ , KeyValuePair enc pairs)
1054− , RecordToEncoding arity b
1055− ) => RecordToEncoding arity (a :*: b) where
1056− recordToEncoding opts targs (a :*: b) | omitNothingFields opts =
1057− (E.econcat $ intersperse E.comma $
1058− filter (not . E.nullEncoding)
1059− [ fst (recordToEncoding opts targs a)
1060− , fst (recordToEncoding opts targs b) ]
1061− , Nothing)
1062− recordToEncoding opts targs (a :*: b) =
1063− (fst (recordToEncoding opts targs a) >< E.comma ><
1064− fst (recordToEncoding opts targs b),
1065− Nothing)
1066−
1067−
1068−instance (Selector s, GToEncoding arity a) => RecordToEncoding arity (S1 s a) where
1069− recordToEncoding = fieldToEncoding
1070−
1071−instance OVERLAPPING_ (Selector s, ToJSON a) =>
1072− RecordToEncoding arity (S1 s (K1 i (Maybe a))) where
1073− recordToEncoding opts _ (M1 k1) | omitNothingFields opts
1074− , K1 Nothing <- k1 = (E.empty, Nothing)
1075− recordToEncoding opts targs m1 = fieldToEncoding opts targs m1
1076−
1077−fieldToEncoding :: (Selector s, GToEncoding arity a)
1078− => Options -> ToArgs Encoding arity p
1119+ => Options -> ToArgs enc arity p
1079− -> S1 s a p -> (Encoding, Maybe Encoding)
1120+ -> S1 s a p -> pairs
1080−fieldToEncoding opts targs m1 =
1121+fieldToPair opts targs m1 =
1081− let keyBuilder = toEncoding (fieldLabelModifier opts $ selName m1)
1122+ let key = fieldLabelModifier opts (selName m1)
1082− valueBuilder = gToEncoding opts targs (unM1 m1)
1123+ value = gToJSON opts targs (unM1 m1)
1083− in (keyBuilder >< E.colon >< valueBuilder, Just valueBuilder)
1124+ in key `pair` value
1125+{-# INLINE fieldToPair #-}
1084
1126
1085--------------------------------------------------------------------------------
1127--------------------------------------------------------------------------------
1086
1128
@@ -1103,10 +1145,12 @@ instance ( WriteProduct arity a
1103 lenL = len `unsafeShiftR` 1
1145 lenL = len `unsafeShiftR` 1
1104 lenR = len - lenL
1146 lenR = len - lenL
1105 ixR = ix + lenL
1147 ixR = ix + lenL
1148+ {-# INLINE writeProduct #-}
1106
1149
1107−instance OVERLAPPABLE_ (GToJSON arity a) => WriteProduct arity a where
1150+instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where
1108 writeProduct opts targs mv ix _ =
1151 writeProduct opts targs mv ix _ =
1109 VM.unsafeWrite mv ix . gToJSON opts targs
1152 VM.unsafeWrite mv ix . gToJSON opts targs
1153+ {-# INLINE writeProduct #-}
1110
1154
1111--------------------------------------------------------------------------------
1155--------------------------------------------------------------------------------
1112
1156
@@ -1121,114 +1165,44 @@ instance ( EncodeProduct arity a
1121 E.econcat $ intersperse E.comma $
1165 E.econcat $ intersperse E.comma $
1122 filter (not . E.nullEncoding)
1166 filter (not . E.nullEncoding)
1123 [encodeProduct opts targs a, encodeProduct opts targs b]
1167 [encodeProduct opts targs a, encodeProduct opts targs b]
1124− encodeProduct opts targs (a :*: b) = encodeProduct opts targs a >*<
1168+ encodeProduct opts targs (a :*: b) =
1169+ encodeProduct opts targs a >*<
1125− encodeProduct opts targs b
1170+ encodeProduct opts targs b
1171+ {-# INLINE encodeProduct #-}
1126
1172
1127−instance OVERLAPPABLE_ (GToEncoding arity a) => EncodeProduct arity a where
1173+instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where
1128− encodeProduct opts targs a = E.retagEncoding $ gToEncoding opts targs a
1174+ encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a
1175+ {-# INLINE encodeProduct #-}
1129
1176
1130--------------------------------------------------------------------------------
1177--------------------------------------------------------------------------------
1131
1178
1132−class ObjectWithSingleFieldObj arity f where
1133− objectWithSingleFieldObj :: Options -> ToArgs Value arity a
1134− -> f a -> Object
1135−
1136−instance ( ObjectWithSingleFieldObj arity a
1137− , ObjectWithSingleFieldObj arity b
1138− ) => ObjectWithSingleFieldObj arity (a :+: b) where
1139− objectWithSingleFieldObj opts targs (L1 x) =
1140− objectWithSingleFieldObj opts targs x
1141− objectWithSingleFieldObj opts targs (R1 x) =
1142− objectWithSingleFieldObj opts targs x
1143−
1144−instance ( GToJSON arity a
1179+instance ( GToJSON enc arity a
1145− , ConsToJSON arity a
1180+ , ConsToJSON enc arity a
1181+ , FromPairs enc pairs
1182+ , KeyValuePair enc pairs
1146 , Constructor c
1183 , Constructor c
1147− ) => ObjectWithSingleFieldObj arity (C1 c a) where
1184+ ) => SumToJSON' ObjectWithSingleField enc arity (C1 c a)
1148− objectWithSingleFieldObj opts targs = H.singleton typ . gToJSON opts targs
1185+ where
1186+ sumToJSON' opts targs =
1187+ Tagged . fromPairs . (typ `pair`) . gToJSON opts targs
1149 where
1188 where
1150− typ = pack $ constructorTagModifier opts $
1189+ typ = constructorTagModifier opts $
1151 conName (undefined :: t c a p)
1190 conName (undefined :: t c a p)
1152
1191
1153--------------------------------------------------------------------------------
1192--------------------------------------------------------------------------------
1154
1193
1155−class ObjectWithSingleFieldEnc arity f where
1156− objectWithSingleFieldEnc :: Options -> ToArgs Encoding arity a
1157− -> f a -> Encoding
1158−
1159−instance ( ObjectWithSingleFieldEnc arity a
1160− , ObjectWithSingleFieldEnc arity b
1161− ) => ObjectWithSingleFieldEnc arity (a :+: b) where
1162− objectWithSingleFieldEnc opts targs (L1 x) =
1163− objectWithSingleFieldEnc opts targs x
1164− objectWithSingleFieldEnc opts targs (R1 x) =
1165− objectWithSingleFieldEnc opts targs x
1166−
1167−instance ( GToEncoding arity a
1168− , ConsToEncoding arity a
1169− , Constructor c
1170− ) => ObjectWithSingleFieldEnc arity (C1 c a) where
1171− objectWithSingleFieldEnc opts targs v =
1172− E.openCurly ><
1173− toEncoding
1174− (constructorTagModifier opts
1175− (conName (undefined :: t c a p))) ><
1176− E.colon ><
1177− gToEncoding opts targs v ><
1178− E.closeCurly
1179−
1180−--------------------------------------------------------------------------------
1181−
1182−class UntaggedValueObj arity f where
1183− untaggedValueObj :: Options -> ToArgs Value arity a
1184− -> f a -> Value
1185−
1186−instance
1187− ( UntaggedValueObj arity a
1188− , UntaggedValueObj arity b
1189− ) => UntaggedValueObj arity (a :+: b)
1190− where
1191− untaggedValueObj opts targs (L1 x) = untaggedValueObj opts targs x
1192− untaggedValueObj opts targs (R1 x) = untaggedValueObj opts targs x
1193−
1194instance OVERLAPPABLE_
1194instance OVERLAPPABLE_
1195− ( GToJSON arity a
1196− , ConsToJSON arity a
1195+ ( ConsToJSON enc arity a
1197− ) => UntaggedValueObj arity (C1 c a) where
1196+ ) => SumToJSON' UntaggedValue enc arity (C1 c a)
1198− untaggedValueObj = gToJSON
1199−
1200−instance OVERLAPPING_
1201− ( Constructor c )
1202− => UntaggedValueObj arity (C1 c U1)
1203 where
1197 where
1204− untaggedValueObj opts _ _ = toJSON $
1198+ sumToJSON' opts targs = Tagged . gToJSON opts targs
1205− constructorTagModifier opts $ conName (undefined :: t c U1 p)
1206−
1207−--------------------------------------------------------------------------------
1208−
1209−class UntaggedValueEnc arity f where
1210− untaggedValueEnc :: Options -> ToArgs Encoding arity a
1211− -> f a -> Encoding
1212−instance
1213− ( UntaggedValueEnc arity a
1214− , UntaggedValueEnc arity b
1215− ) => UntaggedValueEnc arity (a :+: b)
1216− where
1217− untaggedValueEnc opts targs (L1 x) = untaggedValueEnc opts targs x
1218− untaggedValueEnc opts targs (R1 x) = untaggedValueEnc opts targs x
1219−
1220−instance OVERLAPPABLE_
1221− ( GToEncoding arity a
1222− , ConsToEncoding arity a
1223− ) => UntaggedValueEnc arity (C1 c a)
1224− where
1225− untaggedValueEnc = gToEncoding
1226
1199
1227instance OVERLAPPING_
1200instance OVERLAPPING_
1228− ( Constructor c )
1201+ ( Constructor c
1229− => UntaggedValueEnc arity (C1 c U1)
1202+ , FromString enc
1203+ ) => SumToJSON' UntaggedValue enc arity (C1 c U1)
1230 where
1204 where
1231− untaggedValueEnc opts _ _ = toEncoding $
1205+ sumToJSON' opts _ _ = Tagged . fromString $
1232 constructorTagModifier opts $ conName (undefined :: t c U1 p)
1206 constructorTagModifier opts $ conName (undefined :: t c U1 p)
1233
1207
1234-------------------------------------------------------------------------------
1208-------------------------------------------------------------------------------
@@ -1302,6 +1276,13 @@ instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
1302 toEncoding = toEncoding2
1276 toEncoding = toEncoding2
1303 {-# INLINE toEncoding #-}
1277 {-# INLINE toEncoding #-}
1304
1278
1279+instance ToJSON Void where
1280+ toJSON = absurd
1281+ {-# INLINE toJSON #-}
1282+
1283+ toEncoding = absurd
1284+ {-# INLINE toEncoding #-}
1285+
1305
1286
1306instance ToJSON Bool where
1287instance ToJSON Bool where
1307 toJSON = Bool
1288 toJSON = Bool
@@ -1487,7 +1468,6 @@ instance ToJSONKey Int64 where
1487 toJSONKey = toJSONKeyTextEnc E.int64Text
1468 toJSONKey = toJSONKeyTextEnc E.int64Text
1488 {-# INLINE toJSONKey #-}
1469 {-# INLINE toJSONKey #-}
1489
1470
1490−
1491instance ToJSON Word where
1471instance ToJSON Word where
1492 toJSON = Number . fromIntegral
1472 toJSON = Number . fromIntegral
1493 {-# INLINE toJSON #-}
1473 {-# INLINE toJSON #-}
@@ -1547,6 +1527,12 @@ instance ToJSONKey Word64 where
1547 toJSONKey = toJSONKeyTextEnc E.word64Text
1527 toJSONKey = toJSONKeyTextEnc E.word64Text
1548 {-# INLINE toJSONKey #-}
1528 {-# INLINE toJSONKey #-}
1549
1529
1530+instance ToJSON CTime where
1531+ toJSON (CTime i) = toJSON i
1532+ {-# INLINE toJSON #-}
1533+
1534+ toEncoding (CTime i) = toEncoding i
1535+ {-# INLINE toEncoding #-}
1550
1536
1551instance ToJSON Text where
1537instance ToJSON Text where
1552 toJSON = String
1538 toJSON = String
@@ -1661,7 +1647,7 @@ instance (ToJSON a) => ToJSON (Identity a) where
1661 toEncodingList = liftToEncodingList toEncoding toEncodingList
1647 toEncodingList = liftToEncodingList toEncoding toEncodingList
1662 {-# INLINE toEncodingList #-}
1648 {-# INLINE toEncodingList #-}
1663
1649
1664−instance (ToJSONKey a, ToJSON a) => ToJSONKey (Identity a) where
1650+instance (ToJSONKey a) => ToJSONKey (Identity a) where
1665 toJSONKey = contramapToJSONKeyFunction runIdentity toJSONKey
1651 toJSONKey = contramapToJSONKeyFunction runIdentity toJSONKey
1666 toJSONKeyList = contramapToJSONKeyFunction (map runIdentity) toJSONKeyList
1652 toJSONKeyList = contramapToJSONKeyFunction (map runIdentity) toJSONKeyList
1667
1653
@@ -1810,13 +1796,11 @@ instance ToJSONKey k => ToJSON1 (M.Map k) where
1810 ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . M.toList
1796 ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . M.toList
1811 {-# INLINE liftToJSON #-}
1797 {-# INLINE liftToJSON #-}
1812
1798
1813− -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> M.Map k a -> Encoding
1814 liftToEncoding g _ = case toJSONKey of
1799 liftToEncoding g _ = case toJSONKey of
1815 ToJSONKeyText _ f -> dict f g M.foldrWithKey
1800 ToJSONKeyText _ f -> dict f g M.foldrWithKey
1816 ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . M.toList
1801 ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . M.toList
1817 where
1802 where
1818− -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
1819− pairEncoding f (a, b) = tuple $ f a >*< g b
1803+ pairEncoding f (a, b) = E.list id [f a, g b]
1820 {-# INLINE liftToEncoding #-}
1804 {-# INLINE liftToEncoding #-}
1821
1805
1822
1806
@@ -1854,6 +1838,17 @@ instance (ToJSON v) => ToJSON (Tree.Tree v) where
1854 toEncoding = toEncoding1
1838 toEncoding = toEncoding1
1855 {-# INLINE toEncoding #-}
1839 {-# INLINE toEncoding #-}
1856
1840
1841+-------------------------------------------------------------------------------
1842+-- uuid
1843+-------------------------------------------------------------------------------
1844+
1845+instance ToJSON UUID.UUID where
1846+ toJSON = toJSON . UUID.toText
1847+ toEncoding = E.unsafeToEncoding . EB.quote . B.byteString . UUID.toASCIIBytes
1848+
1849+instance ToJSONKey UUID.UUID where
1850+ toJSONKey = ToJSONKeyText UUID.toText $
1851+ E.unsafeToEncoding . EB.quote . B.byteString . UUID.toASCIIBytes
1857
1852
1858-------------------------------------------------------------------------------
1853-------------------------------------------------------------------------------
1859-- vector
1854-- vector
@@ -1867,6 +1862,8 @@ instance ToJSON1 Vector where
1867 {-# INLINE liftToEncoding #-}
1862 {-# INLINE liftToEncoding #-}
1868
1863
1869instance (ToJSON a) => ToJSON (Vector a) where
1864instance (ToJSON a) => ToJSON (Vector a) where
1865+ {-# SPECIALIZE instance ToJSON Array #-}
1866+
1870 toJSON = toJSON1
1867 toJSON = toJSON1
1871 {-# INLINE toJSON #-}
1868 {-# INLINE toJSON #-}
1872
1869
@@ -1934,11 +1931,12 @@ instance ToJSONKey k => ToJSON1 (H.HashMap k) where
1934 ToJSONKeyText _ f -> dict f g H.foldrWithKey
1931 ToJSONKeyText _ f -> dict f g H.foldrWithKey
1935 ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . H.toList
1932 ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . H.toList
1936 where
1933 where
1937− -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
1938− pairEncoding f (a, b) = tuple $ f a >*< g b
1934+ pairEncoding f (a, b) = E.list id [f a, g b]
1939 {-# INLINE liftToEncoding #-}
1935 {-# INLINE liftToEncoding #-}
1940
1936
1941instance (ToJSON v, ToJSONKey k) => ToJSON (H.HashMap k v) where
1937instance (ToJSON v, ToJSONKey k) => ToJSON (H.HashMap k v) where
1938+ {-# SPECIALIZE instance ToJSON Object #-}
1939+
1942 toJSON = toJSON1
1940 toJSON = toJSON1
1943 {-# INLINE toJSON #-}
1941 {-# INLINE toJSON #-}
1944
1942
@@ -1968,6 +1966,32 @@ dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
1968formatMillis :: (FormatTime t) => t -> String
1966formatMillis :: (FormatTime t) => t -> String
1969formatMillis = take 3 . formatTime defaultTimeLocale "%q"
1967formatMillis = take 3 . formatTime defaultTimeLocale "%q"
1970
1968
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+
1971-------------------------------------------------------------------------------
1995-------------------------------------------------------------------------------
1972-- time
1996-- time
1973-------------------------------------------------------------------------------
1997-------------------------------------------------------------------------------
@@ -2016,10 +2040,11 @@ stringEncoding :: Encoding' Text -> Value
2016stringEncoding = String
2040stringEncoding = String
2017 . T.dropAround (== '"')
2041 . T.dropAround (== '"')
2018 . T.decodeLatin1
2042 . T.decodeLatin1
2019− . lazyToStrictByteString
2043+ . L.toStrict
2020 . E.encodingToLazyByteString
2044 . E.encodingToLazyByteString
2021{-# INLINE stringEncoding #-}
2045{-# INLINE stringEncoding #-}
2022
2046
2047+
2023instance ToJSON NominalDiffTime where
2048instance ToJSON NominalDiffTime where
2024 toJSON = Number . realToFrac
2049 toJSON = Number . realToFrac
2025 {-# INLINE toJSON #-}
2050 {-# INLINE toJSON #-}
@@ -2027,18 +2052,159 @@ instance ToJSON NominalDiffTime where
2027 toEncoding = E.scientific . realToFrac
2052 toEncoding = E.scientific . realToFrac
2028 {-# INLINE toEncoding #-}
2053 {-# INLINE toEncoding #-}
2029
2054
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+
2030-------------------------------------------------------------------------------
2107-------------------------------------------------------------------------------
2031-- base Monoid/Semigroup
2108-- base Monoid/Semigroup
2032-------------------------------------------------------------------------------
2109-------------------------------------------------------------------------------
2033
2110
2034−instance ToJSON1 Dual where
2111+instance ToJSON1 Monoid.Dual where
2035− liftToJSON t _ = t . getDual
2112+ liftToJSON t _ = t . Monoid.getDual
2113+ {-# INLINE liftToJSON #-}
2114+
2115+ liftToEncoding t _ = t . Monoid.getDual
2116+ {-# INLINE liftToEncoding #-}
2117+
2118+instance ToJSON a => ToJSON (Monoid.Dual a) where
2119+ toJSON = toJSON1
2120+ {-# INLINE toJSON #-}
2121+
2122+ toEncoding = toEncoding1
2123+ {-# INLINE toEncoding #-}
2124+
2125+
2126+instance ToJSON1 Monoid.First where
2127+ liftToJSON t to' = liftToJSON t to' . Monoid.getFirst
2128+ {-# INLINE liftToJSON #-}
2129+
2130+ liftToEncoding t to' = liftToEncoding t to' . Monoid.getFirst
2131+ {-# INLINE liftToEncoding #-}
2132+
2133+instance ToJSON a => ToJSON (Monoid.First a) where
2134+ toJSON = toJSON1
2135+ {-# INLINE toJSON #-}
2136+
2137+ toEncoding = toEncoding1
2138+ {-# INLINE toEncoding #-}
2139+
2140+
2141+instance ToJSON1 Monoid.Last where
2142+ liftToJSON t to' = liftToJSON t to' . Monoid.getLast
2143+ {-# INLINE liftToJSON #-}
2144+
2145+ liftToEncoding t to' = liftToEncoding t to' . Monoid.getLast
2146+ {-# INLINE liftToEncoding #-}
2147+
2148+instance ToJSON a => ToJSON (Monoid.Last a) where
2149+ toJSON = toJSON1
2150+ {-# INLINE toJSON #-}
2151+
2152+ toEncoding = toEncoding1
2153+ {-# INLINE toEncoding #-}
2154+
2155+
2156+instance ToJSON1 Semigroup.Min where
2157+ liftToJSON t _ (Semigroup.Min x) = t x
2158+ {-# INLINE liftToJSON #-}
2159+
2160+ liftToEncoding t _ (Semigroup.Min x) = t x
2161+ {-# INLINE liftToEncoding #-}
2162+
2163+instance ToJSON a => ToJSON (Semigroup.Min a) where
2164+ toJSON = toJSON1
2165+ {-# INLINE toJSON #-}
2166+
2167+ toEncoding = toEncoding1
2168+ {-# INLINE toEncoding #-}
2169+
2170+
2171+instance ToJSON1 Semigroup.Max where
2172+ liftToJSON t _ (Semigroup.Max x) = t x
2173+ {-# INLINE liftToJSON #-}
2174+
2175+ liftToEncoding t _ (Semigroup.Max x) = t x
2176+ {-# INLINE liftToEncoding #-}
2177+
2178+instance ToJSON a => ToJSON (Semigroup.Max a) where
2179+ toJSON = toJSON1
2180+ {-# INLINE toJSON #-}
2181+
2182+ toEncoding = toEncoding1
2183+ {-# INLINE toEncoding #-}
2184+
2185+instance ToJSON1 Semigroup.First where
2186+ liftToJSON t _ (Semigroup.First x) = t x
2187+ {-# INLINE liftToJSON #-}
2188+
2189+ liftToEncoding t _ (Semigroup.First x) = t x
2190+ {-# INLINE liftToEncoding #-}
2191+
2192+instance ToJSON a => ToJSON (Semigroup.First a) where
2193+ toJSON = toJSON1
2194+ {-# INLINE toJSON #-}
2195+
2196+ toEncoding = toEncoding1
2197+ {-# INLINE toEncoding #-}
2198+
2199+
2200+instance ToJSON1 Semigroup.Last where
2201+ liftToJSON t _ (Semigroup.Last x) = t x
2036 {-# INLINE liftToJSON #-}
2202 {-# INLINE liftToJSON #-}
2037
2203
2038− liftToEncoding t _ = t . getDual
2204+ liftToEncoding t _ (Semigroup.Last x) = t x
2039 {-# INLINE liftToEncoding #-}
2205 {-# INLINE liftToEncoding #-}
2040
2206
2041−instance ToJSON a => ToJSON (Dual a) where
2207+instance ToJSON a => ToJSON (Semigroup.Last a) where
2042 toJSON = toJSON1
2208 toJSON = toJSON1
2043 {-# INLINE toJSON #-}
2209 {-# INLINE toJSON #-}
2044
2210
@@ -2046,14 +2212,14 @@ instance ToJSON a => ToJSON (Dual a) where
2046 {-# INLINE toEncoding #-}
2212 {-# INLINE toEncoding #-}
2047
2213
2048
2214
2049−instance ToJSON1 First where
2215+instance ToJSON1 Semigroup.WrappedMonoid where
2050− liftToJSON t to' = liftToJSON t to' . getFirst
2216+ liftToJSON t _ (Semigroup.WrapMonoid x) = t x
2051 {-# INLINE liftToJSON #-}
2217 {-# INLINE liftToJSON #-}
2052
2218
2053− liftToEncoding t to' = liftToEncoding t to' . getFirst
2219+ liftToEncoding t _ (Semigroup.WrapMonoid x) = t x
2054 {-# INLINE liftToEncoding #-}
2220 {-# INLINE liftToEncoding #-}
2055
2221
2056−instance ToJSON a => ToJSON (First a) where
2222+instance ToJSON a => ToJSON (Semigroup.WrappedMonoid a) where
2057 toJSON = toJSON1
2223 toJSON = toJSON1
2058 {-# INLINE toJSON #-}
2224 {-# INLINE toJSON #-}
2059
2225
@@ -2061,14 +2227,14 @@ instance ToJSON a => ToJSON (First a) where
2061 {-# INLINE toEncoding #-}
2227 {-# INLINE toEncoding #-}
2062
2228
2063
2229
2064−instance ToJSON1 Last where
2230+instance ToJSON1 Semigroup.Option where
2065− liftToJSON t to' = liftToJSON t to' . getLast
2231+ liftToJSON t to' = liftToJSON t to' . Semigroup.getOption
2066 {-# INLINE liftToJSON #-}
2232 {-# INLINE liftToJSON #-}
2067
2233
2068− liftToEncoding t to' = liftToEncoding t to' . getLast
2234+ liftToEncoding t to' = liftToEncoding t to' . Semigroup.getOption
2069 {-# INLINE liftToEncoding #-}
2235 {-# INLINE liftToEncoding #-}
2070
2236
2071−instance ToJSON a => ToJSON (Last a) where
2237+instance ToJSON a => ToJSON (Semigroup.Option a) where
2072 toJSON = toJSON1
2238 toJSON = toJSON1
2073 {-# INLINE toJSON #-}
2239 {-# INLINE toJSON #-}
2074
2240
@@ -2079,6 +2245,13 @@ instance ToJSON a => ToJSON (Last a) where
2079-- tagged
2245-- tagged
2080-------------------------------------------------------------------------------
2246-------------------------------------------------------------------------------
2081
2247
2248+instance ToJSON1 Proxy where
2249+ liftToJSON _ _ _ = Null
2250+ {-# INLINE liftToJSON #-}
2251+
2252+ liftToEncoding _ _ _ = E.null_
2253+ {-# INLINE liftToEncoding #-}
2254+
2082instance ToJSON (Proxy a) where
2255instance ToJSON (Proxy a) where
2083 toJSON _ = Null
2256 toJSON _ = Null
2084 {-# INLINE toJSON #-}
2257 {-# INLINE toJSON #-}
@@ -2087,6 +2260,13 @@ instance ToJSON (Proxy a) where
2087 {-# INLINE toEncoding #-}
2260 {-# INLINE toEncoding #-}
2088
2261
2089
2262
2263+instance ToJSON2 Tagged where
2264+ liftToJSON2 _ _ t _ (Tagged x) = t x
2265+ {-# INLINE liftToJSON2 #-}
2266+
2267+ liftToEncoding2 _ _ t _ (Tagged x) = t x
2268+ {-# INLINE liftToEncoding2 #-}
2269+
2090instance ToJSON1 (Tagged a) where
2270instance ToJSON1 (Tagged a) where
2091 liftToJSON t _ (Tagged x) = t x
2271 liftToJSON t _ (Tagged x) = t x
2092 {-# INLINE liftToJSON #-}
2272 {-# INLINE liftToJSON #-}
@@ -2101,7 +2281,6 @@ instance ToJSON b => ToJSON (Tagged a b) where
2101 toEncoding = toEncoding1
2281 toEncoding = toEncoding1
2102 {-# INLINE toEncoding #-}
2282 {-# INLINE toEncoding #-}
2103
2283
2104−
2105instance ToJSONKey b => ToJSONKey (Tagged a b) where
2284instance ToJSONKey b => ToJSONKey (Tagged a b) where
2106 toJSONKey = contramapToJSONKeyFunction unTagged toJSONKey
2285 toJSONKey = contramapToJSONKeyFunction unTagged toJSONKey
2107 toJSONKeyList = contramapToJSONKeyFunction (fmap unTagged) toJSONKeyList
2286 toJSONKeyList = contramapToJSONKeyFunction (fmap unTagged) toJSONKeyList
@@ -2122,7 +2301,7 @@ instance (ToJSONKey a, ToJSON a) => ToJSONKey [a] where
2122 toJSONKey = toJSONKeyList
2301 toJSONKey = toJSONKeyList
2123
2302
2124-------------------------------------------------------------------------------
2303-------------------------------------------------------------------------------
2125−-- Tuple instances, see tuple-instances-to.hs
2304+-- Tuple instances
2126-------------------------------------------------------------------------------
2305-------------------------------------------------------------------------------
2127
2306
2128instance ToJSON2 (,) where
2307instance ToJSON2 (,) where
@@ -2133,9 +2312,7 @@ instance ToJSON2 (,) where
2133 return mv
2312 return mv
2134 {-# INLINE liftToJSON2 #-}
2313 {-# INLINE liftToJSON2 #-}
2135
2314
2136− liftToEncoding2 toA _ toB _ (a, b) = tuple $
2315+ liftToEncoding2 toA _ toB _ (a, b) = E.list id [toA a, toB b]
2137− toA a >*<
2138− toB b
2139 {-# INLINE liftToEncoding2 #-}
2316 {-# INLINE liftToEncoding2 #-}
2140
2317
2141instance (ToJSON a) => ToJSON1 ((,) a) where
2318instance (ToJSON a) => ToJSON1 ((,) a) where
@@ -2159,10 +2336,11 @@ instance (ToJSON a) => ToJSON2 ((,,) a) where
2159 return mv
2336 return mv
2160 {-# INLINE liftToJSON2 #-}
2337 {-# INLINE liftToJSON2 #-}
2161
2338
2162− liftToEncoding2 toB _ toC _ (a, b, c) = tuple $
2339+ liftToEncoding2 toB _ toC _ (a, b, c) = E.list id
2163− toEncoding a >*<
2340+ [ toEncoding a
2164− toB b >*<
2341+ , toB b
2165− toC c
2342+ , toC c
2343+ ]
2166 {-# INLINE liftToEncoding2 #-}
2344 {-# INLINE liftToEncoding2 #-}
2167
2345
2168instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where
2346instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where
@@ -2187,11 +2365,12 @@ instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where
2187 return mv
2365 return mv
2188 {-# INLINE liftToJSON2 #-}
2366 {-# INLINE liftToJSON2 #-}
2189
2367
2190− liftToEncoding2 toC _ toD _ (a, b, c, d) = tuple $
2368+ liftToEncoding2 toC _ toD _ (a, b, c, d) = E.list id
2191− toEncoding a >*<
2369+ [ toEncoding a
2192− toEncoding b >*<
2370+ , toEncoding b
2193− toC c >*<
2371+ , toC c
2194− toD d
2372+ , toD d
2373+ ]
2195 {-# INLINE liftToEncoding2 #-}
2374 {-# INLINE liftToEncoding2 #-}
2196
2375
2197instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where
2376instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where
@@ -2217,12 +2396,13 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where
2217 return mv
2396 return mv
2218 {-# INLINE liftToJSON2 #-}
2397 {-# INLINE liftToJSON2 #-}
2219
2398
2220− liftToEncoding2 toD _ toE _ (a, b, c, d, e) = tuple $
2399+ liftToEncoding2 toD _ toE _ (a, b, c, d, e) = E.list id
2221− toEncoding a >*<
2400+ [ toEncoding a
2222− toEncoding b >*<
2401+ , toEncoding b
2223− toEncoding c >*<
2402+ , toEncoding c
2224− toD d >*<
2403+ , toD d
2225− toE e
2404+ , toE e
2405+ ]
2226 {-# INLINE liftToEncoding2 #-}
2406 {-# INLINE liftToEncoding2 #-}
2227
2407
2228instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON1 ((,,,,) a b c d) where
2408instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON1 ((,,,,) a b c d) where
@@ -2249,13 +2429,14 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) w
2249 return mv
2429 return mv
2250 {-# INLINE liftToJSON2 #-}
2430 {-# INLINE liftToJSON2 #-}
2251
2431
2252− liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = tuple $
2432+ liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = E.list id
2253− toEncoding a >*<
2433+ [ toEncoding a
2254− toEncoding b >*<
2434+ , toEncoding b
2255− toEncoding c >*<
2435+ , toEncoding c
2256− toEncoding d >*<
2436+ , toEncoding d
2257− toE e >*<
2437+ , toE e
2258− toF f
2438+ , toF f
2439+ ]
2259 {-# INLINE liftToEncoding2 #-}
2440 {-# INLINE liftToEncoding2 #-}
2260
2441
2261instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON1 ((,,,,,) a b c d e) where
2442instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON1 ((,,,,,) a b c d e) where
@@ -2283,14 +2464,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,)
2283 return mv
2464 return mv
2284 {-# INLINE liftToJSON2 #-}
2465 {-# INLINE liftToJSON2 #-}
2285
2466
2286− liftToEncoding2 toF _ toG _ (a, b, c, d, e, f, g) = tuple $
2467+ liftToEncoding2 toF _ toG _ (a, b, c, d, e, f, g) = E.list id
2287− toEncoding a >*<
2468+ [ toEncoding a
2288− toEncoding b >*<
2469+ , toEncoding b
2289− toEncoding c >*<
2470+ , toEncoding c
2290− toEncoding d >*<
2471+ , toEncoding d
2291− toEncoding e >*<
2472+ , toEncoding e
2292− toF f >*<
2473+ , toF f
2293− toG g
2474+ , toG g
2475+ ]
2294 {-# INLINE liftToEncoding2 #-}
2476 {-# INLINE liftToEncoding2 #-}
2295
2477
2296instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON1 ((,,,,,,) a b c d e f) where
2478instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON1 ((,,,,,,) a b c d e f) where
@@ -2319,15 +2501,16 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2
2319 return mv
2501 return mv
2320 {-# INLINE liftToJSON2 #-}
2502 {-# INLINE liftToJSON2 #-}
2321
2503
2322− liftToEncoding2 toG _ toH _ (a, b, c, d, e, f, g, h) = tuple $
2504+ liftToEncoding2 toG _ toH _ (a, b, c, d, e, f, g, h) = E.list id
2323− toEncoding a >*<
2505+ [ toEncoding a
2324− toEncoding b >*<
2506+ , toEncoding b
2325− toEncoding c >*<
2507+ , toEncoding c
2326− toEncoding d >*<
2508+ , toEncoding d
2327− toEncoding e >*<
2509+ , toEncoding e
2328− toEncoding f >*<
2510+ , toEncoding f
2329− toG g >*<
2511+ , toG g
2330− toH h
2512+ , toH h
2513+ ]
2331 {-# INLINE liftToEncoding2 #-}
2514 {-# INLINE liftToEncoding2 #-}
2332
2515
2333instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON1 ((,,,,,,,) a b c d e f g) where
2516instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON1 ((,,,,,,,) a b c d e f g) where
@@ -2357,16 +2540,17 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g)
2357 return mv
2540 return mv
2358 {-# INLINE liftToJSON2 #-}
2541 {-# INLINE liftToJSON2 #-}
2359
2542
2360− liftToEncoding2 toH _ toI _ (a, b, c, d, e, f, g, h, i) = tuple $
2543+ liftToEncoding2 toH _ toI _ (a, b, c, d, e, f, g, h, i) = E.list id
2361− toEncoding a >*<
2544+ [ toEncoding a
2362− toEncoding b >*<
2545+ , toEncoding b
2363− toEncoding c >*<
2546+ , toEncoding c
2364− toEncoding d >*<
2547+ , toEncoding d
2365− toEncoding e >*<
2548+ , toEncoding e
2366− toEncoding f >*<
2549+ , toEncoding f
2367− toEncoding g >*<
2550+ , toEncoding g
2368− toH h >*<
2551+ , toH h
2369− toI i
2552+ , toI i
2553+ ]
2370 {-# INLINE liftToEncoding2 #-}
2554 {-# INLINE liftToEncoding2 #-}
2371
2555
2372instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON1 ((,,,,,,,,) a b c d e f g h) where
2556instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON1 ((,,,,,,,,) a b c d e f g h) where
@@ -2397,17 +2581,18 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2397 return mv
2581 return mv
2398 {-# INLINE liftToJSON2 #-}
2582 {-# INLINE liftToJSON2 #-}
2399
2583
2400− liftToEncoding2 toI _ toJ _ (a, b, c, d, e, f, g, h, i, j) = tuple $
2584+ liftToEncoding2 toI _ toJ _ (a, b, c, d, e, f, g, h, i, j) = E.list id
2401− toEncoding a >*<
2585+ [ toEncoding a
2402− toEncoding b >*<
2586+ , toEncoding b
2403− toEncoding c >*<
2587+ , toEncoding c
2404− toEncoding d >*<
2588+ , toEncoding d
2405− toEncoding e >*<
2589+ , toEncoding e
2406− toEncoding f >*<
2590+ , toEncoding f
2407− toEncoding g >*<
2591+ , toEncoding g
2408− toEncoding h >*<
2592+ , toEncoding h
2409− toI i >*<
2593+ , toI i
2410− toJ j
2594+ , toJ j
2595+ ]
2411 {-# INLINE liftToEncoding2 #-}
2596 {-# INLINE liftToEncoding2 #-}
2412
2597
2413instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON1 ((,,,,,,,,,) a b c d e f g h i) where
2598instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON1 ((,,,,,,,,,) a b c d e f g h i) where
@@ -2439,18 +2624,19 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2439 return mv
2624 return mv
2440 {-# INLINE liftToJSON2 #-}
2625 {-# INLINE liftToJSON2 #-}
2441
2626
2442− liftToEncoding2 toJ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = tuple $
2627+ liftToEncoding2 toJ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = E.list id
2443− toEncoding a >*<
2628+ [ toEncoding a
2444− toEncoding b >*<
2629+ , toEncoding b
2445− toEncoding c >*<
2630+ , toEncoding c
2446− toEncoding d >*<
2631+ , toEncoding d
2447− toEncoding e >*<
2632+ , toEncoding e
2448− toEncoding f >*<
2633+ , toEncoding f
2449− toEncoding g >*<
2634+ , toEncoding g
2450− toEncoding h >*<
2635+ , toEncoding h
2451− toEncoding i >*<
2636+ , toEncoding i
2452− toJ j >*<
2637+ , toJ j
2453− toK k
2638+ , toK k
2639+ ]
2454 {-# INLINE liftToEncoding2 #-}
2640 {-# INLINE liftToEncoding2 #-}
2455
2641
2456instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON1 ((,,,,,,,,,,) a b c d e f g h i j) where
2642instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON1 ((,,,,,,,,,,) a b c d e f g h i j) where
@@ -2483,19 +2669,20 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2483 return mv
2669 return mv
2484 {-# INLINE liftToJSON2 #-}
2670 {-# INLINE liftToJSON2 #-}
2485
2671
2486− liftToEncoding2 toK _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = tuple $
2672+ liftToEncoding2 toK _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = E.list id
2487− toEncoding a >*<
2673+ [ toEncoding a
2488− toEncoding b >*<
2674+ , toEncoding b
2489− toEncoding c >*<
2675+ , toEncoding c
2490− toEncoding d >*<
2676+ , toEncoding d
2491− toEncoding e >*<
2677+ , toEncoding e
2492− toEncoding f >*<
2678+ , toEncoding f
2493− toEncoding g >*<
2679+ , toEncoding g
2494− toEncoding h >*<
2680+ , toEncoding h
2495− toEncoding i >*<
2681+ , toEncoding i
2496− toEncoding j >*<
2682+ , toEncoding j
2497− toK k >*<
2683+ , toK k
2498− toL l
2684+ , toL l
2685+ ]
2499 {-# INLINE liftToEncoding2 #-}
2686 {-# INLINE liftToEncoding2 #-}
2500
2687
2501instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON1 ((,,,,,,,,,,,) a b c d e f g h i j k) where
2688instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON1 ((,,,,,,,,,,,) a b c d e f g h i j k) where
@@ -2529,20 +2716,21 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2529 return mv
2716 return mv
2530 {-# INLINE liftToJSON2 #-}
2717 {-# INLINE liftToJSON2 #-}
2531
2718
2532− liftToEncoding2 toL _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = tuple $
2719+ liftToEncoding2 toL _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = E.list id
2533− toEncoding a >*<
2720+ [ toEncoding a
2534− toEncoding b >*<
2721+ , toEncoding b
2535− toEncoding c >*<
2722+ , toEncoding c
2536− toEncoding d >*<
2723+ , toEncoding d
2537− toEncoding e >*<
2724+ , toEncoding e
2538− toEncoding f >*<
2725+ , toEncoding f
2539− toEncoding g >*<
2726+ , toEncoding g
2540− toEncoding h >*<
2727+ , toEncoding h
2541− toEncoding i >*<
2728+ , toEncoding i
2542− toEncoding j >*<
2729+ , toEncoding j
2543− toEncoding k >*<
2730+ , toEncoding k
2544− toL l >*<
2731+ , toL l
2545− toM m
2732+ , toM m
2733+ ]
2546 {-# INLINE liftToEncoding2 #-}
2734 {-# INLINE liftToEncoding2 #-}
2547
2735
2548instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON1 ((,,,,,,,,,,,,) a b c d e f g h i j k l) where
2736instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON1 ((,,,,,,,,,,,,) a b c d e f g h i j k l) where
@@ -2577,21 +2765,22 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2577 return mv
2765 return mv
2578 {-# INLINE liftToJSON2 #-}
2766 {-# INLINE liftToJSON2 #-}
2579
2767
2580− liftToEncoding2 toM _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = tuple $
2768+ liftToEncoding2 toM _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = E.list id
2581− toEncoding a >*<
2769+ [ toEncoding a
2582− toEncoding b >*<
2770+ , toEncoding b
2583− toEncoding c >*<
2771+ , toEncoding c
2584− toEncoding d >*<
2772+ , toEncoding d
2585− toEncoding e >*<
2773+ , toEncoding e
2586− toEncoding f >*<
2774+ , toEncoding f
2587− toEncoding g >*<
2775+ , toEncoding g
2588− toEncoding h >*<
2776+ , toEncoding h
2589− toEncoding i >*<
2777+ , toEncoding i
2590− toEncoding j >*<
2778+ , toEncoding j
2591− toEncoding k >*<
2779+ , toEncoding k
2592− toEncoding l >*<
2780+ , toEncoding l
2593− toM m >*<
2781+ , toM m
2594− toN n
2782+ , toN n
2783+ ]
2595 {-# INLINE liftToEncoding2 #-}
2784 {-# INLINE liftToEncoding2 #-}
2596
2785
2597instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON1 ((,,,,,,,,,,,,,) a b c d e f g h i j k l m) where
2786instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON1 ((,,,,,,,,,,,,,) a b c d e f g h i j k l m) where
@@ -2627,22 +2816,23 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2627 return mv
2816 return mv
2628 {-# INLINE liftToJSON2 #-}
2817 {-# INLINE liftToJSON2 #-}
2629
2818
2630− liftToEncoding2 toN _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = tuple $
2819+ liftToEncoding2 toN _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = E.list id
2631− toEncoding a >*<
2820+ [ toEncoding a
2632− toEncoding b >*<
2821+ , toEncoding b
2633− toEncoding c >*<
2822+ , toEncoding c
2634− toEncoding d >*<
2823+ , toEncoding d
2635− toEncoding e >*<
2824+ , toEncoding e
2636− toEncoding f >*<
2825+ , toEncoding f
2637− toEncoding g >*<
2826+ , toEncoding g
2638− toEncoding h >*<
2827+ , toEncoding h
2639− toEncoding i >*<
2828+ , toEncoding i
2640− toEncoding j >*<
2829+ , toEncoding j
2641− toEncoding k >*<
2830+ , toEncoding k
2642− toEncoding l >*<
2831+ , toEncoding l
2643− toEncoding m >*<
2832+ , toEncoding m
2644− toN n >*<
2833+ , toN n
2645− toO o
2834+ , toO o
2835+ ]
2646 {-# INLINE liftToEncoding2 #-}
2836 {-# INLINE liftToEncoding2 #-}
2647
2837
2648instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n) => ToJSON1 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n) where
2838instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n) => ToJSON1 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n) where
@@ -2657,27 +2847,26 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2657 toEncoding = toEncoding2
2847 toEncoding = toEncoding2
2658 {-# INLINE toEncoding #-}
2848 {-# INLINE toEncoding #-}
2659
2849
2660−-------------------------------------------------------------------------------
2850+--------------------------------------------------------------------------------
2661−-- pre-bytestring-0.10 compatibility
2662−-------------------------------------------------------------------------------
2663
2851
2664−{-# INLINE lazyToStrictByteString #-}
2852+-- | Wrap a list of pairs as an object.
2665−lazyToStrictByteString :: L.ByteString -> S.ByteString
2853+class Monoid pairs => FromPairs enc pairs | enc -> pairs where
2666−#if MIN_VERSION_bytestring(0,10,0)
2854+ fromPairs :: pairs -> enc
2667−lazyToStrictByteString = L.toStrict
2668−#else
2669−lazyToStrictByteString = packChunks
2670
2855
2671−-- packChunks is taken from the blaze-builder package.
2856+instance (a ~ Value) => FromPairs (Encoding' a) Series where
2857+ fromPairs = E.pairs
2672
2858
2673−-- | Pack the chunks of a lazy bytestring into a single strict bytestring.
2859+instance FromPairs Value (DList Pair) where
2674−packChunks :: L.ByteString -> S.ByteString
2860+ fromPairs = object . toList
2675−packChunks lbs = do
2861+
2676− S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)
2862+-- | Like 'KeyValue' but the value is already converted to JSON
2677− where
2863+-- ('Value' or 'Encoding'), and the result actually represents lists of pairs
2678− copyChunks !L.Empty !_pf = return ()
2864+-- so it can be readily concatenated.
2679− copyChunks !(L.Chunk (S.PS fpbuf o l) lbs') !pf = do
2865+class Monoid kv => KeyValuePair v kv where
2680− withForeignPtr fpbuf $ \pbuf ->
2866+ pair :: String -> v -> kv
2681− copyBytes pf (pbuf `plusPtr` o) l
2867+
2682− copyChunks lbs' (pf `plusPtr` l)
2868+instance (v ~ Value) => KeyValuePair v (DList Pair) where
2683−#endif
2869+ pair k v = DList.singleton (pack k .= v)
2870+
2871+instance (e ~ Encoding) => KeyValuePair e Series where
2872+ pair = E.pairStr
Makefilenew file+2
@@ -0,0 +1,2 @@
1+lint:
2+ ./run-hlint.sh --cpp-include include/ --cpp-file travis/cabal_macros.h Data/ attoparsec-iso8601/ benchmarks/ examples/ ffi/ pure/ tests/
README.markdown+3−4
@@ -15,14 +15,13 @@ Master [git repository](http://github.com/bos/aeson):
15
15
16* `git clone git://github.com/bos/aeson.git`
16* `git clone git://github.com/bos/aeson.git`
17
17
18−There's also a [Mercurial mirror](http://bitbucket.org/bos/aeson):
18+See what's changed in recent (and upcoming) releases:
19
19
20−* `hg clone http://bitbucket.org/bos/aeson`
20+* https://github.com/bos/aeson/blob/master/changelog.md
21
21
22(You can create and contribute changes using either git or Mercurial.)
22(You can create and contribute changes using either git or Mercurial.)
23
23
24
24
25# Authors
25# Authors
26
26
27−This library is written and maintained by Bryan O'Sullivan,
27+This library was originally written by Bryan O'Sullivan.
28−<bos@serpentine.com>.
aeson.cabal+110−65
@@ -1,12 +1,12 @@
1name: aeson
1name: aeson
2−version: 1.0.0.0
2+version: 1.4.6.0
3license: BSD3
3license: BSD3
4license-file: LICENSE
4license-file: LICENSE
5category: Text, Web, JSON
5category: Text, Web, JSON
6copyright: (c) 2011-2016 Bryan O'Sullivan
6copyright: (c) 2011-2016 Bryan O'Sullivan
7 (c) 2011 MailRank, Inc.
7 (c) 2011 MailRank, Inc.
8author: Bryan O'Sullivan <bos@serpentine.com>
8author: Bryan O'Sullivan <bos@serpentine.com>
9−maintainer: Bryan O'Sullivan <bos@serpentine.com>
9+maintainer: Adam Bergmark <adam@bergmark.nl>
10stability: experimental
10stability: experimental
11tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1
11tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1
12synopsis: Fast JSON parsing and encoding
12synopsis: Fast JSON parsing and encoding
@@ -21,23 +21,10 @@ description:
21 To get started, see the documentation for the @Data.Aeson@ module
21 To get started, see the documentation for the @Data.Aeson@ module
22 below.
22 below.
23 .
23 .
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− .
38 (A note on naming: in Greek mythology, Aeson was the father of Jason.)
24 (A note on naming: in Greek mythology, Aeson was the father of Jason.)
39
25
40extra-source-files:
26extra-source-files:
27+ *.yaml
41 README.markdown
28 README.markdown
42 benchmarks/*.cabal
29 benchmarks/*.cabal
43 benchmarks/*.hs
30 benchmarks/*.hs
@@ -46,12 +33,17 @@ extra-source-files:
46 benchmarks/Makefile
33 benchmarks/Makefile
47 benchmarks/Typed/*.hs
34 benchmarks/Typed/*.hs
48 benchmarks/json-data/*.json
35 benchmarks/json-data/*.json
49− include/overlapping-compat.h
36+ cbits/*.c
50 changelog.md
37 changelog.md
51 examples/*.cabal
38 examples/*.cabal
52 examples/*.hs
39 examples/*.hs
53 examples/Twitter/*.hs
40 examples/Twitter/*.hs
41+ ffi/Data/Aeson/Parser/*.hs
54 include/*.h
42 include/*.h
43+ tests/JSONTestSuite/test_parsing/*.json
44+ tests/JSONTestSuite/test_transform/*.json
45+ tests/golden/*.expected
46+ pure/Data/Aeson/Parser/*.hs
55
47
56flag developer
48flag developer
57 description: operate in developer mode
49 description: operate in developer mode
@@ -68,8 +60,14 @@ flag bytestring-builder
68 default: False
60 default: False
69 manual: False
61 manual: False
70
62
63+flag cffi
64+ description: Controls whether to include c-ffi bits or pure haskell. Default to False for security.
65+ default: False
66+ manual: True
67+
71library
68library
72 default-language: Haskell2010
69 default-language: Haskell2010
70+ hs-source-dirs: . attoparsec-iso8601/
73
71
74 exposed-modules:
72 exposed-modules:
75 Data.Aeson
73 Data.Aeson
@@ -78,10 +76,12 @@ library
78 Data.Aeson.Text
76 Data.Aeson.Text
79 Data.Aeson.Types
77 Data.Aeson.Types
80 Data.Aeson.TH
78 Data.Aeson.TH
79+ Data.Aeson.QQ.Simple
81
80
82 Data.Aeson.Encoding.Internal
81 Data.Aeson.Encoding.Internal
83 Data.Aeson.Internal
82 Data.Aeson.Internal
84 Data.Aeson.Internal.Time
83 Data.Aeson.Internal.Time
84+ Data.Aeson.Parser.Internal
85
85
86 -- Deprecated modules
86 -- Deprecated modules
87 exposed-modules:
87 exposed-modules:
@@ -90,55 +90,75 @@ library
90 other-modules:
90 other-modules:
91 Data.Aeson.Encoding.Builder
91 Data.Aeson.Encoding.Builder
92 Data.Aeson.Internal.Functions
92 Data.Aeson.Internal.Functions
93− Data.Aeson.Parser.Internal
93+ Data.Aeson.Parser.Unescape
94 Data.Aeson.Parser.Time
94 Data.Aeson.Parser.Time
95 Data.Aeson.Types.FromJSON
95 Data.Aeson.Types.FromJSON
96 Data.Aeson.Types.Generic
96 Data.Aeson.Types.Generic
97 Data.Aeson.Types.ToJSON
97 Data.Aeson.Types.ToJSON
98 Data.Aeson.Types.Class
98 Data.Aeson.Types.Class
99 Data.Aeson.Types.Internal
99 Data.Aeson.Types.Internal
100+ Data.Attoparsec.Time
101+ Data.Attoparsec.Time.Internal
100
102
103+ -- GHC bundled libs
101 build-depends:
104 build-depends:
102− attoparsec >= 0.13.0.1,
105+ base >= 4.7.0.0 && < 5,
103− base >= 4.5 && < 5,
106+ bytestring >= 0.10.4.0 && < 0.11,
104− base-compat >= 0.9.1 && < 0.10,
105− containers >= 0.2.4.1,
107+ containers >= 0.5.5.1 && < 0.7,
106− deepseq >= 1.3,
108+ deepseq >= 1.3.0.0 && < 1.5,
107− dlist >= 0.2,
108− ghc-prim >= 0.2,
109+ ghc-prim >= 0.2 && < 0.6,
109− hashable >= 1.1.2.0,
110− scientific >= 0.3.4.7 && < 0.4,
111− tagged >=0.8.3 && <0.9,
112− template-haskell >= 2.7,
110+ template-haskell >= 2.9.0.0 && < 2.16,
113− text >= 1.1.1.0,
111+ text >= 1.2.3.0 && < 1.3,
114− time >= 1.1.1.4,
112+ time >= 1.4 && < 1.10
115− time-locale-compat >= 0.1.1 && < 0.2,
116− unordered-containers >= 0.2.5.0,
117− vector >= 0.8
118
113
119− if flag(bytestring-builder)
114+ if impl(ghc >= 8.0)
120− build-depends: bytestring >= 0.9 && < 0.10.4,
115+ build-depends: bytestring >= 0.10.8.1
121− bytestring-builder >= 0.10.4 && < 1
116+
122− else
117+ -- Compat
118+ build-depends:
119+ base-compat >= 0.9.1 && < 0.12,
120+ time-compat >= 1.9.2.2 && < 1.10
121+
122+ if !impl(ghc >= 8.6)
123− build-depends: bytestring >= 0.10.4
123+ build-depends:
124+ contravariant >=1.4.1 && <1.6
124
125
125 if !impl(ghc >= 8.0)
126 if !impl(ghc >= 8.0)
126 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
127 -- `Data.Semigroup` and `Control.Monad.Fail` and `Control.Monad.IO.Class` are available in base only since GHC 8.0 / base 4.9
127 build-depends:
128 build-depends:
128− semigroups >= 0.18.2 && < 0.19,
129+ semigroups >= 0.18.5 && < 0.20,
129− transformers >= 0.2.2.0,
130+ transformers >= 0.3.0.0 && < 0.6,
130− transformers-compat >= 0.3,
131+ transformers-compat >= 0.6.2 && < 0.7,
131 fail == 4.9.*
132 fail == 4.9.*
132
133
133 if !impl(ghc >= 7.10)
134 if !impl(ghc >= 7.10)
134 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
135 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
135− build-depends: nats >= 1 && < 1.2
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
156+
157+ ghc-options: -Wall
136
158
137 if flag(developer)
159 if flag(developer)
138 ghc-options: -Werror
160 ghc-options: -Werror
139 ghc-prof-options: -auto-all
161 ghc-prof-options: -auto-all
140− else
141− ghc-options: -Wall
142
162
143 if flag(fast)
163 if flag(fast)
144 ghc-options: -O0
164 ghc-options: -O0
@@ -146,53 +166,78 @@ library
146 ghc-options: -O2
166 ghc-options: -O2
147
167
148 include-dirs: include
168 include-dirs: include
169+ if impl(ghcjs) || !flag(cffi)
170+ hs-source-dirs: pure
171+ other-modules: Data.Aeson.Parser.UnescapePure
172+ else
173+ c-sources: cbits/unescape_string.c
174+ cpp-options: -DCFFI
175+ hs-source-dirs: ffi
176+ other-modules: Data.Aeson.Parser.UnescapeFFI
149
177
150test-suite tests
178test-suite tests
151 default-language: Haskell2010
179 default-language: Haskell2010
152− type: exitcode-stdio-1.0
180+ type: exitcode-stdio-1.0
153− hs-source-dirs: tests
181+ hs-source-dirs: tests ffi pure
154− main-is: Tests.hs
182+ main-is: Tests.hs
183+ c-sources: cbits/unescape_string.c
184+ ghc-options: -Wall -threaded -rtsopts
185+
155 other-modules:
186 other-modules:
187+ Data.Aeson.Parser.UnescapeFFI
188+ Data.Aeson.Parser.UnescapePure
156 DataFamilies.Properties
189 DataFamilies.Properties
157 DataFamilies.Instances
190 DataFamilies.Instances
158 DataFamilies.Encoders
191 DataFamilies.Encoders
159 DataFamilies.Types
192 DataFamilies.Types
160 Encoders
193 Encoders
194+ ErrorMessages
161 Functions
195 Functions
162 Instances
196 Instances
163 Options
197 Options
198+ PropUtils
164 Properties
199 Properties
200+ PropertyGeneric
201+ PropertyKeys
202+ PropertyRoundTrip
203+ PropertyRTFunctors
204+ PropertyTH
205+ SerializationFormatSpec
165 Types
206 Types
166 UnitTests
207 UnitTests
167 UnitTests.NullaryConstructors
208 UnitTests.NullaryConstructors
168
209
169− ghc-options: -Wall -threaded -rtsopts
170−
171 build-depends:
210 build-depends:
172− HUnit,
173− QuickCheck >= 2.7 && <2.8.3,
211+ QuickCheck >= 2.10.0.1 && < 2.14,
174 aeson,
212 aeson,
213+ integer-logarithms >= 1 && <1.1,
175 attoparsec,
214 attoparsec,
176 base,
215 base,
177 base-compat,
216 base-compat,
178− base-orphans >= 0.5.3 && <0.6,
217+ base-orphans >= 0.5.3 && <0.9,
218+ base16-bytestring,
179 containers,
219 containers,
220+ directory,
180 dlist,
221 dlist,
222+ Diff >= 0.4 && < 0.5,
223+ filepath,
181− generic-deriving >= 1.10 && < 1.11,
224+ generic-deriving >= 1.10 && < 1.14,
182 ghc-prim >= 0.2,
225 ghc-prim >= 0.2,
183 hashable >= 1.2.4.0,
226 hashable >= 1.2.4.0,
184 scientific,
227 scientific,
185 tagged,
228 tagged,
186 template-haskell,
229 template-haskell,
187− test-framework,
230+ tasty,
188− test-framework-hunit,
231+ tasty-golden,
189− test-framework-quickcheck2,
232+ tasty-hunit,
233+ tasty-quickcheck,
190 text,
234 text,
191 time,
235 time,
192− time-locale-compat,
236+ time-compat,
193 unordered-containers,
237 unordered-containers,
238+ uuid-types,
194 vector,
239 vector,
195− quickcheck-instances >=0.3.12
240+ quickcheck-instances >= 0.3.21 && <0.4
196
241
197 if flag(bytestring-builder)
242 if flag(bytestring-builder)
198 build-depends: bytestring >= 0.9 && < 0.10.4,
243 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -202,20 +247,20 @@ test-suite tests
202
247
203 if !impl(ghc >= 8.0)
248 if !impl(ghc >= 8.0)
204 build-depends:
249 build-depends:
205− semigroups >= 0.18.2 && < 0.19,
250+ semigroups >= 0.18.2 && < 0.20,
206 transformers >= 0.2.2.0,
251 transformers >= 0.2.2.0,
207 transformers-compat >= 0.3
252 transformers-compat >= 0.3
208
253
209 if !impl(ghc >= 7.10)
254 if !impl(ghc >= 7.10)
210− build-depends: nats >=1 && <1.2
255+ build-depends: nats >=1 && <1.2,
256+ void >=0.7.2 && <0.8
211
257
212 if impl(ghc >= 7.8)
258 if impl(ghc >= 7.8)
213 build-depends: hashable-time >= 0.2 && <0.3
259 build-depends: hashable-time >= 0.2 && <0.3
214
260
261+ if flag(fast)
262+ ghc-options: -fno-enable-rewrite-rules
263+
215source-repository head
264source-repository head
216 type: git
265 type: git
217 location: git://github.com/bos/aeson.git
266 location: git://github.com/bos/aeson.git
218−
219−source-repository head
220− type: mercurial
221− location: https://bitbucket.org/bos/aeson
attoparsec-iso8601/Data/Attoparsec/Time.hsnew file+148
@@ -0,0 +1,148 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+-- |
5+-- Module: Data.Aeson.Parser.Time
6+-- Copyright: (c) 2015-2016 Bryan O'Sullivan
7+-- License: BSD3
8+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
9+-- Stability: experimental
10+-- Portability: portable
11+--
12+-- Parsers for parsing dates and times.
13+
14+module Data.Attoparsec.Time
15+ (
16+ day
17+ , localTime
18+ , timeOfDay
19+ , timeZone
20+ , utcTime
21+ , zonedTime
22+ ) where
23+
24+import Prelude.Compat
25+
26+import Control.Applicative ((<|>))
27+import Control.Monad (void, when)
28+import Data.Attoparsec.Text as A
29+import Data.Attoparsec.Time.Internal (toPico)
30+import Data.Bits ((.&.))
31+import Data.Char (isDigit, ord)
32+import Data.Fixed (Pico)
33+import Data.Int (Int64)
34+import Data.Maybe (fromMaybe)
35+import Data.Time.Calendar (Day, fromGregorianValid)
36+import Data.Time.Clock (UTCTime(..))
37+import qualified Data.Text as T
38+import qualified Data.Time.LocalTime as Local
39+
40+-- | Parse a date of the form @[+,-]YYYY-MM-DD@.
41+day :: Parser Day
42+day = do
43+ 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"
47+ maybe (fail "invalid date") return (fromGregorianValid (absOrNeg y) m d)
48+
49+-- | Parse a two-digit integer (e.g. day of month, hour).
50+twoDigits :: Parser Int
51+twoDigits = do
52+ a <- digit
53+ b <- digit
54+ let c2d c = ord c .&. 15
55+ return $! c2d a * 10 + c2d b
56+
57+-- | Parse a time of the form @HH:MM[:SS[.SSS]]@.
58+timeOfDay :: Parser Local.TimeOfDay
59+timeOfDay = do
60+ h <- twoDigits
61+ m <- char ':' *> twoDigits
62+ s <- option 0 (char ':' *> seconds)
63+ if h < 24 && m < 60 && s < 61
64+ then return (Local.TimeOfDay h m s)
65+ else fail "invalid time"
66+
67+data T = T {-# UNPACK #-} !Int {-# UNPACK #-} !Int64
68+
69+-- | Parse a count of seconds, with the integer part being two digits
70+-- long.
71+seconds :: Parser Pico
72+seconds = do
73+ real <- twoDigits
74+ mc <- peekChar
75+ case mc of
76+ Just '.' -> do
77+ t <- anyChar *> takeWhile1 isDigit
78+ return $! parsePicos real t
79+ _ -> return $! fromIntegral real
80+ where
81+ parsePicos a0 t = toPico (fromIntegral (t' * 10^n))
82+ where T n t' = T.foldl' step (T 12 (fromIntegral a0)) t
83+ step ma@(T m a) c
84+ | m <= 0 = ma
85+ | otherwise = T (m-1) (10 * a + fromIntegral (ord c) .&. 15)
86+
87+-- | Parse a time zone, and return 'Nothing' if the offset from UTC is
88+-- zero. (This makes some speedups possible.)
89+timeZone :: Parser (Maybe Local.TimeZone)
90+timeZone = do
91+ let maybeSkip c = do ch <- peekChar'; when (ch == c) (void anyChar)
92+ maybeSkip ' '
93+ ch <- satisfy $ \c -> c == 'Z' || c == '+' || c == '-'
94+ if ch == 'Z'
95+ then return Nothing
96+ else do
97+ h <- twoDigits
98+ mm <- peekChar
99+ m <- case mm of
100+ Just ':' -> anyChar *> twoDigits
101+ Just d | isDigit d -> twoDigits
102+ _ -> return 0
103+ let off | ch == '-' = negate off0
104+ | otherwise = off0
105+ off0 = h * 60 + m
106+ case undefined of
107+ _ | off == 0 ->
108+ return Nothing
109+ | off < -720 || off > 840 || m > 59 ->
110+ fail "invalid time zone offset"
111+ | otherwise ->
112+ let !tz = Local.minutesToTimeZone off
113+ in return (Just tz)
114+
115+-- | Parse a date and time, of the form @YYYY-MM-DD HH:MM[:SS[.SSS]]@.
116+-- The space may be replaced with a @T@. The number of seconds is optional
117+-- and may be followed by a fractional component.
118+localTime :: Parser Local.LocalTime
119+localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay
120+ where daySep = satisfy (\c -> c == 'T' || c == ' ')
121+
122+-- | Behaves as 'zonedTime', but converts any time zone offset into a
123+-- UTC time.
124+utcTime :: Parser UTCTime
125+utcTime = do
126+ lt@(Local.LocalTime d t) <- localTime
127+ mtz <- timeZone
128+ case mtz of
129+ Nothing -> let !tt = Local.timeOfDayToTime t
130+ in return (UTCTime d tt)
131+ Just tz -> return $! Local.localTimeToUTC tz lt
132+
133+-- | Parse a date with time zone info. Acceptable formats:
134+--
135+-- @YYYY-MM-DD HH:MM Z@
136+-- @YYYY-MM-DD HH:MM:SS Z@
137+-- @YYYY-MM-DD HH:MM:SS.SSS Z@
138+--
139+-- The first space may instead be a @T@, and the second space is
140+-- optional. The @Z@ represents UTC. The @Z@ may be replaced with a
141+-- time zone offset of the form @+0000@ or @-08:00@, where the first
142+-- two digits are hours, the @:@ is optional and the second two digits
143+-- (also optional) are minutes.
144+zonedTime :: Parser Local.ZonedTime
145+zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)
146+
147+utc :: Local.TimeZone
148+utc = Local.TimeZone 0 False ""
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hsnew file+64
@@ -0,0 +1,64 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+-- |
4+-- Module: Data.Aeson.Internal.Time
5+-- Copyright: (c) 2015-2016 Bryan O'Sullivan
6+-- License: BSD3
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
8+-- Stability: experimental
9+-- Portability: portable
10+
11+module Data.Attoparsec.Time.Internal
12+ (
13+ TimeOfDay64(..)
14+ , fromPico
15+ , toPico
16+ , diffTimeOfDay64
17+ , toTimeOfDay64
18+ ) where
19+
20+import Prelude.Compat
21+
22+import Data.Int (Int64)
23+import Data.Time
24+import Unsafe.Coerce (unsafeCoerce)
25+
26+#if MIN_VERSION_time(1,6,0)
27+
28+import Data.Time.Clock (diffTimeToPicoseconds)
29+
30+#endif
31+
32+import Data.Fixed (Pico, Fixed(MkFixed))
33+
34+#if !MIN_VERSION_time(1,6,0)
35+
36+diffTimeToPicoseconds :: DiffTime -> Integer
37+diffTimeToPicoseconds = unsafeCoerce
38+
39+#endif
40+
41+toPico :: Integer -> Pico
42+toPico = MkFixed
43+
44+fromPico :: Pico -> Integer
45+fromPico (MkFixed i) = i
46+
47+-- | Like TimeOfDay, but using a fixed-width integer for seconds.
48+data TimeOfDay64 = TOD {-# UNPACK #-} !Int
49+ {-# UNPACK #-} !Int
50+ {-# UNPACK #-} !Int64
51+
52+posixDayLength :: DiffTime
53+posixDayLength = 86400
54+
55+diffTimeOfDay64 :: 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
62+
63+toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
64+toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
attoparsec-iso8601/LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2011, MailRank, Inc.
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions
7+are met:
8+
9+1. Redistributions of source code must retain the above copyright
10+ notice, this list of conditions and the following disclaimer.
11+
12+2. Redistributions in binary form must reproduce the above copyright
13+ notice, this list of conditions and the following disclaimer in the
14+ documentation and/or other materials provided with the distribution.
15+
16+3. Neither the name of the author nor the names of his contributors
17+ may be used to endorse or promote products derived from this software
18+ without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
21+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
24+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30+POSSIBILITY OF SUCH DAMAGE.
attoparsec-iso8601/README.mdnew file+19
@@ -0,0 +1,19 @@
1+Parsing of ISO 8601 dates.
2+
3+This package is used to parse dates in aeson. It is split into a
4+separate package to be shared by other projects that want to parse
5+dates like aeson does.
6+
7+For now, this project is located in the aeson repository and aeson
8+itself uses the source of this package without pulling in the package
9+as a dependency.
10+
11+## Stability
12+
13+Since aeson depends on this package we want to be very careful about
14+changing the format.
15+
16+There may be breaking changes if we find that the format is
17+incorrectly too lenient. We consider widening of the allowed input a
18+non-breaking addition since all previously valid input will still
19+parse correctly.
attoparsec-iso8601/attoparsec-iso8601.cabalnew file+55
@@ -0,0 +1,55 @@
1+name: attoparsec-iso8601
2+version: 1.0.1.0
3+synopsis: Parsing of ISO 8601 dates, originally from aeson.
4+description: Parsing of ISO 8601 dates, originally from aeson.
5+license: BSD3
6+license-file: LICENSE
7+category: Parsing
8+copyright: (c) 2011-2016 Bryan O'Sullivan
9+ (c) 2011 MailRank, Inc.
10+author: Bryan O'Sullivan <bos@serpentine.com>
11+maintainer: Adam Bergmark <adam@bergmark.nl>
12+stability: experimental
13+cabal-version: >=1.10
14+homepage: https://github.com/bos/aeson
15+bug-reports: https://github.com/bos/aeson/issues
16+build-type: Simple
17+extra-source-files:
18+ README.md
19+
20+flag developer
21+ description: operate in developer mode
22+ default: False
23+ manual: True
24+
25+flag fast
26+ description: compile without optimizations
27+ default: False
28+ manual: True
29+
30+library
31+ hs-source-dirs: .
32+ default-language: Haskell2010
33+ ghc-options: -Wall
34+ exposed-modules:
35+ Data.Attoparsec.Time.Internal
36+ Data.Attoparsec.Time
37+ build-depends:
38+ attoparsec >= 0.13.0.1,
39+ base >= 4.7 && < 5,
40+ base-compat >= 0.9.1 && < 0.12,
41+ text >= 1.1.1.0,
42+ time >= 1.1.1.4
43+
44+ if flag(fast)
45+ ghc-options: -O0
46+ else
47+ ghc-options: -O2
48+
49+ if flag(developer)
50+ ghc-options: -Werror
51+ ghc-prof-options: -auto-all
52+
53+source-repository head
54+ type: git
55+ location: git://github.com/bos/aeson.git
attoparsec-iso8601/changelog.mdnew file+5
@@ -0,0 +1,5 @@
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.hsdeleted−229
@@ -1,229 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE DeriveGeneric #-}
3−{-# LANGUAGE TemplateHaskell #-}
4−
5−module Main (main) where
6−
7−import Prelude ()
8−import Prelude.Compat
9−
10−import Control.DeepSeq (NFData, rnf, deepseq)
11−import Criterion.Main hiding (defaultOptions)
12−import Data.Aeson.Encode
13−import Data.Aeson.TH
14−import Data.Aeson.Types
15−import Data.Data (Data)
16−import Data.Typeable (Typeable)
17−import GHC.Generics (Generic)
18−import Options
19−import qualified Data.Aeson.Generic as G (fromJSON, toJSON)
20−
21−--------------------------------------------------------------------------------
22−
23−data D a = Nullary
24− | Unary Int
25− | Product String Char a
26− | Record { testOne :: Double
27− , testTwo :: Bool
28− , testThree :: D a
29− }
30− deriving (Show, Eq, Data, Typeable)
31−
32−deriveJSON opts ''D
33−
34−instance NFData a => NFData (D a) where
35− rnf Nullary = ()
36− rnf (Unary n) = rnf n
37− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
38− rnf (Record d b y) = d `deepseq` b `deepseq` rnf y
39−
40−type T = D (D (D ()))
41−
42−d :: T
43−d = Record
44− { testOne = 1234.56789
45− , testTwo = True
46− , testThree = Product "Hello World!" 'a' $
47− Record
48− { testOne = 9876.54321
49− , testTwo = False
50− , testThree = Product "Yeehaa!!!" '\n' Nullary
51− }
52− }
53−
54−--------------------------------------------------------------------------------
55−
56−data D' a = Nullary'
57− | Unary' Int
58− | Product' String Char a
59− | Record' { testOne' :: Double
60− , testTwo' :: Bool
61− , testThree' :: D' a
62− }
63− deriving (Show, Eq, Generic, Data, Typeable)
64−
65−instance ToJSON a => ToJSON (D' a) where
66− toJSON = genericToJSON opts
67−
68−instance FromJSON a => FromJSON (D' a) where
69− parseJSON = genericParseJSON opts
70−
71−instance NFData a => NFData (D' a) where
72− rnf Nullary' = ()
73− rnf (Unary' n) = rnf n
74− rnf (Product' s c x) = s `deepseq` c `deepseq` rnf x
75− rnf (Record' d b y) = d `deepseq` b `deepseq` rnf y
76−
77−type T' = D' (D' (D' ()))
78−
79−d' :: T'
80−d' = Record'
81− { testOne' = 1234.56789
82− , testTwo' = True
83− , testThree' = Product' "Hello World!" 'a' $
84− Record'
85− { testOne' = 9876.54321
86− , testTwo' = False
87− , testThree' = Product' "Yeehaa!!!" '\n' Nullary'
88− }
89− }
90−
91−--------------------------------------------------------------------------------
92−
93−data BigRecord = BigRecord
94− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
95− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
96− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
97− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
98− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
99− } deriving (Show, Eq, Generic, Data, Typeable)
100−
101−instance NFData BigRecord
102−
103−bigRecord = BigRecord 1 2 3 4 5
104− 6 7 8 9 10
105− 11 12 13 14 15
106− 16 17 18 19 20
107− 21 22 23 24 25
108−
109−gBigRecordToJSON :: BigRecord -> Value
110−gBigRecordToJSON = genericToJSON opts
111−
112−gBigRecordFromJSON :: Value -> Result BigRecord
113−gBigRecordFromJSON = parse $ genericParseJSON opts
114−
115−thBigRecordToJSON :: BigRecord -> Value
116−thBigRecordToJSON = $(mkToJSON opts ''BigRecord)
117−
118−thBigRecordFromJSON :: Value -> Result BigRecord
119−thBigRecordFromJSON = parse $(mkParseJSON opts ''BigRecord)
120−
121−--------------------------------------------------------------------------------
122−
123−data BigProduct = BigProduct
124− !Int !Int !Int !Int !Int
125− !Int !Int !Int !Int !Int
126− !Int !Int !Int !Int !Int
127− !Int !Int !Int !Int !Int
128− !Int !Int !Int !Int !Int
129− deriving (Show, Eq, Generic, Data, Typeable)
130−
131−instance NFData BigProduct
132−
133−bigProduct = BigProduct 1 2 3 4 5
134− 6 7 8 9 10
135− 11 12 13 14 15
136− 16 17 18 19 20
137− 21 22 23 24 25
138−
139−gBigProductToJSON :: BigProduct -> Value
140−gBigProductToJSON = genericToJSON opts
141−
142−gBigProductFromJSON :: Value -> Result BigProduct
143−gBigProductFromJSON = parse $ genericParseJSON opts
144−
145−thBigProductToJSON :: BigProduct -> Value
146−thBigProductToJSON = $(mkToJSON opts ''BigProduct)
147−
148−thBigProductFromJSON :: Value -> Result BigProduct
149−thBigProductFromJSON = parse $(mkParseJSON opts ''BigProduct)
150−
151−--------------------------------------------------------------------------------
152−
153−data BigSum = F01 | F02 | F03 | F04 | F05
154− | F06 | F07 | F08 | F09 | F10
155− | F11 | F12 | F13 | F14 | F15
156− | F16 | F17 | F18 | F19 | F20
157− | F21 | F22 | F23 | F24 | F25
158− deriving (Show, Eq, Generic, Data, Typeable)
159−
160−instance NFData BigSum
161−
162−bigSum = F25
163−
164−gBigSumToJSON :: BigSum -> Value
165−gBigSumToJSON = genericToJSON opts
166−
167−gBigSumFromJSON :: Value -> Result BigSum
168−gBigSumFromJSON = parse $ genericParseJSON opts
169−
170−thBigSumToJSON :: BigSum -> Value
171−thBigSumToJSON = $(mkToJSON opts ''BigSum)
172−
173−thBigSumFromJSON :: Value -> Result BigSum
174−thBigSumFromJSON = parse $(mkParseJSON opts ''BigSum)
175−
176−--------------------------------------------------------------------------------
177−
178−type FJ a = Value -> Result a
179−
180−main :: IO ()
181−main = defaultMain
182− [ let v = toJSON d
183− in (d, d', v) `deepseq`
184− bgroup "D"
185− [ group "toJSON" (nf toJSON d)
186− (nf G.toJSON d)
187− (nf toJSON d')
188− , group "fromJSON" (nf ( fromJSON :: FJ T ) v)
189− (nf (G.fromJSON :: FJ T ) v)
190− (nf ( fromJSON :: FJ T') v)
191− ]
192− , let v = thBigRecordToJSON bigRecord
193− in bigRecord `deepseq` v `deepseq`
194− bgroup "BigRecord"
195− [ group "toJSON" (nf thBigRecordToJSON bigRecord)
196− (nf G.toJSON bigRecord)
197− (nf gBigRecordToJSON bigRecord)
198− , group "fromJSON" (nf (thBigRecordFromJSON :: FJ BigRecord) v)
199− (nf (G.fromJSON :: FJ BigRecord) v)
200− (nf (gBigRecordFromJSON :: FJ BigRecord) v)
201− ]
202− , let v = thBigProductToJSON bigProduct
203− in bigProduct `deepseq` v `deepseq`
204− bgroup "BigProduct"
205− [ group "toJSON" (nf thBigProductToJSON bigProduct)
206− (nf G.toJSON bigProduct)
207− (nf gBigProductToJSON bigProduct)
208− , group "fromJSON" (nf (thBigProductFromJSON :: FJ BigProduct) v)
209− (nf (G.fromJSON :: FJ BigProduct) v)
210− (nf (gBigProductFromJSON :: FJ BigProduct) v)
211− ]
212− , let v = thBigSumToJSON bigSum
213− in bigSum `deepseq` v `deepseq`
214− bgroup "BigSum"
215− [ group "toJSON" (nf thBigSumToJSON bigSum)
216− (nf G.toJSON bigSum)
217− (nf gBigSumToJSON bigSum)
218− , group "fromJSON" (nf (thBigSumFromJSON :: FJ BigSum) v)
219− (nf (G.fromJSON :: FJ BigSum) v)
220− (nf (gBigSumFromJSON :: FJ BigSum) v)
221− ]
222− ]
223−
224−group n th syb gen = bcompare
225− [ bgroup n [ bench "th" th
226− , bench "syb" syb
227− , bench "generic" gen
228− ]
229− ]
benchmarks/AesonEncode.hs+4−5
@@ -1,14 +1,13 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3
4
4module Main (main) where
5module Main (main) where
5
6
6−import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.DeepSeq
9import Control.DeepSeq
10−import Control.Exception
10+import Control.Monad (forM_)
11−import Control.Monad
12import Data.Aeson
11import Data.Aeson
13import Data.Attoparsec.ByteString (IResult(..), parseWith)
12import Data.Attoparsec.ByteString (IResult(..), parseWith)
14import Data.Char (isDigit)
13import Data.Char (isDigit)
@@ -25,7 +24,7 @@ main = do
25 (c:a) -> (c,a)
24 (c:a) -> (c,a)
26 [] -> error "Unexpected empty list"
25 [] -> error "Unexpected empty list"
27 let count = read cnt :: Int
26 let count = read cnt :: Int
28− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
27+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
29 putStrLn $ arg ++ ":"
28 putStrLn $ arg ++ ":"
30 let refill = B.hGet h 16384
29 let refill = B.hGet h 16384
31 result0 <- parseWith refill json =<< refill
30 result0 <- parseWith refill json =<< refill
@@ -35,7 +34,7 @@ main = do
35 start <- getCurrentTime
34 start <- getCurrentTime
36 let loop !n r
35 let loop !n r
37 | n >= count = return ()
36 | n >= count = return ()
38− | otherwise = {-# SCC "loop" #-} do
37+ | otherwise = {-# SCC "loop" #-}
39 rnf (encode r) `seq` loop (n+1) r
38 rnf (encode r) `seq` loop (n+1) r
40 loop 0 r0
39 loop 0 r0
41 delta <- flip diffUTCTime start `fmap` getCurrentTime
40 delta <- flip diffUTCTime start `fmap` getCurrentTime
benchmarks/AesonFoldable.hs+14−42
@@ -1,16 +1,14 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2−{-# LANGUAGE PackageImports #-}
3
3
4module Main (main) where
4module Main (main) where
5
5
6import Criterion.Main
6import Criterion.Main
7
7
8−import Prelude ()
9import Prelude.Compat
8import Prelude.Compat
10
9
11import Data.Foldable (toList)
10import Data.Foldable (toList)
12−import qualified "aeson" Data.Aeson as A
11+import qualified Data.Aeson as A
13−import qualified "aeson-benchmarks" Data.Aeson as B
14import qualified Data.Sequence as S
12import qualified Data.Sequence as S
15import qualified Data.Vector as V
13import qualified Data.Vector as V
16import qualified Data.Vector.Unboxed as U
14import qualified Data.Vector.Unboxed as U
@@ -21,10 +19,6 @@ import qualified Data.Vector.Unboxed as U
21
19
22newtype L f = L { getL :: f Int }
20newtype L f = L { getL :: f Int }
23
21
24−instance Foldable f => B.ToJSON (L f) where
25− toJSON = error "do not use this"
26− toEncoding = B.toEncoding . toList . getL
27−
28instance Foldable f => A.ToJSON (L f) where
22instance Foldable f => A.ToJSON (L f) where
29 toJSON = error "do not use this"
23 toJSON = error "do not use this"
30 toEncoding = A.toEncoding . toList . getL
24 toEncoding = A.toEncoding . toList . getL
@@ -35,10 +29,6 @@ instance Foldable f => A.ToJSON (L f) where
35
29
36newtype F f = F { getF :: f Int }
30newtype F f = F { getF :: f Int }
37
31
38−instance Foldable f => B.ToJSON (F f) where
39− toJSON = error "do not use this"
40− toEncoding = B.foldable . getF
41−
42instance Foldable f => A.ToJSON (F f) where
32instance Foldable f => A.ToJSON (F f) where
43 toJSON = error "do not use this"
33 toJSON = error "do not use this"
44 toEncoding = A.foldable . getF
34 toEncoding = A.foldable . getF
@@ -63,52 +53,34 @@ valueUVector = U.fromList valueList
63-- Main
53-- Main
64-------------------------------------------------------------------------------
54-------------------------------------------------------------------------------
65
55
66−benchEncodeA
56+benchEncode
67 :: A.ToJSON a
57 :: A.ToJSON a
68 => String
58 => String
69 -> a
59 -> a
70 -> Benchmark
60 -> Benchmark
71−benchEncodeA name val
61+benchEncode name val
72 = bench ("A " ++ name) $ nf A.encode val
62 = bench ("A " ++ name) $ nf A.encode val
73
63
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−
82main :: IO ()
64main :: IO ()
83main = defaultMain
65main = defaultMain
84 [ bgroup "encode"
66 [ bgroup "encode"
85 [ bgroup "List"
67 [ bgroup "List"
86− [ benchEncodeB "-" valueList
87− , benchEncodeB "L" $ L valueList
88− , benchEncodeB "F" $ F valueList
89− , benchEncodeA "-" valueList
68+ [ benchEncode "-" valueList
90− , benchEncodeA "L" $ L valueList
69+ , benchEncode "L" $ L valueList
91− , benchEncodeA "F" $ F valueList
70+ , benchEncode "F" $ F valueList
92 ]
71 ]
93 , bgroup "Seq"
72 , bgroup "Seq"
94− [ benchEncodeB "-" valueSeq
95− , benchEncodeB "L" $ L valueSeq
96− , benchEncodeB "F" $ F valueSeq
97− , benchEncodeA "-" valueSeq
73+ [ benchEncode "-" valueSeq
98− , benchEncodeA "L" $ L valueSeq
74+ , benchEncode "L" $ L valueSeq
99− , benchEncodeA "F" $ F valueSeq
75+ , benchEncode "F" $ F valueSeq
100 ]
76 ]
101 , bgroup "Vector"
77 , bgroup "Vector"
102− [ benchEncodeB "-" valueVector
103− , benchEncodeB "L" $ L valueVector
104− , benchEncodeB "F" $ F valueVector
105− , benchEncodeA "-" valueVector
78+ [ benchEncode "-" valueVector
106− , benchEncodeA "L" $ L valueVector
79+ , benchEncode "L" $ L valueVector
107− , benchEncodeA "F" $ F valueVector
80+ , benchEncode "F" $ F valueVector
108 ]
81 ]
109 , bgroup "Vector.Unboxed"
82 , bgroup "Vector.Unboxed"
110− [ benchEncodeB "-" valueUVector
111− , benchEncodeA "-" valueUVector
83+ [ benchEncode "-" valueUVector
112 ]
84 ]
113 ]
85 ]
114 ]
86 ]
benchmarks/AesonMap.hs+39−56
@@ -1,13 +1,11 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3−{-# LANGUAGE PackageImports #-}
4{-# LANGUAGE RankNTypes #-}
3{-# LANGUAGE RankNTypes #-}
5
4
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
7
6
8module Main (main) where
7module Main (main) where
9
8
10−import Prelude ()
11import Prelude.Compat
9import Prelude.Compat
12
10
13import Control.DeepSeq
11import Control.DeepSeq
@@ -15,9 +13,8 @@ import Criterion.Main
15import Data.Hashable
13import Data.Hashable
16import Data.Proxy (Proxy (..))
14import Data.Proxy (Proxy (..))
17import Data.Tagged (Tagged (..))
15import Data.Tagged (Tagged (..))
18−import qualified "aeson" Data.Aeson as A
16+import Data.Aeson
19−import qualified "aeson-benchmarks" Data.Aeson as B
20−import qualified "aeson-benchmarks" Data.Aeson.Types as B (fromJSONKeyCoerce)
17+import Data.Aeson.Types (fromJSONKeyCoerce)
21import qualified Data.ByteString.Lazy as LBS
18import qualified Data.ByteString.Lazy as LBS
22import qualified Data.HashMap.Strict as HM
19import qualified Data.HashMap.Strict as HM
23import qualified Data.Map as M
20import qualified Data.Map as M
@@ -47,10 +44,10 @@ instance NFData T1 where
47instance Hashable T1 where
44instance Hashable T1 where
48 hashWithSalt salt (T1 t) = hashWithSalt salt t
45 hashWithSalt salt (T1 t) = hashWithSalt salt t
49
46
50−instance B.FromJSON T1 where
47+instance FromJSON T1 where
51− parseJSON = B.withText "T1" $ pure . T1
48+ parseJSON = withText "T1" $ pure . T1
52−instance B.FromJSONKey T1 where
49+instance FromJSONKey T1 where
53− fromJSONKey = B.FromJSONKeyText T1
50+ fromJSONKey = FromJSONKeyText T1
54
51
55-------------------------------------------------------------------------------
52-------------------------------------------------------------------------------
56-- Coerce
53-- Coerce
@@ -64,10 +61,10 @@ instance NFData T2 where
64instance Hashable T2 where
61instance Hashable T2 where
65 hashWithSalt salt (T2 t) = hashWithSalt salt t
62 hashWithSalt salt (T2 t) = hashWithSalt salt t
66
63
67−instance B.FromJSON T2 where
64+instance FromJSON T2 where
68− parseJSON = B.withText "T2" $ pure . T2
65+ parseJSON = withText "T2" $ pure . T2
69−instance B.FromJSONKey T2 where
66+instance FromJSONKey T2 where
70− fromJSONKey = B.fromJSONKeyCoerce
67+ fromJSONKey = fromJSONKeyCoerce
71
68
72-------------------------------------------------------------------------------
69-------------------------------------------------------------------------------
73-- TextParser
70-- TextParser
@@ -81,10 +78,10 @@ instance NFData T3 where
81instance Hashable T3 where
78instance Hashable T3 where
82 hashWithSalt salt (T3 t) = hashWithSalt salt t
79 hashWithSalt salt (T3 t) = hashWithSalt salt t
83
80
84−instance B.FromJSON T3 where
81+instance FromJSON T3 where
85− parseJSON = B.withText "T3" $ pure . T3
82+ parseJSON = withText "T3" $ pure . T3
86−instance B.FromJSONKey T3 where
83+instance FromJSONKey T3 where
87− fromJSONKey = B.FromJSONKeyTextParser (pure . T3)
84+ fromJSONKey = FromJSONKeyTextParser (pure . T3)
88
85
89-------------------------------------------------------------------------------
86-------------------------------------------------------------------------------
90-- Values
87-- Values
@@ -97,40 +94,30 @@ value1000 = value 1000
97value10000 = value 10000
94value10000 = value 10000
98
95
99encodedValue10 :: LBS.ByteString
96encodedValue10 :: LBS.ByteString
100−encodedValue10 = B.encode $ value10
97+encodedValue10 = encode value10
101
98
102encodedValue100 :: LBS.ByteString
99encodedValue100 :: LBS.ByteString
103−encodedValue100 = B.encode $ value100
100+encodedValue100 = encode value100
104
101
105encodedValue1000 :: LBS.ByteString
102encodedValue1000 :: LBS.ByteString
106−encodedValue1000 = B.encode $ value1000
103+encodedValue1000 = encode value1000
107
104
108encodedValue10000 :: LBS.ByteString
105encodedValue10000 :: LBS.ByteString
109−encodedValue10000 = B.encode $ value10000
106+encodedValue10000 = encode value10000
110
107
111-------------------------------------------------------------------------------
108-------------------------------------------------------------------------------
112-- Helpers
109-- Helpers
113-------------------------------------------------------------------------------
110-------------------------------------------------------------------------------
114
111
115−decodeHMB
112+decodeHM
116− :: (B.FromJSONKey k, Eq k, Hashable k)
113+ :: (FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
117 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
114 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
118−decodeHMB _ = B.decode
115+decodeHM _ = decode
119
116
120−decodeHMA
117+decodeMap
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)
118+ :: (FromJSON (M.Map k T.Text), Ord k)
132 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
119 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
133−decodeMapA _ = A.decode
120+decodeMap _ = decode
134
121
135proxyText :: Proxy T.Text
122proxyText :: Proxy T.Text
136proxyText = Proxy
123proxyText = Proxy
@@ -156,15 +143,14 @@ benchDecodeHM
156 -> LBS.ByteString
143 -> LBS.ByteString
157 -> Benchmark
144 -> Benchmark
158benchDecodeHM name val = bgroup name
145benchDecodeHM name val = bgroup name
159− [ bench "Text" $ nf (decodeHMB proxyText) val
146+ [ bench "Text" $ nf (decodeHM proxyText) val
160− , bench "Identity" $ nf (decodeHMB proxyT1) val
147+ , bench "Identity" $ nf (decodeHM proxyT1) val
161− , bench "Coerce" $ nf (decodeHMB proxyT2) val
148+ , bench "Coerce" $ nf (decodeHM proxyT2) val
162− , bench "Parser" $ nf (decodeHMB proxyT3) val
149+ , bench "Parser" $ nf (decodeHM proxyT3) val
163− , bench "aeson-0.11" $ nf (decodeHMA proxyText) val
164− , bench "Tagged Text" $ nf (decodeHMB $ proxyTagged proxyText) val
150+ , bench "Tagged Text" $ nf (decodeHM $ proxyTagged proxyText) val
165− , bench "Tagged Identity" $ nf (decodeHMB $ proxyTagged proxyT1) val
151+ , bench "Tagged Identity" $ nf (decodeHM $ proxyTagged proxyT1) val
166− , bench "Tagged Coerce" $ nf (decodeHMB $ proxyTagged proxyT2) val
152+ , bench "Tagged Coerce" $ nf (decodeHM $ proxyTagged proxyT2) val
167− , bench "Tagged Parser" $ nf (decodeHMB $ proxyTagged proxyT3) val
153+ , bench "Tagged Parser" $ nf (decodeHM $ proxyTagged proxyT3) val
168 ]
154 ]
169
155
170benchDecodeMap
156benchDecodeMap
@@ -172,11 +158,10 @@ benchDecodeMap
172 -> LBS.ByteString
158 -> LBS.ByteString
173 -> Benchmark
159 -> Benchmark
174benchDecodeMap name val = bgroup name
160benchDecodeMap name val = bgroup name
175− [ bench "Text" $ nf (decodeMapB proxyText) val
161+ [ bench "Text" $ nf (decodeMap proxyText) val
176− , bench "Identity" $ nf (decodeMapB proxyT1) val
162+ , bench "Identity" $ nf (decodeMap proxyT1) val
177− , bench "Coerce" $ nf (decodeMapB proxyT2) val
163+ , bench "Coerce" $ nf (decodeMap proxyT2) val
178− , bench "Parser" $ nf (decodeMapB proxyT3) val
164+ , bench "Parser" $ nf (decodeMap proxyT3) val
179− , bench "aeson-0.11" $ nf (decodeMapA proxyText) val
180 ]
165 ]
181
166
182benchEncodeHM
167benchEncodeHM
@@ -184,8 +169,7 @@ benchEncodeHM
184 -> HM.HashMap T.Text T.Text
169 -> HM.HashMap T.Text T.Text
185 -> Benchmark
170 -> Benchmark
186benchEncodeHM name val = bgroup name
171benchEncodeHM name val = bgroup name
187− [ bench "Text" $ nf B.encode val
172+ [ bench "Text" $ nf encode val
188− , bench "aeson-0.11" $ nf A.encode val
189 ]
173 ]
190
174
191benchEncodeMap
175benchEncodeMap
@@ -193,8 +177,7 @@ benchEncodeMap
193 -> HM.HashMap T.Text T.Text
177 -> HM.HashMap T.Text T.Text
194 -> Benchmark
178 -> Benchmark
195benchEncodeMap name val = bgroup name
179benchEncodeMap name val = bgroup name
196− [ bench "Text" $ nf B.encode val'
180+ [ bench "Text" $ nf encode val'
197− , bench "aeson-0.11" $ nf A.encode val'
198 ]
181 ]
199 where
182 where
200 val' :: M.Map T.Text T.Text
183 val' :: M.Map T.Text T.Text
benchmarks/AesonParse.hs+3−5
@@ -1,14 +1,12 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3−{-# LANGUAGE PackageImports #-}
4
4
5module Main (main) where
5module Main (main) where
6
6
7−import Prelude ()
8import Prelude.Compat
7import Prelude.Compat
9
8
10−import "aeson-benchmarks" Data.Aeson
9+import Data.Aeson
11−import Control.Exception
12import Control.Monad
10import Control.Monad
13import Data.Attoparsec.ByteString (IResult(..), parseWith)
11import Data.Attoparsec.ByteString (IResult(..), parseWith)
14import Data.Time.Clock
12import Data.Time.Clock
@@ -21,7 +19,7 @@ main = do
21 (bs:cnt:args) <- getArgs
19 (bs:cnt:args) <- getArgs
22 let count = read cnt :: Int
20 let count = read cnt :: Int
23 blkSize = read bs
21 blkSize = read bs
24− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
22+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
25 putStrLn $ arg ++ ":"
23 putStrLn $ arg ++ ":"
26 start <- getCurrentTime
24 start <- getCurrentTime
27 let loop !good !bad
25 let loop !good !bad
benchmarks/AesonTuples.hs+1−2
@@ -1,8 +1,7 @@
1−module Main where
1+{-# LANGUAGE NoImplicitPrelude #-}
2
2
3module Main (main) where
3module Main (main) where
4
4
5−import Prelude ()
6import Prelude.Compat
5import Prelude.Compat
7
6
8import Control.DeepSeq (deepseq)
7import Control.DeepSeq (deepseq)
benchmarks/Auto/G/BigProduct.hsnew file+33
@@ -0,0 +1,33 @@
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.hsnew file+33
@@ -0,0 +1,33 @@
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.hsnew file+28
@@ -0,0 +1,28 @@
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.hsnew file+44
@@ -0,0 +1,44 @@
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.hsnew file+27
@@ -0,0 +1,27 @@
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.hsnew file+27
@@ -0,0 +1,27 @@
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.hsnew file+22
@@ -0,0 +1,22 @@
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.hsnew file+38
@@ -0,0 +1,38 @@
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.hsnew file+73
@@ -0,0 +1,73 @@
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+10−5
@@ -1,22 +1,25 @@
1−{-# LANGUAGE PackageImports #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3
4
4module Main (main) where
5module Main (main) where
5
6
6−import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Compare.BufferBuilder ()
9import Compare.BufferBuilder ()
10−import Compare.JsonBuilder ()
11import Criterion.Main
10import Criterion.Main
12import Data.BufferBuilder.Json
11import Data.BufferBuilder.Json
13−import Data.Json.Builder
14import Twitter
12import Twitter
15import Twitter.Manual ()
13import Twitter.Manual ()
16import Typed.Common
14import Typed.Common
17−import qualified "aeson-benchmarks" Data.Aeson as Aeson
15+import qualified Data.Aeson as Aeson
18import qualified Compare.JsonBench as JsonBench
16import qualified Compare.JsonBench as JsonBench
19
17
18+#ifdef MIN_VERSION_json_builder
19+import Data.Json.Builder
20+import Compare.JsonBuilder ()
21+#endif
22+
20main :: IO ()
23main :: IO ()
21main =
24main =
22 defaultMain [
25 defaultMain [
@@ -24,7 +27,9 @@ main =
24 bgroup "twitter" [
27 bgroup "twitter" [
25 bench "aeson" $ nf Aeson.encode twtr
28 bench "aeson" $ nf Aeson.encode twtr
26 , bench "buffer-builder" $ nf encodeJson twtr
29 , bench "buffer-builder" $ nf encodeJson twtr
30+#ifdef MIN_VERSION_json_builder
27 , bench "json-builder" $ nf toJsonLBS twtr
31 , bench "json-builder" $ nf toJsonLBS twtr
32+#endif
28 ]
33 ]
29 , JsonBench.benchmarks
34 , JsonBench.benchmarks
30 ]
35 ]
benchmarks/Compare/BufferBuilder.hs+2−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE RecordWildCards #-}
3
4
@@ -6,13 +7,11 @@
6
7
7module Compare.BufferBuilder () where
8module Compare.BufferBuilder () where
8
9
9−import Prelude ()
10+import Prelude.Compat hiding ((<>))
10−import Prelude.Compat
11
11
12import Data.BufferBuilder.Json
12import Data.BufferBuilder.Json
13import Data.Int (Int64)
13import Data.Int (Int64)
14import Data.Monoid ((<>))
14import Data.Monoid ((<>))
15−import Prelude hiding (id)
16import Twitter
15import Twitter
17import qualified Data.BufferBuilder.Utf8 as UB
16import qualified Data.BufferBuilder.Utf8 as UB
18
17
benchmarks/Compare/JsonBench.hs+18−10
@@ -1,18 +1,18 @@
1−-- Adapted from a buffer-builder benchmark:
1+{-# LANGUAGE CPP #-}
2−--
3−-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
4−
5{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE BangPatterns #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7{-# LANGUAGE MagicHash #-}
3{-# LANGUAGE MagicHash #-}
4+{-# LANGUAGE NoImplicitPrelude #-}
8{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE RecordWildCards #-}
6{-# LANGUAGE RecordWildCards #-}
10{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
11
8
9+-- Adapted from a buffer-builder benchmark:
10+--
11+-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
12+
12module Compare.JsonBench (benchmarks) where
13module Compare.JsonBench (benchmarks) where
13
14
14−import Prelude ()
15+import Prelude.Compat hiding ((<>))
15−import Prelude.Compat
16
16
17import Control.DeepSeq (NFData(..))
17import Control.DeepSeq (NFData(..))
18import Criterion
18import Criterion
@@ -20,9 +20,13 @@ import Data.Aeson ((.:))
20import Data.Monoid ((<>))
20import Data.Monoid ((<>))
21import Data.Text (Text)
21import Data.Text (Text)
22import Typed.Common (load)
22import Typed.Common (load)
23+import qualified Control.Monad.Fail as Fail
23import qualified Data.Aeson as Aeson
24import qualified Data.Aeson as Aeson
24import qualified Data.BufferBuilder.Json as Json
25import qualified Data.BufferBuilder.Json as Json
26+
27+#ifdef MIN_VERSION_json_builder
25import qualified Data.Json.Builder as JB
28import qualified Data.Json.Builder as JB
29+#endif
26
30
27data EyeColor = Green | Blue | Brown
31data EyeColor = Green | Blue | Brown
28 deriving (Eq, Show)
32 deriving (Eq, Show)
@@ -65,10 +69,10 @@ instance NFData Gender where rnf !_ = ()
65instance NFData Fruit where rnf !_ = ()
69instance NFData Fruit where rnf !_ = ()
66
70
67instance NFData Friend where
71instance NFData Friend where
68− rnf Friend {..} = (rnf fId) `seq` (rnf fName) `seq` ()
72+ rnf Friend {..} = rnf fId `seq` rnf fName
69
73
70instance NFData User where
74instance NFData User where
71− 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` ()
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
72
76
73eyeColorTable :: [(Text, EyeColor)]
77eyeColorTable :: [(Text, EyeColor)]
74eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
78eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
@@ -79,7 +83,7 @@ genderTable = [("male", Male), ("female", Female)]
79fruitTable :: [(Text, Fruit)]
83fruitTable :: [(Text, Fruit)]
80fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
84fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
81
85
82−enumFromJson :: Monad m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
86+enumFromJson :: Fail.MonadFail m => String -> [(Text, enum)] -> (json -> m Text) -> json -> m enum
83enumFromJson enumName table extract v = do
87enumFromJson enumName table extract v = do
84 s <- extract v
88 s <- extract v
85 case lookup s table of
89 case lookup s table of
@@ -269,6 +273,7 @@ instance Json.ToJson User where
269 <> "greeting"# Json..=# uGreeting
273 <> "greeting"# Json..=# uGreeting
270 <> "favoriteFruit"# Json..=# uFavouriteFruit
274 <> "favoriteFruit"# Json..=# uFavouriteFruit
271
275
276+#ifdef MIN_VERSION_json_builder
272---- json-builder instances ----
277---- json-builder instances ----
273
278
274instance JB.Value EyeColor where
279instance JB.Value EyeColor where
@@ -320,6 +325,7 @@ instance JB.Value User where
320 <> t "friends" `JB.row` uFriends
325 <> t "friends" `JB.row` uFriends
321 <> t "greeting" `JB.row` uGreeting
326 <> t "greeting" `JB.row` uGreeting
322 <> t "favoriteFruit" `JB.row` uFavouriteFruit
327 <> t "favoriteFruit" `JB.row` uFavouriteFruit
328+#endif
323
329
324benchmarks :: Benchmark
330benchmarks :: Benchmark
325benchmarks = env (load "json-data/buffer-builder.json") $
331benchmarks = env (load "json-data/buffer-builder.json") $
@@ -327,5 +333,7 @@ benchmarks = env (load "json-data/buffer-builder.json") $
327 bgroup "json-bench" [
333 bgroup "json-bench" [
328 bench "aeson" $ nf Aeson.encode parsedUserList
334 bench "aeson" $ nf Aeson.encode parsedUserList
329 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
335 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
336+#ifdef MIN_VERSION_json_builder
330 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
337 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
338+#endif
331 ]
339 ]
benchmarks/Compare/JsonBuilder.hs+2−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
2
3
3{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
4{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
@@ -5,12 +6,10 @@
5
6
6module Compare.JsonBuilder () where
7module Compare.JsonBuilder () where
7
8
8−import Prelude ()
9+import Prelude.Compat hiding ((<>))
9−import Prelude.Compat
10
10
11import Data.Json.Builder
11import Data.Json.Builder
12import Data.Monoid ((<>))
12import Data.Monoid ((<>))
13−import Prelude hiding (id)
14import Twitter
13import Twitter
15
14
16instance JsObject Metadata where
15instance JsObject Metadata where
benchmarks/CompareWithJSON.hs+33−21
@@ -1,16 +1,20 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1{-# OPTIONS_GHC -fno-warn-orphans #-}
3{-# OPTIONS_GHC -fno-warn-orphans #-}
2
4
3module Main (main) where
5module Main (main) where
4
6
5−import Prelude ()
6import Prelude.Compat
7import Prelude.Compat
7
8
8import Blaze.ByteString.Builder (toLazyByteString)
9import Blaze.ByteString.Builder (toLazyByteString)
9import Blaze.ByteString.Builder.Char.Utf8 (fromString)
10import Blaze.ByteString.Builder.Char.Utf8 (fromString)
10import Control.DeepSeq (NFData(rnf))
11import Control.DeepSeq (NFData(rnf))
11import Criterion.Main
12import Criterion.Main
13+import Data.Maybe (fromMaybe)
12import qualified Data.Aeson as A
14import qualified Data.Aeson as A
13import qualified Data.Aeson.Text as A
15import qualified Data.Aeson.Text as A
16+import qualified Data.Aeson.Parser.Internal as I
17+import qualified Data.ByteString as BS
14import qualified Data.ByteString.Lazy as BL
18import qualified Data.ByteString.Lazy as BL
15import qualified Data.Text.Lazy as TL
19import qualified Data.Text.Lazy as TL
16import qualified Data.Text.Lazy.Builder as TLB
20import qualified Data.Text.Lazy.Builder as TLB
@@ -23,7 +27,7 @@ instance (NFData v) => NFData (J.JSObject v) where
23instance NFData J.JSValue where
27instance NFData J.JSValue where
24 rnf J.JSNull = ()
28 rnf J.JSNull = ()
25 rnf (J.JSBool b) = rnf b
29 rnf (J.JSBool b) = rnf b
26− rnf (J.JSRational a b) = rnf a `seq` rnf b `seq` ()
30+ rnf (J.JSRational a b) = rnf a `seq` rnf b
27 rnf (J.JSString s) = rnf (J.fromJSString s)
31 rnf (J.JSString s) = rnf (J.fromJSString s)
28 rnf (J.JSArray lst) = rnf lst
32 rnf (J.JSArray lst) = rnf lst
29 rnf (J.JSObject o) = rnf o
33 rnf (J.JSObject o) = rnf o
@@ -34,15 +38,18 @@ decodeJ s =
34 J.Ok v -> v
38 J.Ok v -> v
35 J.Error _ -> error "fail to parse via JSON"
39 J.Error _ -> error "fail to parse via JSON"
36
40
37−decodeA :: BL.ByteString -> A.Value
41+decode :: BL.ByteString -> A.Value
38−decodeA s = case A.decode s of
42+decode s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
39− Just v -> v
43+
44+decode' :: BL.ByteString -> A.Value
45+decode' s = fromMaybe (error "fail to parse via Aeson") $ A.decode' s
46+
47+decodeS :: BS.ByteString -> A.Value
40− Nothing -> error "fail to parse via Aeson"
48+decodeS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
41
49
42−decodeA' :: BL.ByteString -> A.Value
50+decodeIP :: BL.ByteString -> A.Value
43−decodeA' s = case A.decode' s of
44− Just v -> v
45− Nothing -> error "fail to parse via Aeson"
51+decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
52+ I.decodeWith I.jsonEOF A.fromJSON s
46
53
47encodeJ :: J.JSValue -> BL.ByteString
54encodeJ :: J.JSValue -> BL.ByteString
48encodeJ = toLazyByteString . fromString . J.encode
55encodeJ = toLazyByteString . fromString . J.encode
@@ -58,32 +65,37 @@ main = do
58 let enFile = "json-data/twitter100.json"
65 let enFile = "json-data/twitter100.json"
59 jpFile = "json-data/jp100.json"
66 jpFile = "json-data/jp100.json"
60 enA <- BL.readFile enFile
67 enA <- BL.readFile enFile
68+ enS <- BS.readFile enFile
61 enJ <- readFile enFile
69 enJ <- readFile enFile
62 jpA <- BL.readFile jpFile
70 jpA <- BL.readFile jpFile
71+ jpS <- BS.readFile jpFile
63 jpJ <- readFile jpFile
72 jpJ <- readFile jpFile
64 defaultMain [
73 defaultMain [
65 bgroup "decode" [
74 bgroup "decode" [
66 bgroup "en" [
75 bgroup "en" [
67− bench "aeson/lazy" $ nf decodeA enA
76+ bench "aeson/lazy" $ nf decode enA
68− , bench "aeson/strict" $ nf decodeA' enA
77+ , bench "aeson/strict" $ nf decode' enA
78+ , bench "aeson/stricter" $ nf decodeS enS
79+ , bench "aeson/parser" $ nf decodeIP enA
69− , bench "json" $ nf decodeJ enJ
80+ , bench "json" $ nf decodeJ enJ
70 ]
81 ]
71 , bgroup "jp" [
82 , bgroup "jp" [
72− bench "aeson" $ nf decodeA jpA
83+ bench "aeson" $ nf decode jpA
84+ , bench "aeson/stricter" $ nf decodeS jpS
73− , bench "json" $ nf decodeJ jpJ
85+ , bench "json" $ nf decodeJ jpJ
74 ]
86 ]
75 ]
87 ]
76 , bgroup "encode" [
88 , bgroup "encode" [
77 bgroup "en" [
89 bgroup "en" [
78− bench "aeson-to-bytestring" $ nf A.encode (decodeA enA)
90+ bench "aeson-to-bytestring" $ nf A.encode (decode enA)
79− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA enA)
91+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode enA)
80− , bench "aeson-to-text" $ nf encodeToText (decodeA enA)
92+ , bench "aeson-to-text" $ nf encodeToText (decode enA)
81 , bench "json" $ nf encodeJ (decodeJ enJ)
93 , bench "json" $ nf encodeJ (decodeJ enJ)
82 ]
94 ]
83 , bgroup "jp" [
95 , bgroup "jp" [
84− bench "aeson-to-bytestring" $ nf A.encode (decodeA jpA)
96+ bench "aeson-to-bytestring" $ nf A.encode (decode jpA)
85− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA jpA)
97+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode jpA)
86− , bench "aeson-to-text" $ nf encodeToText (decodeA jpA)
98+ , bench "aeson-to-text" $ nf encodeToText (decode jpA)
87 , bench "json" $ nf encodeJ (decodeJ jpJ)
99 , bench "json" $ nf encodeJ (decodeJ jpJ)
88 ]
100 ]
89 ]
101 ]
benchmarks/Dates.hs+10−9
@@ -1,6 +1,7 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Main (main) where
3module Main (main) where
2
4
3−import Prelude ()
4import Prelude.Compat
5import Prelude.Compat
5
6
6import Criterion.Main
7import Criterion.Main
@@ -22,22 +23,22 @@ main = do
22 defaultMain [
23 defaultMain [
23 bgroup "decode" [
24 bgroup "decode" [
24 bgroup "UTCTime" [
25 bgroup "UTCTime" [
25− env file1 $ \ ~bs -> bench "whole" $ nf utcTime bs
26+ env file1 $ \bs -> bench "whole" $ nf utcTime bs
26− , env file2 $ \ ~bs -> bench "fractional" $ nf utcTime bs
27+ , env file2 $ \bs -> bench "fractional" $ nf utcTime bs
27 ]
28 ]
28 , bgroup "ZonedTime" [
29 , bgroup "ZonedTime" [
29− env file1 $ \ ~bs -> bench "whole" $ nf zTime bs
30+ env file1 $ \bs -> bench "whole" $ nf zTime bs
30− , env file2 $ \ ~bs -> bench "fractional" $ nf zTime bs
31+ , env file2 $ \bs -> bench "fractional" $ nf zTime bs
31 ]
32 ]
32 ]
33 ]
33 , bgroup "encode" [
34 , bgroup "encode" [
34 bgroup "UTCTime" [
35 bgroup "UTCTime" [
35− env (utcTime <$> file1) $ \ ~ts -> bench "whole" $ nf encode ts
36+ env (utcTime <$> file1) $ \ts -> bench "whole" $ nf encode ts
36− , env (utcTime <$> file2) $ \ ~ts -> bench "fractional" $ nf encode ts
37+ , env (utcTime <$> file2) $ \ts -> bench "fractional" $ nf encode ts
37 ]
38 ]
38 , bgroup "ZonedTime" [
39 , bgroup "ZonedTime" [
39− env (zTime <$> file1) $ \ ~ts -> bench "whole" $ nf encode ts
40+ env (zTime <$> file1) $ \ts -> bench "whole" $ nf encode ts
40− , env (zTime <$> file2) $ \ ~ts -> bench "fractional" $ nf encode ts
41+ , env (zTime <$> file2) $ \ts -> bench "fractional" $ nf encode ts
41 ]
42 ]
42 ]
43 ]
43 ]
44 ]
benchmarks/Escape.hsnew file+32
@@ -0,0 +1,32 @@
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.hsnew file+165
@@ -0,0 +1,165 @@
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+3−2
@@ -1,10 +1,11 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4+
3{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
4
6
5module Main (main) where
7module Main (main) where
6
8
7−import Prelude ()
8import Prelude.Compat
9import Prelude.Compat
9
10
10import Control.DeepSeq
11import Control.DeepSeq
@@ -16,7 +17,7 @@ import Text.JSON
16instance NFData JSValue where
17instance NFData JSValue where
17 rnf JSNull = ()
18 rnf JSNull = ()
18 rnf (JSBool b) = rnf b
19 rnf (JSBool b) = rnf b
19− rnf (JSRational b r) = rnf b `seq` rnf r `seq` ()
20+ rnf (JSRational b r) = rnf b `seq` rnf r
20 rnf (JSString s) = rnf (fromJSString s)
21 rnf (JSString s) = rnf (fromJSString s)
21 rnf (JSArray vs) = rnf vs
22 rnf (JSArray vs) = rnf vs
22 rnf (JSObject kvs) = rnf (fromJSObject kvs)
23 rnf (JSObject kvs) = rnf (fromJSObject kvs)
benchmarks/Micro.hs+8−6
@@ -1,11 +1,13 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Main (main) where
3module Main (main) where
2
4
3−import Prelude ()
4import Prelude.Compat
5import Prelude.Compat
5
6
6import Criterion.Main
7import Criterion.Main
7−import qualified Data.Aeson.Encoding.Builder as AB
8+
8−import qualified Data.ByteString.Builder as B
9+-- Encoding is a newtype wrapper around Builder
10+import Data.Aeson.Encoding (text, string, encodingToLazyByteString)
9import qualified Data.Text as T
11import qualified Data.Text as T
10
12
11main :: IO ()
13main :: IO ()
@@ -13,8 +15,8 @@ main = do
13 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
15 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
14 defaultMain [
16 defaultMain [
15 bgroup "string" [
17 bgroup "string" [
16− bench "text" $ nf (B.toLazyByteString . AB.text) (T.pack txt)
18+ bench "text" $ nf (encodingToLazyByteString . text) (T.pack txt)
17− , bench "string direct" $ nf (B.toLazyByteString . AB.string) txt
19+ , bench "string direct" $ nf (encodingToLazyByteString . string) txt
18− , bench "string via text" $ nf (B.toLazyByteString . AB.text . T.pack) txt
20+ , bench "string via text" $ nf (encodingToLazyByteString . text . T.pack) txt
19 ]
21 ]
20 ]
22 ]
benchmarks/Options.hs+1−4
@@ -1,7 +1,4 @@
1−module Options () where
1+module Options (opts) where
2−
3−import Prelude ()
4−import Prelude.Compat
5
2
6import Data.Aeson.Types
3import Data.Aeson.Types
7
4
benchmarks/ReadFile.hs+2−2
@@ -1,9 +1,9 @@
1{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
3
4
4module Main (main) where
5module Main (main) where
5
6
6−import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.DeepSeq
9import Control.DeepSeq
@@ -20,7 +20,7 @@ import qualified Data.ByteString as B
20main = do
20main = do
21 (cnt:args) <- getArgs
21 (cnt:args) <- getArgs
22 let count = read cnt :: Int
22 let count = read cnt :: Int
23− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
23+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
24 putStrLn $ arg ++ ":"
24 putStrLn $ arg ++ ":"
25 start <- getCurrentTime
25 start <- getCurrentTime
26 let loop !n
26 let loop !n
benchmarks/Typed.hs+5−1
@@ -1,6 +1,7 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Main (main) where
3module Main (main) where
2
4
3−import Prelude ()
4import Prelude.Compat
5import Prelude.Compat
5
6
6import Criterion.Main
7import Criterion.Main
@@ -13,4 +14,7 @@ main = defaultMain [
13 Generic.benchmarks
14 Generic.benchmarks
14 , Manual.benchmarks
15 , Manual.benchmarks
15 , TH.benchmarks
16 , TH.benchmarks
17+ , Generic.decodeBenchmarks
18+ , Manual.decodeBenchmarks
19+ , TH.decodeBenchmarks
16 ]
20 ]
benchmarks/Typed/Common.hs+1−8
@@ -1,23 +1,16 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2−{-# LANGUAGE PackageImports #-}
3
2
4{-# OPTIONS_GHC -fno-warn-unused-imports #-}
3{-# OPTIONS_GHC -fno-warn-unused-imports #-}
5
4
6module Typed.Common (load) where
5module Typed.Common (load) where
7
6
8−import Prelude ()
9import Prelude.Compat
7import Prelude.Compat
10
8
11import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
12import System.Exit
10import System.Exit
13import System.IO
11import System.IO
14
12
15−#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
16import Data.Aeson hiding (Result)
13import Data.Aeson hiding (Result)
17−#else
18−import "aeson" Data.Aeson hiding (Result)
19−import qualified "aeson-benchmarks" Data.Aeson as B
20−#endif
21
14
22load :: FromJSON a => FilePath -> IO a
15load :: FromJSON a => FilePath -> IO a
23load fileName = do
16load fileName = do
benchmarks/Typed/Generic.hs+27−25
@@ -1,42 +1,44 @@
1−{-# LANGUAGE PackageImports #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
2−module Typed.Generic (benchmarks) where
3+module Typed.Generic (benchmarks, decodeBenchmarks) where
3
4
4−import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import "aeson" Data.Aeson hiding (Result)
7+import Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10−import Twitter.TH
10+import Twitter.Generic
11import Typed.Common
11import Typed.Common
12−import qualified "aeson-benchmarks" Data.Aeson as B
13−
14−encodeDirectA :: Result -> L.ByteString
15−encodeDirectA = encode
16
12
17−encodeViaValueA :: Result -> L.ByteString
13+encodeDirect :: Result -> L.ByteString
18−encodeViaValueA = encode . toJSON
14+encodeDirect = encode
19
15
20−encodeDirectB :: Result -> L.ByteString
21−encodeDirectB = B.encode
22−
23−encodeViaValueB :: Result -> L.ByteString
16+encodeViaValue :: Result -> L.ByteString
24−encodeViaValueB = B.encode . B.toJSON
17+encodeViaValue = encode . toJSON
25
18
26benchmarks :: Benchmark
19benchmarks :: Benchmark
27benchmarks =
20benchmarks =
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
29− bgroup "generic" [
22+ bgroup "encodeGeneric" [
30 bgroup "direct" [
23 bgroup "direct" [
31− bench "twitter100" $ nf encodeDirectB twitter100
24+ bench "twitter100" $ nf encodeDirect twitter100
32− , bench "jp100" $ nf encodeDirectB jp100
33− , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34− , bench "jp100 baseline" $ nf encodeDirectA jp100
25+ , bench "jp100" $ nf encodeDirect jp100
35 ]
26 ]
36 , bgroup "viaValue" [
27 , bgroup "viaValue" [
37− bench "twitter100" $ nf encodeViaValueB twitter100
28+ bench "twitter100" $ nf encodeViaValue twitter100
38− , bench "jp100" $ nf encodeViaValueB jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
39− , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
30+ ]
31+ ]
32+
33+decodeDirect :: L.ByteString -> Maybe Result
34+decodeDirect = decode
35+
36+decodeBenchmarks :: Benchmark
37+decodeBenchmarks =
38+ env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39+ bgroup "decodeGeneric"
40+ [ bgroup "direct"
41+ [ bench "twitter100" $ nf decodeDirect twitter100
40− , bench "jp100 baseline" $ nf encodeViaValueA jp100
42+ , bench "jp100" $ nf decodeDirect jp100
41 ]
43 ]
42 ]
44 ]
benchmarks/Typed/Manual.hs+26−24
@@ -1,42 +1,44 @@
1−{-# LANGUAGE PackageImports #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
2−module Typed.Manual (benchmarks) where
3+module Typed.Manual (benchmarks, decodeBenchmarks) where
3
4
4−import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import "aeson" Data.Aeson hiding (Result)
7+import Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10import Twitter.Manual
10import Twitter.Manual
11import Typed.Common
11import Typed.Common
12−import qualified "aeson-benchmarks" Data.Aeson as B
13−
14−encodeDirectA :: Result -> L.ByteString
15−encodeDirectA = encode
16
12
17−encodeViaValueA :: Result -> L.ByteString
13+encodeDirect :: Result -> L.ByteString
18−encodeViaValueA = encode . toJSON
14+encodeDirect = encode
19
15
20−encodeDirectB :: Result -> L.ByteString
21−encodeDirectB = B.encode
22−
23−encodeViaValueB :: Result -> L.ByteString
16+encodeViaValue :: Result -> L.ByteString
24−encodeViaValueB = B.encode . B.toJSON
17+encodeViaValue = encode . toJSON
25
18
26benchmarks :: Benchmark
19benchmarks :: Benchmark
27benchmarks =
20benchmarks =
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
29− bgroup "manual" [
22+ bgroup "encodeManual" [
30 bgroup "direct" [
23 bgroup "direct" [
31− bench "twitter100" $ nf encodeDirectB twitter100
24+ bench "twitter100" $ nf encodeDirect twitter100
32− , bench "jp100" $ nf encodeDirectB jp100
33− , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34− , bench "jp100 baseline" $ nf encodeDirectA jp100
25+ , bench "jp100" $ nf encodeDirect jp100
35 ]
26 ]
36 , bgroup "viaValue" [
27 , bgroup "viaValue" [
37− bench "twitter100" $ nf encodeViaValueB twitter100
28+ bench "twitter100" $ nf encodeViaValue twitter100
38− , bench "jp100" $ nf encodeViaValueB jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
39− , bench "twitter100 baseline" $ nf encodeViaValueA twitter100
30+ ]
31+ ]
32+
33+decodeDirect :: L.ByteString -> Maybe Result
34+decodeDirect = decode
35+
36+decodeBenchmarks :: Benchmark
37+decodeBenchmarks =
38+ env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39+ bgroup "decodeManual"
40+ [ bgroup "direct"
41+ [ bench "twitter100" $ nf decodeDirect twitter100
40− , bench "jp100 baseline" $ nf encodeViaValueA jp100
42+ , bench "jp100" $ nf decodeDirect jp100
41 ]
43 ]
42 ]
44 ]
benchmarks/Typed/TH.hs+26−24
@@ -1,42 +1,44 @@
1−{-# LANGUAGE PackageImports #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
2−module Typed.TH (benchmarks) where
3+module Typed.TH (benchmarks, decodeBenchmarks) where
3
4
4−import Prelude ()
5import Prelude.Compat
5import Prelude.Compat
6
6
7−import "aeson" Data.Aeson hiding (Result)
7+import Data.Aeson hiding (Result)
8import Criterion
8import Criterion
9import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
10import Twitter.TH
10import Twitter.TH
11import Typed.Common
11import Typed.Common
12−import qualified "aeson-benchmarks" Data.Aeson as B
13−
14−encodeDirectA :: Result -> L.ByteString
15−encodeDirectA = encode
16
12
17−encodeViaValueA :: Result -> L.ByteString
13+encodeDirect :: Result -> L.ByteString
18−encodeViaValueA = encode . toJSON
14+encodeDirect = encode
19
15
20−encodeDirectB :: Result -> L.ByteString
21−encodeDirectB = B.encode
22−
23−encodeViaValueB :: Result -> L.ByteString
16+encodeViaValue :: Result -> L.ByteString
24−encodeViaValueB = B.encode . B.toJSON
17+encodeViaValue = encode . toJSON
25
18
26benchmarks :: Benchmark
19benchmarks :: Benchmark
27benchmarks =
20benchmarks =
28 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
21 env ((,) <$> load "json-data/twitter100.json" <*> load "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
29− bgroup "th" [
22+ bgroup "encodeTH" [
30 bgroup "direct" [
23 bgroup "direct" [
31− bench "twitter100" $ nf encodeDirectB twitter100
24+ bench "twitter100" $ nf encodeDirect twitter100
32− , bench "jp100" $ nf encodeDirectB jp100
33− , bench "twitter100 baseline" $ nf encodeDirectA twitter100
34− , bench "jp100 baseline" $ nf encodeDirectA jp100
25+ , bench "jp100" $ nf encodeDirect jp100
35 ]
26 ]
36 , bgroup "viaValue" [
27 , bgroup "viaValue" [
37− bench "twitter100" $ nf encodeViaValueA twitter100
28+ bench "twitter100" $ nf encodeViaValue twitter100
38− , bench "jp100" $ nf encodeViaValueA jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
39− , bench "twitter100 baseline" $ nf encodeViaValueB twitter100
30+ ]
31+ ]
32+
33+decodeDirect :: L.ByteString -> Maybe Result
34+decodeDirect = decode
35+
36+decodeBenchmarks :: Benchmark
37+decodeBenchmarks =
38+ env ((,) <$> L.readFile "json-data/twitter100.json" <*> L.readFile "json-data/jp100.json") $ \ ~(twitter100, jp100) ->
39+ bgroup "decodeTH"
40+ [ bgroup "direct"
41+ [ bench "twitter100" $ nf decodeDirect twitter100
40− , bench "jp100 baseline" $ nf encodeViaValueB jp100
42+ , bench "jp100" $ nf decodeDirect jp100
41 ]
43 ]
42 ]
44 ]
benchmarks/aeson-benchmarks.cabal+183−62
@@ -1,60 +1,21 @@
1name: aeson-benchmarks
1name: aeson-benchmarks
2version: 0
2version: 0
3build-type: Simple
3build-type: Simple
4−
5−cabal-version: >=1.8
4+cabal-version: >=1.10
6
5
7flag bytestring-builder
6flag bytestring-builder
8 description: Depend on the bytestring-builder package for backwards compatibility.
7 description: Depend on the bytestring-builder package for backwards compatibility.
9 default: False
8 default: False
10 manual: False
9 manual: False
11
10
12−library
11+flag local-aeson
13− hs-source-dirs: .. .
12+ description: Build the local version of aeson, to avoid rebuilding aeson's
14−
13+ reverse dependencies for benchmarking (statistics, criterion).
15− exposed-modules:
14+ default: True
16− Data.Aeson
15+ manual: True
17− Data.Aeson.Encoding
18− Data.Aeson.Parser
19− Data.Aeson.Text
20− Data.Aeson.Types
21− Data.Aeson.TH
22−
23− Data.Aeson.Encoding.Internal
24− Data.Aeson.Internal
25− Data.Aeson.Internal.Time
26−
27− Data.Aeson.Encoding.Builder
28− Data.Aeson.Internal.Functions
29− Data.Aeson.Parser.Internal
30− Data.Aeson.Parser.Time
31− Data.Aeson.Types.FromJSON
32− Data.Aeson.Types.Generic
33− Data.Aeson.Types.ToJSON
34− Data.Aeson.Types.Class
35− Data.Aeson.Types.Internal
36
16
37− build-depends:
17+library
38− attoparsec >= 0.13.0.1,
18+ default-language: Haskell2010
39− base == 4.*,
40− base-compat >= 0.9.1 && <0.10,
41− time-locale-compat >=0.1.1 && <0.2,
42− containers,
43− deepseq,
44− dlist >= 0.2,
45− fail == 4.9.*,
46− ghc-prim >= 0.2,
47− hashable >= 1.1.2.0,
48− mtl,
49− scientific >= 0.3.4.7 && < 0.4,
50− syb,
51− tagged >=0.8.3 && <0.9,
52− template-haskell >= 2.4,
53− text >= 1.1.1.0,
54− time,
55− transformers,
56− unordered-containers >= 0.2.3.0,
57− vector >= 0.7.1
58
19
59 if flag(bytestring-builder)
20 if flag(bytestring-builder)
60 build-depends: bytestring >= 0.9 && < 0.10.4,
21 build-depends: bytestring >= 0.9 && < 0.10.4,
@@ -62,22 +23,142 @@ library
62 else
23 else
63 build-depends: bytestring >= 0.10.4
24 build-depends: bytestring >= 0.10.4
64
25
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+
65− if impl(ghc >=7.8)
55+ if !impl(ghc >= 8.6)
66− cpp-options: -DHAS_COERCIBLE
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
67
62
68− if !impl(ghc >= 8.0)
63+ if !impl(ghc >= 7.10)
69− -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
64+ -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
70− build-depends: semigroups >= 0.18.2 && < 0.19
65+ build-depends: nats,
66+ void
71
67
72− include-dirs: ../include
68+ include-dirs: ../include
73
69
74− ghc-options: -O2 -Wall
70+ ghc-options: -O2 -Wall
75− cpp-options: -DGENERICS
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
131+
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
76
150
77executable aeson-benchmark-compare
151executable aeson-benchmark-compare
152+ default-language: Haskell2010
78 main-is: Compare.hs
153 main-is: Compare.hs
79 hs-source-dirs: ../examples .
154 hs-source-dirs: ../examples .
80 ghc-options: -Wall -O2 -rtsopts
155 ghc-options: -Wall -O2 -rtsopts
156+ other-modules:
157+ Compare.BufferBuilder
158+ Compare.JsonBench
159+ Twitter
160+ Twitter.Manual
161+ Typed.Common
81 build-depends:
162 build-depends:
82 aeson-benchmarks,
163 aeson-benchmarks,
83 base,
164 base,
@@ -87,10 +168,19 @@ executable aeson-benchmark-compare
87 criterion >= 1.0,
168 criterion >= 1.0,
88 deepseq,
169 deepseq,
89 ghc-prim,
170 ghc-prim,
90− json-builder,
91 text
171 text
92
172
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+
93executable aeson-benchmark-micro
182executable aeson-benchmark-micro
183+ default-language: Haskell2010
94 main-is: Micro.hs
184 main-is: Micro.hs
95 hs-source-dirs: ../examples .
185 hs-source-dirs: ../examples .
96 ghc-options: -Wall -O2 -rtsopts
186 ghc-options: -Wall -O2 -rtsopts
@@ -105,14 +195,21 @@ executable aeson-benchmark-micro
105 text
195 text
106
196
107executable aeson-benchmark-typed
197executable aeson-benchmark-typed
198+ default-language: Haskell2010
108 main-is: Typed.hs
199 main-is: Typed.hs
109 hs-source-dirs: ../examples .
200 hs-source-dirs: ../examples .
110 ghc-options: -Wall -O2 -rtsopts
201 ghc-options: -Wall -O2 -rtsopts
111− -- We must help ourself in situations when there is both
202+ other-modules:
112− -- aeson and aeson-benchmakrs
203+ Twitter
113− cpp-options: -DHAS_BOTH_AESON_AND_BENCHMARKS
204+ Twitter.Generic
205+ Twitter.Manual
206+ Twitter.Options
207+ Twitter.TH
208+ Typed.Common
209+ Typed.Generic
210+ Typed.Manual
211+ Typed.TH
114 build-depends:
212 build-depends:
115− aeson,
116 aeson-benchmarks,
213 aeson-benchmarks,
117 base,
214 base,
118 base-compat,
215 base-compat,
@@ -127,8 +224,11 @@ executable aeson-benchmark-typed
127 bytestring-builder >= 0.10.4 && < 1
224 bytestring-builder >= 0.10.4 && < 1
128 else
225 else
129 build-depends: bytestring >= 0.10.4
226 build-depends: bytestring >= 0.10.4
227+ if impl(ghc < 8.0)
228+ build-depends: semigroups
130
229
131executable aeson-benchmark-compare-with-json
230executable aeson-benchmark-compare-with-json
231+ default-language: Haskell2010
132 main-is: CompareWithJSON.hs
232 main-is: CompareWithJSON.hs
133 ghc-options: -Wall -O2 -rtsopts
233 ghc-options: -Wall -O2 -rtsopts
134 build-depends:
234 build-depends:
@@ -143,6 +243,7 @@ executable aeson-benchmark-compare-with-json
143 text
243 text
144
244
145executable aeson-benchmark-aeson-encode
245executable aeson-benchmark-aeson-encode
246+ default-language: Haskell2010
146 main-is: AesonEncode.hs
247 main-is: AesonEncode.hs
147 ghc-options: -Wall -O2 -rtsopts
248 ghc-options: -Wall -O2 -rtsopts
148 build-depends:
249 build-depends:
@@ -155,6 +256,7 @@ executable aeson-benchmark-aeson-encode
155 time
256 time
156
257
157executable aeson-benchmark-aeson-parse
258executable aeson-benchmark-aeson-parse
259+ default-language: Haskell2010
158 main-is: AesonParse.hs
260 main-is: AesonParse.hs
159 ghc-options: -Wall -O2 -rtsopts
261 ghc-options: -Wall -O2 -rtsopts
160 build-depends:
262 build-depends:
@@ -166,6 +268,7 @@ executable aeson-benchmark-aeson-parse
166 time
268 time
167
269
168executable aeson-benchmark-json-parse
270executable aeson-benchmark-json-parse
271+ default-language: Haskell2010
169 main-is: JsonParse.hs
272 main-is: JsonParse.hs
170 ghc-options: -Wall -O2 -rtsopts
273 ghc-options: -Wall -O2 -rtsopts
171 build-depends:
274 build-depends:
@@ -176,6 +279,7 @@ executable aeson-benchmark-json-parse
176 time
279 time
177
280
178executable aeson-benchmark-dates
281executable aeson-benchmark-dates
282+ default-language: Haskell2010
179 main-is: Dates.hs
283 main-is: Dates.hs
180 ghc-options: -Wall -O2 -rtsopts
284 ghc-options: -Wall -O2 -rtsopts
181 build-depends:
285 build-depends:
@@ -187,12 +291,14 @@ executable aeson-benchmark-dates
187 aeson-benchmarks,
291 aeson-benchmarks,
188 text,
292 text,
189 time
293 time
294+ if impl(ghc >= 8.2)
295+ ghc-options: -Wno-simplifiable-class-constraints
190
296
191executable aeson-benchmark-map
297executable aeson-benchmark-map
298+ default-language: Haskell2010
192 main-is: AesonMap.hs
299 main-is: AesonMap.hs
193 ghc-options: -Wall -O2 -rtsopts
300 ghc-options: -Wall -O2 -rtsopts
194 build-depends:
301 build-depends:
195− aeson,
196 aeson-benchmarks,
302 aeson-benchmarks,
197 base,
303 base,
198 base-compat,
304 base-compat,
@@ -206,10 +312,10 @@ executable aeson-benchmark-map
206 unordered-containers
312 unordered-containers
207
313
208executable aeson-benchmark-foldable
314executable aeson-benchmark-foldable
315+ default-language: Haskell2010
209 main-is: AesonFoldable.hs
316 main-is: AesonFoldable.hs
210 ghc-options: -Wall -O2 -rtsopts
317 ghc-options: -Wall -O2 -rtsopts
211 build-depends:
318 build-depends:
212− aeson,
213 aeson-benchmarks,
319 aeson-benchmarks,
214 base,
320 base,
215 base-compat,
321 base-compat,
@@ -222,3 +328,18 @@ executable aeson-benchmark-foldable
222 text,
328 text,
223 unordered-containers,
329 unordered-containers,
224 vector
330 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.projectnew file+5
@@ -0,0 +1,5 @@
1+with-compiler: ghc
2+packages: .
3+packages: attoparsec-iso8601
4+-- packages: benchmarks
5+tests: true
cbits/unescape_string.cnew file+149
@@ -0,0 +1,149 @@
1+// Copyright (c) 2008-2009 Bjoern Hoehrmann
2+// Copyright (c) 2015, Ondrej Palkovsky
3+// Copyright (c) 2016, Winterland
4+
5+#include <string.h>
6+#include <stdio.h>
7+#include <stdint.h>
8+
9+
10+#define UTF8_ACCEPT 0
11+#define UTF8_REJECT 12
12+
13+static const uint8_t utf8d[] = {
14+ // The first part of the table maps bytes to character classes that
15+ // to reduce the size of the transition table and create bitmasks.
16+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
17+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
18+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
19+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
20+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
21+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
22+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
23+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
24+
25+ // The second part is a transition table that maps a combination
26+ // of a state of the automaton and a character class to a state.
27+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
28+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
29+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
30+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
31+ 12,36,12,12,12,12,12,12,12,12,12,12,
32+};
33+
34+static inline uint32_t decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
35+ uint32_t type = utf8d[byte];
36+
37+ *codep = (*state != UTF8_ACCEPT) ?
38+ (byte & 0x3fu) | (*codep << 6) :
39+ (0xff >> type) & (byte);
40+
41+ *state = utf8d[256 + *state + type];
42+ return *state;
43+}
44+
45+static inline uint16_t decode_hex(uint32_t c)
46+{
47+ if (c >= '0' && c <= '9') return c - '0';
48+ else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
49+ else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
50+ return 0xFFFF; // Should not happen
51+}
52+
53+// Decode, return non-zero value on error
54+int _js_decode_string(uint16_t *const dest, size_t *destoff,
55+ const uint8_t *s, const uint8_t *const srcend)
56+{
57+ uint16_t *d = dest + *destoff;
58+ uint32_t state = 0;
59+ uint32_t codepoint;
60+
61+ uint8_t surrogate = 0;
62+ uint16_t temp_hex = 0;
63+ uint16_t unidata;
64+
65+ // Optimized version of dispatch when just an ASCII char is expected
66+ #define DISPATCH_ASCII(label) {\
67+ if (s >= srcend) {\
68+ return -1;\
69+ }\
70+ codepoint = *s++;\
71+ goto label;\
72+ }
73+
74+ standard:
75+ // Test end of stream
76+ while (s < srcend) {
77+ if (decode(&state, &codepoint, *s++) != UTF8_ACCEPT) {
78+ if (state == UTF8_REJECT) { return -1; }
79+ continue;
80+ }
81+
82+ if (codepoint == '\\')
83+ DISPATCH_ASCII(backslash)
84+ else if (codepoint <= 0xffff)
85+ *d++ = (uint16_t) codepoint;
86+ else {
87+ *d++ = (uint16_t) (0xD7C0 + (codepoint >> 10));
88+ *d++ = (uint16_t) (0xDC00 + (codepoint & 0x3FF));
89+ }
90+ }
91+ *destoff = d - dest;
92+ // Exit point
93+ return (state != UTF8_ACCEPT);
94+ backslash:
95+ switch (codepoint) {
96+ case '"':
97+ case '\\':
98+ case '/':
99+ *d++ = (uint16_t) codepoint;
100+ goto standard;
101+ break;
102+ case 'b': *d++ = '\b';goto standard;
103+ case 'f': *d++ = '\f';goto standard;
104+ case 'n': *d++ = '\n';goto standard;
105+ case 'r': *d++ = '\r';goto standard;
106+ case 't': *d++ = '\t';goto standard;
107+ case 'u': DISPATCH_ASCII(unicode1);;break;
108+ default:
109+ return -1;
110+ }
111+ unicode1:
112+ temp_hex = decode_hex(codepoint);
113+ if (temp_hex == 0xFFFF) { return -1; }
114+ else unidata = temp_hex << 12;
115+ DISPATCH_ASCII(unicode2);
116+ unicode2:
117+ temp_hex = decode_hex(codepoint);
118+ if (temp_hex == 0xFFFF) { return -1; }
119+ else unidata |= temp_hex << 8;
120+ DISPATCH_ASCII(unicode3);
121+ unicode3:
122+ temp_hex = decode_hex(codepoint);
123+ if (temp_hex == 0xFFFF) { return -1; }
124+ else unidata |= temp_hex << 4;
125+ DISPATCH_ASCII(unicode4);
126+ unicode4:
127+ temp_hex = decode_hex(codepoint);
128+ if (temp_hex == 0xFFFF) { return -1; }
129+ else unidata |= temp_hex;
130+ *d++ = (uint16_t) unidata;
131+
132+ if (surrogate) {
133+ if (unidata < 0xDC00 || unidata > 0xDFFF) // is not low surrogate
134+ return -1;
135+ surrogate = 0;
136+ } else if (unidata >= 0xD800 && unidata <= 0xDBFF ) { // is high surrogate
137+ surrogate = 1;
138+ DISPATCH_ASCII(surrogate1);
139+ } else if (unidata >= 0xDC00 && unidata <= 0xDFFF) { // is low surrogate
140+ return -1;
141+ }
142+ goto standard;
143+ surrogate1:
144+ if (codepoint != '\\') { return -1; }
145+ DISPATCH_ASCII(surrogate2)
146+ surrogate2:
147+ if (codepoint != 'u') { return -1; }
148+ DISPATCH_ASCII(unicode1)
149+}
changelog.md+264−1
@@ -1,11 +1,267 @@
1For 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).
1For 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).
2
2
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
157+
158+* Add `parserThrowError` and `parserCatchError` combinators, thanks to Oleg Grenrus.
159+
160+* Add `Generic` instance for `Value`, thanks to Xia Li-yao.
161+
162+* Fix a mistake in the 1.2.0.0 changelog, the `cffi` flag is disabled by default! Thanks to dbaynard.
163+
164+## 1.2.0.0
165+
166+* `tagSingleConstructors`, an option to encode single-constructor types as tagged sums was added to `Options`. It is disabled by default for backward compatibility.
167+
168+* The `cffi` flag is now turned off (`False`) by default, this means C FFI code is no longer used by default. You can flip the flag to get C implementation.
169+
170+* The `Options` constructor is no longer exposed to prevent new options from being breaking changes, use `defaultOptions` instead.
171+
172+* The contents of `GToJSON` and `GToEncoding` are no longer exposed.
173+
174+* Some INLINE pragmas were removed to avoid GHC running out of simplifier ticks.
175+
176+### 1.1.2.0
177+
178+* Fix an accidental change in the format of `deriveJSON`. Thanks to Xia Li-yao!
179+
180+* Documentation improvements regarding `ToJSON`, `FromJSON`, and `SumEncoding`. Thanks to Xia Li-yao and Lennart Spitzner!
181+
182+### 1.1.1.0
183+
184+* Added a pure implementation of the C FFI code, the C FFI code. If you wish to use the pure haskell version set the `cffi` flag to `False`. This should make aeson compile when C isn't available, such as for GHCJS. Thanks to James Parker & Marcin Tolysz!
185+
186+* Using the `fast` flag can no longer cause a test case to fail. As far as we know this didn't affect any users of the library itself. Thanks to Xia Li-yao!
187+
188+## 1.1.0.0
189+
190+* Added instances for `UUID`.
191+
192+* The operators for parsing fields now have named aliases:
193+ - `.:` => `parseField`
194+ - `.:?` => `parseFieldMaybe`
195+ - `.:!` => `parseFieldMaybe'`
196+ - These functions now also have variants with explicit parser functions: `explicitParseField`, `explicitParseFieldMaybe`, "explicitParseFieldMaybe'`
197+Thanks to Oleg Grenrus.
198+
199+* `ToJSONKey (Identity a)` and `FromJSONKey (Identity a)` no longer require the unnecessary `FromJSON a` constraint. Thanks to Oleg Grenrus.
200+
201+* Added `Data.Aeson.Encoding.pair'` which is a more general version of `Data.Aeson.Encoding.pair`. Thanks to Andrew Martin.
202+
203+* `Day`s BCE are properly encoded and `+` is now a valid prefix for `Day`s CE. Thanks to Matt Parsons.
204+
205+* Some commonly used ToJSON instances are now specialized in order to improve compile time. Thanks to Bartosz Nitka.
206+
207+
208+[JSONTestSuite](https://github.com/nst/JSONTestSuite) cleanups, all
209+motivated by tighter RFC 7159 compliance:
210+
211+* The parser now rejects numbers for which
212+ [the integer portion contains a leading zero](https://github.com/bos/aeson/commit/3fb7c155f2255482b1b9566ec5c1eaf9895d630e).
213+* The parser now rejects numbers for which
214+ [a decimal point is not followed by at least one digit](https://github.com/bos/aeson/commit/ecfca35a45286dbe2bbaf5f62354be393bc59b66),
215+* The parser now rejects documents that contain [whitespace outside the
216+ set {space, newline, carriage return, tab}](https://github.com/bos/aeson/commit/8ef622c2ad8d4a109884e17c2792238a2a320e44).
217+
218+Over 90% of JSONTestSuite tests currently pass. The remainder can be
219+categorised as follows:
220+
221+* The string parser is strict with Unicode compliance where the RFC
222+ leaves room for implementation-defined behaviour (tests prefixed
223+ with "`i_string_`". (This is necessary because the `text` library
224+ cannot accommodate invalid Unicode.)
225+
226+* The parser does not (and will not) support UTF-16, UTF-32, or byte
227+ order marks (BOM).
228+* The parser accepts unescaped control characters, even though the RFC
229+ states that control characters must be escaped. (This may change at
230+ some point, but doesn't seem important.)
231+
232+#### 1.0.2.1
233+
234+* Fixes a regression where a bunch of valid characters caused an
235+ "Invalid UTF8-Stream" error when decoding. Thanks to Vladimir
236+ Shabanov who investigated and fixed this.
237+
238+### 1.0.2.0
239+
240+* Fixes a regression where it was no longer possible to derive
241+ instances for types such as `data T a = T { f1 :: a, f2 :: Maybe a
242+ }`.
243+
244+Thanks to Sean Leather for fixing this, and to Ryan Scott for helping out.
245+
246+### 1.0.1.0
247+
248+* Decoding performance has been significantly improved (see
249+ https://github.com/bos/aeson/pull/452). Thanks to @winterland1989.
250+
251+* Add `ToJSON`/`FromJSON` instances for newtypes from
252+ `Data.Semigroup`: `Min`, `Max`, `First`, `Last`, `WrappedMonoid`,
253+ `Option`. Thanks to Lennart Spitzner.
254+
255+* Make the documentation for `.:!` more accurate. Thanks to Ian Jeffries.
256+
3# 1.0.0.0
257# 1.0.0.0
4
258
5Major enhancements:
259Major enhancements:
6
260
7* Introduced new `FromJSONKey` and `ToJSONKey` type classes that are
261* Introduced new `FromJSONKey` and `ToJSONKey` type classes that are
8− used to encode maps without going through HashMap
262+ used to encode maps without going through HashMap. This also allows arbitrary
263+ serialization of keys where a string-like key will encode into an object and
264+ other keys will encode into an array of key-value tuples.
9
265
10* Added higher rank classes: `ToJSON1`, `ToJSON2`, `FromJSON1`, and
266* Added higher rank classes: `ToJSON1`, `ToJSON2`, `FromJSON1`, and
11 `FromJSON2`.
267 `FromJSON2`.
@@ -15,6 +271,13 @@ Major enhancements:
15
271
16Other enhancements:
272Other enhancements:
17
273
274+* A Cabal `fast` flag was added to disable building with optimizations. This drastically speeds up compiling both aeson ***and*** libraries using aeson so it is recommended to enable it during development. With cabal-install you can `cabal install aeson -ffast` and with stack you can add a flag section to your stack.yaml:
275+```
276+flags:
277+ aeson:
278+ fast: true
279+```
280+
18* Added list specific members to `ToJSON` and `FromJSON` classes. In
281* Added list specific members to `ToJSON` and `FromJSON` classes. In
19 the same way `Read` and `Show` handle lists specifically. This
282 the same way `Read` and `Show` handle lists specifically. This
20 removes need for overlapping instances to handle `String`.
283 removes need for overlapping instances to handle `String`.
examples/Generic.hs+1−1
@@ -6,11 +6,11 @@
6-- automatically derive the Generic class for us.
6-- automatically derive the Generic class for us.
7
7
8{-# LANGUAGE DeriveGeneric #-}
8{-# LANGUAGE DeriveGeneric #-}
9+{-# LANGUAGE NoImplicitPrelude #-}
9{-# LANGUAGE OverloadedStrings #-}
10{-# LANGUAGE OverloadedStrings #-}
10
11
11module Main (main) where
12module Main (main) where
12
13
13−import Prelude ()
14import Prelude.Compat
14import Prelude.Compat
15
15
16import Data.Aeson (FromJSON, ToJSON, decode, encode)
16import Data.Aeson (FromJSON, ToJSON, decode, encode)
examples/Simplest.hs+1−1
@@ -1,9 +1,9 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE RecordWildCards #-}
3
4
4module Main (main) where
5module Main (main) where
5
6
6−import Prelude ()
7import Prelude.Compat
7import Prelude.Compat
8
8
9import Control.Applicative (empty)
9import Control.Applicative (empty)
examples/TemplateHaskell.hs+1−1
@@ -1,12 +1,12 @@
1-- We can use Template Haskell (TH) to generate instances of the
1-- We can use Template Haskell (TH) to generate instances of the
2-- FromJSON and ToJSON classes automatically. This is the fastest way
2-- FromJSON and ToJSON classes automatically. This is the fastest way
3-- to add JSON support for a type.
3-- to add JSON support for a type.
4+{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
6
7
7module Main (main) where
8module Main (main) where
8
9
9−import Prelude ()
10import Prelude.Compat
10import Prelude.Compat
11
11
12import Data.Aeson (decode, encode)
12import Data.Aeson (decode, encode)
examples/Twitter.hs+4−2
@@ -10,6 +10,7 @@
10
10
11{-# LANGUAGE DeriveDataTypeable #-}
11{-# LANGUAGE DeriveDataTypeable #-}
12{-# LANGUAGE DeriveGeneric #-}
12{-# LANGUAGE DeriveGeneric #-}
13+{-# LANGUAGE NoImplicitPrelude #-}
13
14
14module Twitter
15module Twitter
15 (
16 (
@@ -19,7 +20,6 @@ module Twitter
19 , Result(..)
20 , Result(..)
20 ) where
21 ) where
21
22
22−import Prelude ()
23import Prelude.Compat
23import Prelude.Compat
24
24
25import Control.DeepSeq
25import Control.DeepSeq
@@ -27,7 +27,9 @@ import Data.Data (Typeable, Data)
27import Data.Int (Int64)
27import Data.Int (Int64)
28import Data.Text (Text)
28import Data.Text (Text)
29import GHC.Generics (Generic)
29import GHC.Generics (Generic)
30−import Prelude hiding (id)
30+
31+{-# ANN module "Hlint: ignore Use camelCase" #-}
32+{-# ANN module "Hlint: ignore Use newtype instead of data" #-}
31
33
32data Metadata = Metadata {
34data Metadata = Metadata {
33 result_type :: Text
35 result_type :: Text
examples/Twitter/Generic.hs+31−37
@@ -1,6 +1,7 @@
1-- Use GHC generics to automatically generate good instances.
1-- Use GHC generics to automatically generate good instances.
2+
2−{-# LANGUAGE CPP #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3−{-# LANGUAGE PackageImports #-}
4+
4{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
5
6
6module Twitter.Generic
7module Twitter.Generic
@@ -11,40 +12,33 @@ module Twitter.Generic
11 , Result(..)
12 , Result(..)
12 ) where
13 ) where
13
14
14−import Prelude ()
15+import Prelude.Compat ()
15−import Prelude.Compat
16
16
17import Twitter
17import Twitter
18−
18+import Twitter.Options
19−#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
19+
20−import Data.Aeson (ToJSON, FromJSON)
20+import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
21−#else
21+
22−import "aeson" Data.Aeson (ToJSON, FromJSON)
22+instance ToJSON Metadata where
23−import qualified "aeson-benchmarks" Data.Aeson as B
23+ toJSON = genericToJSON twitterOptions
24−#endif
24+ toEncoding = genericToEncoding twitterOptions
25−
26−instance ToJSON Metadata
25+instance FromJSON Metadata where
27−instance FromJSON Metadata
26+ parseJSON = genericParseJSON twitterOptions
28−
27+
29−instance ToJSON Geo
28+instance ToJSON Geo where
30−instance FromJSON Geo
29+ toJSON = genericToJSON twitterOptions
31−
30+ toEncoding = genericToEncoding twitterOptions
32−instance ToJSON Story
33−instance FromJSON Story
31+instance FromJSON Geo where
34−
32+ parseJSON = genericParseJSON twitterOptions
33+
35−instance ToJSON Result
34+instance ToJSON Story where
36−instance FromJSON Result
35+ toJSON = genericToJSON twitterOptions
37−
36+ toEncoding = genericToEncoding twitterOptions
38−#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
39−instance B.ToJSON Metadata
40−instance B.FromJSON Metadata
37+instance FromJSON Story where
41−
38+ parseJSON = genericParseJSON twitterOptions
42−instance B.ToJSON Geo
39+
43−instance B.FromJSON Geo
44−
45−instance B.ToJSON Story
40+instance ToJSON Result where
46−instance B.FromJSON Story
41+ toJSON = genericToJSON twitterOptions
47−
42+ toEncoding = genericToEncoding twitterOptions
48−instance B.ToJSON Result
49−instance B.FromJSON Result
43+instance FromJSON Result where
50−#endif
44+ parseJSON = genericParseJSON twitterOptions
examples/Twitter/Manual.hs+3−136
@@ -1,9 +1,8 @@
1-- Manually write instances.
1-- Manually write instances.
2−
3−{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
4−{-# LANGUAGE PackageImports #-}
5{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
6{-# LANGUAGE RecordWildCards #-}
4{-# LANGUAGE RecordWildCards #-}
5+
7{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
8
7
9module Twitter.Manual
8module Twitter.Manual
@@ -14,20 +13,13 @@ module Twitter.Manual
14 , Result(..)
13 , Result(..)
15 ) where
14 ) where
16
15
17−import Prelude ()
18import Prelude.Compat
16import Prelude.Compat
19
17
20import Control.Applicative
18import Control.Applicative
21−import Data.Monoid ((<>))
19+import Data.Semigroup ((<>))
22−import Prelude hiding (id)
23import Twitter
20import Twitter
24
21
25−#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
26import Data.Aeson hiding (Result)
22import Data.Aeson hiding (Result)
27−#else
28−import "aeson" Data.Aeson hiding (Result)
29−import qualified "aeson-benchmarks" Data.Aeson as B
30−#endif
31
23
32instance ToJSON Metadata where
24instance ToJSON Metadata where
33
25
@@ -152,128 +144,3 @@ instance FromJSON Result where
152 <*> v .: "max_id_str"
144 <*> v .: "max_id_str"
153 <*> v .: "query"
145 <*> v .: "query"
154 parseJSON _ = empty
146 parseJSON _ = empty
155−
156−#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
157−instance B.ToJSON Metadata where
158− toJSON Metadata{..} = B.object [
159− "result_type" B..= result_type
160− ]
161−
162− toEncoding Metadata{..} = B.pairs $
163− "result_type" B..= result_type
164−
165−instance B.FromJSON Metadata where
166− parseJSON (B.Object v) = Metadata <$> v B..: "result_type"
167− parseJSON _ = empty
168−
169−instance B.ToJSON Geo where
170− toJSON Geo{..} = B.object [
171− "type_" B..= type_
172− , "coordinates" B..= coordinates
173− ]
174−
175− toEncoding Geo{..} = B.pairs $
176− "type_" B..= type_
177− <> "coordinates" B..= coordinates
178−
179−instance B.FromJSON Geo where
180− parseJSON (B.Object v) = Geo <$>
181− v B..: "type_"
182− <*> v B..: "coordinates"
183− parseJSON _ = empty
184−
185−instance B.ToJSON Story where
186− toJSON Story{..} = B.object [
187− "from_user_id_str" B..= from_user_id_str
188− , "profile_image_url" B..= profile_image_url
189− , "created_at" B..= created_at
190− , "from_user" B..= from_user
191− , "id_str" B..= id_str
192− , "metadata" B..= metadata
193− , "to_user_id" B..= to_user_id
194− , "text" B..= text
195− , "id" B..= id_
196− , "from_user_id" B..= from_user_id
197− , "geo" B..= geo
198− , "iso_language_code" B..= iso_language_code
199− , "to_user_id_str" B..= to_user_id_str
200− , "source" B..= source
201− ]
202−
203− toEncoding Story{..} = B.pairs $
204− "from_user_id_str" B..= from_user_id_str
205− <> "profile_image_url" B..= profile_image_url
206− <> "created_at" B..= created_at
207− <> "from_user" B..= from_user
208− <> "id_str" B..= id_str
209− <> "metadata" B..= metadata
210− <> "to_user_id" B..= to_user_id
211− <> "text" B..= text
212− <> "id" B..= id_
213− <> "from_user_id" B..= from_user_id
214− <> "geo" B..= geo
215− <> "iso_language_code" B..= iso_language_code
216− <> "to_user_id_str" B..= to_user_id_str
217− <> "source" B..= source
218−
219−instance B.FromJSON Story where
220− parseJSON (B.Object v) = Story <$>
221− v B..: "from_user_id_str"
222− <*> v B..: "profile_image_url"
223− <*> v B..: "created_at"
224− <*> v B..: "from_user"
225− <*> v B..: "id_str"
226− <*> v B..: "metadata"
227− <*> v B..: "to_user_id"
228− <*> v B..: "text"
229− <*> v B..: "id"
230− <*> v B..: "from_user_id"
231− <*> v B..: "geo"
232− <*> v B..: "iso_language_code"
233− <*> v B..: "to_user_id_str"
234− <*> v B..: "source"
235− parseJSON _ = empty
236−
237−instance B.ToJSON Result where
238− toJSON Result{..} = B.object [
239− "results" B..= results
240− , "max_id" B..= max_id
241− , "since_id" B..= since_id
242− , "refresh_url" B..= refresh_url
243− , "next_page" B..= next_page
244− , "results_per_page" B..= results_per_page
245− , "page" B..= page
246− , "completed_in" B..= completed_in
247− , "since_id_str" B..= since_id_str
248− , "max_id_str" B..= max_id_str
249− , "query" B..= query
250− ]
251−
252− toEncoding Result{..} = B.pairs $
253− "results" B..= results
254− <> "max_id" B..= max_id
255− <> "since_id" B..= since_id
256− <> "refresh_url" B..= refresh_url
257− <> "next_page" B..= next_page
258− <> "results_per_page" B..= results_per_page
259− <> "page" B..= page
260− <> "completed_in" B..= completed_in
261− <> "since_id_str" B..= since_id_str
262− <> "max_id_str" B..= max_id_str
263− <> "query" B..= query
264−
265−instance B.FromJSON Result where
266− parseJSON (B.Object v) = Result <$>
267− v B..: "results"
268− <*> v B..: "max_id"
269− <*> v B..: "since_id"
270− <*> v B..: "refresh_url"
271− <*> v B..: "next_page"
272− <*> v B..: "results_per_page"
273− <*> v B..: "page"
274− <*> v B..: "completed_in"
275− <*> v B..: "since_id_str"
276− <*> v B..: "max_id_str"
277− <*> v B..: "query"
278− parseJSON _ = empty
279−#endif
examples/Twitter/Options.hsnew file+10
@@ -0,0 +1,10 @@
1+module Twitter.Options (module Twitter.Options) where
2+
3+import Data.Aeson
4+
5+twitterOptions :: Options
6+twitterOptions = defaultOptions
7+ { fieldLabelModifier = \x -> case x of
8+ "id_" -> "id"
9+ _ -> x
10+ }
examples/Twitter/TH.hs+7−19
@@ -1,8 +1,8 @@
1-- Use Template Haskell to generate good instances.
1-- Use Template Haskell to generate good instances.
2
2
3−{-# LANGUAGE CPP #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
4−{-# LANGUAGE PackageImports #-}
5{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
5+
6{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
7
7
8module Twitter.TH
8module Twitter.TH
@@ -13,25 +13,13 @@ module Twitter.TH
13 , Result(..)
13 , Result(..)
14 ) where
14 ) where
15
15
16−import Prelude ()
17
16
18import Twitter
17import Twitter
18+import Twitter.Options
19
19
20−#ifndef HAS_BOTH_AESON_AND_BENCHMARKS
21import Data.Aeson.TH
20import Data.Aeson.TH
22−#else
23−import "aeson" Data.Aeson.TH
24−import qualified "aeson-benchmarks" Data.Aeson.TH as B
25−#endif
26−
27−$(deriveJSON defaultOptions ''Metadata)
28−$(deriveJSON defaultOptions ''Geo)
29−$(deriveJSON defaultOptions ''Story)
30−$(deriveJSON defaultOptions ''Result)
31
21
32−#ifdef HAS_BOTH_AESON_AND_BENCHMARKS
33−$(B.deriveJSON B.defaultOptions ''Metadata)
22+$(deriveJSON twitterOptions ''Metadata)
34−$(B.deriveJSON B.defaultOptions ''Geo)
23+$(deriveJSON twitterOptions ''Geo)
35−$(B.deriveJSON B.defaultOptions ''Story)
24+$(deriveJSON twitterOptions ''Story)
36−$(B.deriveJSON B.defaultOptions ''Result)
25+$(deriveJSON twitterOptions ''Result)
37−#endif
ffi/Data/Aeson/Parser/UnescapeFFI.hsnew file+52
@@ -0,0 +1,52 @@
1+{-# LANGUAGE ForeignFunctionInterface #-}
2+{-# LANGUAGE MagicHash #-}
3+{-# LANGUAGE UnliftedFFITypes #-}
4+
5+module Data.Aeson.Parser.UnescapeFFI
6+ (
7+ unescapeText
8+ ) where
9+
10+import Control.Exception (evaluate, throw, try)
11+import Control.Monad.ST.Unsafe (unsafeIOToST, unsafeSTToIO)
12+import Data.ByteString as B
13+import Data.ByteString.Internal as B
14+import Data.Text.Encoding.Error (UnicodeException (..))
15+import Data.Text.Internal (Text (..))
16+import Data.Text.Internal.Private (runText)
17+import Data.Text.Unsafe (unsafeDupablePerformIO)
18+import Data.Word (Word8)
19+import Foreign.C.Types (CInt (..), CSize (..))
20+import Foreign.ForeignPtr (withForeignPtr)
21+import Foreign.Marshal.Utils (with)
22+import Foreign.Ptr (Ptr, plusPtr)
23+import Foreign.Storable (peek)
24+import GHC.Base (MutableByteArray#)
25+import qualified Data.Text.Array as A
26+
27+foreign import ccall unsafe "_js_decode_string" c_js_decode
28+ :: MutableByteArray# s -> Ptr CSize
29+ -> Ptr Word8 -> Ptr Word8 -> IO CInt
30+
31+unescapeText' :: ByteString -> Text
32+unescapeText' (PS fp off len) = runText $ \done -> do
33+ let go dest = withForeignPtr fp $ \ptr ->
34+ with (0::CSize) $ \destOffPtr -> do
35+ let end = ptr `plusPtr` (off + len)
36+ loop curPtr = do
37+ res <- c_js_decode (A.maBA dest) destOffPtr curPtr end
38+ case res of
39+ 0 -> do
40+ n <- peek destOffPtr
41+ unsafeSTToIO (done dest (fromIntegral n))
42+ _ ->
43+ throw (DecodeError desc Nothing)
44+ loop (ptr `plusPtr` off)
45+ (unsafeIOToST . go) =<< A.new len
46+ where
47+ desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream"
48+{-# INLINE unescapeText' #-}
49+
50+unescapeText :: ByteString -> Either UnicodeException Text
51+unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
52+{-# INLINE unescapeText #-}
include/incoherent-compat.hnew file+7
@@ -0,0 +1,7 @@
1+#if __GLASGOW_HASKELL__ >= 710
2+#define INCOHERENT_ {-# INCOHERENT #-}
3+#else
4+-- This causes some type class instances to break:
5+-- {-# LANGUAGE IncoherentInstances #-}
6+#define INCOHERENT_
7+#endif
pure/Data/Aeson/Parser/UnescapePure.hsnew file+254
@@ -0,0 +1,254 @@
1+-- WARNING: This file is security sensitive as it uses unsafeWrite which does
2+-- not check bounds. Any changes should be made with care and we would love to
3+-- get informed about them, just cc us in any PR targetting this file: @eskimor @jprider63
4+-- We would be happy to review the changes!
5+
6+-- The security check at the end (pos > length) only works if pos grows
7+-- monotonously, if this condition does not hold, the check is flawed.
8+module Data.Aeson.Parser.UnescapePure
9+ (
10+ unescapeText
11+ ) where
12+
13+import Control.Exception (evaluate, throw, try)
14+import Control.Monad (when)
15+import Data.ByteString as B
16+import Data.Bits (Bits, shiftL, shiftR, (.&.), (.|.))
17+import Data.Text (Text)
18+import qualified Data.Text.Array as A
19+import Data.Text.Encoding.Error (UnicodeException (..))
20+import Data.Text.Internal.Private (runText)
21+import Data.Text.Unsafe (unsafeDupablePerformIO)
22+import Data.Word (Word8, Word16, Word32)
23+import GHC.ST (ST)
24+
25+-- Different UTF states.
26+data Utf =
27+ UtfGround
28+ | UtfTail1
29+ | UtfU32e0
30+ | UtfTail2
31+ | UtfU32ed
32+ | Utf843f0
33+ | UtfTail3
34+ | Utf843f4
35+ deriving (Eq)
36+
37+data State =
38+ StateNone
39+ | StateUtf !Utf !Word32
40+ | StateBackslash
41+ | StateU0
42+ | StateU1 !Word16
43+ | StateU2 !Word16
44+ | StateU3 !Word16
45+ | StateS0
46+ | StateS1
47+ | StateSU0
48+ | StateSU1 !Word16
49+ | StateSU2 !Word16
50+ | StateSU3 !Word16
51+ deriving (Eq)
52+
53+-- References:
54+-- http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
55+-- https://github.com/jwilm/vte/blob/master/utf8parse/src/table.rs.in
56+
57+setByte1 :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
58+setByte1 point word = point .|. fromIntegral (word .&. 0x3f)
59+{-# INLINE setByte1 #-}
60+
61+setByte2 :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
62+setByte2 point word = point .|. (fromIntegral (word .&. 0x3f) `shiftL` 6)
63+{-# INLINE setByte2 #-}
64+
65+setByte2Top :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
66+setByte2Top point word = point .|. (fromIntegral (word .&. 0x1f) `shiftL` 6)
67+{-# INLINE setByte2Top #-}
68+
69+setByte3 :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
70+setByte3 point word = point .|. (fromIntegral (word .&. 0x3f) `shiftL` 12)
71+{-# INLINE setByte3 #-}
72+
73+setByte3Top :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
74+setByte3Top point word = point .|. (fromIntegral (word .&. 0xf) `shiftL` 12)
75+{-# INLINE setByte3Top #-}
76+
77+setByte4 :: (Num a, Bits b, Bits a, Integral b) => a -> b -> a
78+setByte4 point word = point .|. (fromIntegral (word .&. 0x7) `shiftL` 18)
79+{-# INLINE setByte4 #-}
80+
81+decode :: Utf -> Word32 -> Word8 -> (Utf, Word32)
82+decode UtfGround point word = case word of
83+ w | 0x00 <= w && w <= 0x7f -> (UtfGround, fromIntegral word)
84+ w | 0xc2 <= w && w <= 0xdf -> (UtfTail1, setByte2Top point word)
85+ 0xe0 -> (UtfU32e0, setByte3Top point word)
86+ w | 0xe1 <= w && w <= 0xec -> (UtfTail2, setByte3Top point word)
87+ 0xed -> (UtfU32ed, setByte3Top point word)
88+ w | 0xee <= w && w <= 0xef -> (UtfTail2, setByte3Top point word)
89+ 0xf0 -> (Utf843f0, setByte4 point word)
90+ w | 0xf1 <= w && w <= 0xf3 -> (UtfTail3, setByte4 point word)
91+ 0xf4 -> (Utf843f4, setByte4 point word)
92+ _ -> throwDecodeError
93+
94+decode UtfU32e0 point word = case word of
95+ w | 0xa0 <= w && w <= 0xbf -> (UtfTail1, setByte2 point word)
96+ _ -> throwDecodeError
97+
98+decode UtfU32ed point word = case word of
99+ w | 0x80 <= w && w <= 0x9f -> (UtfTail1, setByte2 point word)
100+ _ -> throwDecodeError
101+
102+decode Utf843f0 point word = case word of
103+ w | 0x90 <= w && w <= 0xbf -> (UtfTail2, setByte3 point word)
104+ _ -> throwDecodeError
105+
106+decode Utf843f4 point word = case word of
107+ w | 0x80 <= w && w <= 0x8f -> (UtfTail2, setByte3 point word)
108+ _ -> throwDecodeError
109+
110+decode UtfTail3 point word = case word of
111+ w | 0x80 <= w && w <= 0xbf -> (UtfTail2, setByte3 point word)
112+ _ -> throwDecodeError
113+
114+decode UtfTail2 point word = case word of
115+ w | 0x80 <= w && w <= 0xbf -> (UtfTail1, setByte2 point word)
116+ _ -> throwDecodeError
117+
118+decode UtfTail1 point word = case word of
119+ w | 0x80 <= w && w <= 0xbf -> (UtfGround, setByte1 point word)
120+ _ -> throwDecodeError
121+
122+decodeHex :: 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
128+
129+unescapeText' :: ByteString -> Text
130+unescapeText' bs = runText $ \done -> do
131+ dest <- A.new len
132+
133+ (pos, finalState) <- loop dest (0, StateNone) 0
134+
135+ -- Check final state. Currently pos gets only increased over time, so this check should catch overflows.
136+ when ( finalState /= StateNone || pos > len)
137+ throwDecodeError
138+
139+ done dest pos -- TODO: pos, pos-1??? XXX
140+
141+ where
142+ len = B.length bs
143+
144+ runUtf dest pos st point c = case decode st point c of
145+ (UtfGround, 92) -> -- Backslash
146+ return (pos, StateBackslash)
147+ (UtfGround, w) | w <= 0xffff ->
148+ writeAndReturn dest pos (fromIntegral w) StateNone
149+ (UtfGround, w) -> do
150+ write dest pos (0xd7c0 + fromIntegral (w `shiftR` 10))
151+ writeAndReturn dest (pos + 1) (0xdc00 + fromIntegral (w .&. 0x3ff)) StateNone
152+ (st', p) ->
153+ return (pos, StateUtf st' p)
154+
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
161+
162+ -- No pending state.
163+ f dest (pos, StateNone) c = runUtf dest pos UtfGround 0 c
164+
165+ -- In the middle of parsing a UTF string.
166+ f dest (pos, StateUtf st point) c = runUtf dest pos st point c
167+
168+ -- In the middle of escaping a backslash.
169+ f dest (pos, StateBackslash) 34 = writeAndReturn dest pos 34 StateNone -- "
170+ f dest (pos, StateBackslash) 92 = writeAndReturn dest pos 92 StateNone -- Backslash
171+ f dest (pos, StateBackslash) 47 = writeAndReturn dest pos 47 StateNone -- /
172+ f dest (pos, StateBackslash) 98 = writeAndReturn dest pos 8 StateNone -- b
173+ f dest (pos, StateBackslash) 102 = writeAndReturn dest pos 12 StateNone -- f
174+ f dest (pos, StateBackslash) 110 = writeAndReturn dest pos 10 StateNone -- n
175+ f dest (pos, StateBackslash) 114 = writeAndReturn dest pos 13 StateNone -- r
176+ f dest (pos, StateBackslash) 116 = writeAndReturn dest pos 9 StateNone -- t
177+ f _ (pos, StateBackslash) 117 = return (pos, StateU0) -- u
178+ f _ ( _, StateBackslash) _ = throwDecodeError
179+
180+ -- Processing '\u'.
181+ f _ (pos, StateU0) c =
182+ let w = decodeHex c in
183+ return (pos, StateU1 (w `shiftL` 12))
184+
185+ f _ (pos, StateU1 w') c =
186+ let w = decodeHex c in
187+ return (pos, StateU2 (w' .|. (w `shiftL` 8)))
188+
189+ f _ (pos, StateU2 w') c =
190+ let w = decodeHex c in
191+ return (pos, StateU3 (w' .|. (w `shiftL` 4)))
192+
193+ f dest (pos, StateU3 w') c =
194+ let w = decodeHex c in
195+ let u = w' .|. w in
196+
197+ -- Get next state based on surrogates.
198+ let st
199+ | u >= 0xd800 && u <= 0xdbff = -- High surrogate.
200+ StateS0
201+ | u >= 0xdc00 && u <= 0xdfff = -- Low surrogate.
202+ throwDecodeError
203+ | otherwise =
204+ StateNone
205+ in
206+ writeAndReturn dest pos u st
207+
208+ -- Handle surrogates.
209+ f _ (pos, StateS0) 92 = return (pos, StateS1) -- Backslash
210+ f _ ( _, StateS0) _ = throwDecodeError
211+
212+ f _ (pos, StateS1) 117 = return (pos, StateSU0) -- u
213+ f _ ( _, StateS1) _ = throwDecodeError
214+
215+ f _ (pos, StateSU0) c =
216+ let w = decodeHex c in
217+ return (pos, StateSU1 (w `shiftL` 12))
218+
219+ f _ (pos, StateSU1 w') c =
220+ let w = decodeHex c in
221+ return (pos, StateSU2 (w' .|. (w `shiftL` 8)))
222+
223+ f _ (pos, StateSU2 w') c =
224+ let w = decodeHex c in
225+ return (pos, StateSU3 (w' .|. (w `shiftL` 4)))
226+
227+ f dest (pos, StateSU3 w') c =
228+ let w = decodeHex c in
229+ let u = w' .|. w in
230+
231+ -- Check if not low surrogate.
232+ if u < 0xdc00 || u > 0xdfff then
233+ throwDecodeError
234+ else
235+ writeAndReturn dest pos u StateNone
236+
237+write :: A.MArray s -> Int -> Word16 -> ST s ()
238+write dest pos char =
239+ A.unsafeWrite dest pos char
240+{-# INLINE write #-}
241+
242+writeAndReturn :: A.MArray s -> Int -> Word16 -> t -> ST s (Int, t)
243+writeAndReturn dest pos char res = do
244+ write dest pos char
245+ return (pos + 1, res)
246+{-# INLINE writeAndReturn #-}
247+
248+throwDecodeError :: a
249+throwDecodeError =
250+ let desc = "Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream" in
251+ throw (DecodeError desc Nothing)
252+
253+unescapeText :: ByteString -> Either UnicodeException Text
254+unescapeText = unsafeDupablePerformIO . try . evaluate . unescapeText'
run-hlint.shnew file+7
@@ -0,0 +1,7 @@
1+#!/bin/bash
2+
3+if hash hlint 2>/dev/null; then
4+ hlint "$@"
5+else
6+ ./hlint.sh "$@"
7+fi
stack-bench.yaml+10−6
@@ -1,8 +1,12 @@
1−resolver: lts-6.5
1+resolver: nightly-2019-09-21
2+# We use aeson in the snapshot to
3+# - avoid recompilation of criterion
4+# - compare against it
5+# - '.'
6+#
7+# Also we use separate working directory to avoid "unregistering aeson"
8+# caused recompilations
9+work-dir: .stack-work-bench
2packages:
10packages:
3- benchmarks
11- benchmarks
4−extra-deps:
12+extra-deps: []
5−- semigroups-0.18.2
6−flags:
7− semigroups:
8− bytestring-builder: false
stack-ffi-unescape.yamlnew file+9
@@ -0,0 +1,9 @@
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.yamlnew file+15
@@ -0,0 +1,15 @@
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.yamldeleted−12
@@ -1,12 +0,0 @@
1−resolver: lts-6.9
2−packages:
3−- '.'
4−- benchmarks
5−- examples
6−extra-deps:
7−- semigroups-0.18.2
8−flags:
9− aeson:
10− fast: true
11− semigroups:
12− bytestring-builder: false
stack-nightly.yamlnew file+11
@@ -0,0 +1,11 @@
1+resolver: nightly-2019-10-14
2+packages:
3+- '.'
4+- attoparsec-iso8601
5+flags:
6+ aeson:
7+ fast: true
8+ attoparsec-iso8601:
9+ fast: true
10+extra-deps:
11+- generic-deriving-1.12.4
tests/DataFamilies/Encoders.hs+4−1
@@ -1,8 +1,11 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
4
5
5−module DataFamilies.Encoders where
6+module DataFamilies.Encoders (module DataFamilies.Encoders) where
7+
8+import Prelude.Compat
6
9
7import Data.Aeson.TH
10import Data.Aeson.TH
8import Data.Aeson.Types
11import Data.Aeson.Types
tests/DataFamilies/Instances.hs+4−2
@@ -1,16 +1,18 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6+
5{-# OPTIONS_GHC -fno-warn-orphans #-}
7{-# OPTIONS_GHC -fno-warn-orphans #-}
6
8
7module DataFamilies.Instances () where
9module DataFamilies.Instances () where
8
10
9−import Control.Applicative
11+import Prelude.Compat
12+
10import Data.Aeson.TH
13import Data.Aeson.TH
11import Data.Aeson.Types (FromJSON(..))
14import Data.Aeson.Types (FromJSON(..))
12import DataFamilies.Types
15import DataFamilies.Types
13−import Prelude
14import Test.QuickCheck (Arbitrary(..), elements, oneof)
16import Test.QuickCheck (Arbitrary(..), elements, oneof)
15
17
16instance (Arbitrary a) => Arbitrary (Approx a) where
18instance (Arbitrary a) => Arbitrary (Approx a) where
tests/DataFamilies/Properties.hs+8−4
@@ -1,17 +1,21 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
2
3
3module DataFamilies.Properties (tests) where
4module DataFamilies.Properties (tests) where
4
5
6+import Prelude.Compat
7+
5import DataFamilies.Encoders
8import DataFamilies.Encoders
6import DataFamilies.Instances ()
9import DataFamilies.Instances ()
7−import Properties hiding (tests)
10+import PropUtils
11+
8
12
9−import Test.Framework (Test, testGroup)
13+import Test.Tasty (TestTree, testGroup)
10−import Test.Framework.Providers.QuickCheck2 (testProperty)
14+import Test.Tasty.QuickCheck (testProperty)
11
15
12--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
13
17
14−tests :: Test
18+tests :: TestTree
15tests = testGroup "data families" [
19tests = testGroup "data families" [
16 testGroup "template-haskell" [
20 testGroup "template-haskell" [
17 testGroup "toJSON" [
21 testGroup "toJSON" [
tests/DataFamilies/Types.hs+4−1
@@ -4,11 +4,14 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE NoImplicitPrelude #-}
7{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE TemplateHaskell #-}
9{-# LANGUAGE TemplateHaskell #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
10
11
11−module DataFamilies.Types where
12+module DataFamilies.Types (module DataFamilies.Types) where
13+
14+import Prelude.Compat
12
15
13import Generics.Deriving.TH (deriveAll0)
16import Generics.Deriving.TH (deriveAll0)
14import Types (ApproxEq(..))
17import Types (ApproxEq(..))
tests/Encoders.hs+144−7
@@ -1,8 +1,12 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
4
5
5−module Encoders where
6+module Encoders (module Encoders) where
7+
8+import Prelude.Compat
9+import Data.Text (Text)
6
10
7import Data.Aeson.TH
11import Data.Aeson.TH
8import Data.Aeson.Types
12import Data.Aeson.Types
@@ -93,6 +97,18 @@ gNullaryToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingle
93gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
97gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
94gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
98gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
95
99
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"
96
112
97--------------------------------------------------------------------------------
113--------------------------------------------------------------------------------
98-- SomeType encoders/decoders
114-- SomeType encoders/decoders
@@ -172,7 +188,6 @@ gSomeTypeToEncoding2ElemArray = genericToEncoding opts2ElemArray
172gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
188gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
173gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
189gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
174
190
175−#if __GLASGOW_HASKELL__ >= 706
176gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
191gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
177gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
192gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
178
193
@@ -181,7 +196,6 @@ gSomeTypeLiftToJSON2ElemArray = genericLiftToJSON opts2ElemArray
181
196
182gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
197gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
183gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
198gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
184−#endif
185
199
186
200
187gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
201gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
@@ -193,7 +207,6 @@ gSomeTypeToEncodingTaggedObject = genericToEncoding optsTaggedObject
193gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
207gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
194gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
208gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
195
209
196−#if __GLASGOW_HASKELL__ >= 706
197gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
210gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
198gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
211gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
199
212
@@ -202,7 +215,6 @@ gSomeTypeLiftToJSONTaggedObject = genericLiftToJSON optsTaggedObject
202
215
203gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
216gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
204gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
217gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
205−#endif
206
218
207
219
208gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
220gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
@@ -214,7 +226,6 @@ gSomeTypeToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingl
214gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
226gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
215gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
227gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
216
228
217−#if __GLASGOW_HASKELL__ >= 706
218gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
229gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
219gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
230gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
220
231
@@ -223,7 +234,6 @@ gSomeTypeLiftToJSONObjectWithSingleField = genericLiftToJSON optsObjectWithSingl
223
234
224gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
235gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
225gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
236gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
226−#endif
227
237
228
238
229gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
239gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
@@ -232,6 +242,82 @@ gSomeTypeToJSONOmitNothingFields = genericToJSON optsOmitNothingFields
232gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
242gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
233gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
243gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
234
244
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+
297+--------------------------------------------------------------------------------
298+-- IncoherentInstancesNeeded
299+--------------------------------------------------------------------------------
300+
301+-- | This test demonstrates the need for IncoherentInstances. See the definition
302+-- of 'IncoherentInstancesNeeded' for a discussion of the issue.
303+--
304+-- NOTE 1: We only need to compile this test. We do not need to run it.
305+--
306+-- NOTE 2: We actually only use the INCOHERENT pragma on specific instances
307+-- instead of the IncoherentInstances language extension. Therefore, this is
308+-- only supported on GHC versions >= 7.10.
309+#if __GLASGOW_HASKELL__ >= 710
310+incoherentInstancesNeededParseJSONString :: FromJSON a => Value -> Parser (IncoherentInstancesNeeded a)
311+incoherentInstancesNeededParseJSONString = case () of
312+ _ | True -> $(mkParseJSON defaultOptions ''IncoherentInstancesNeeded)
313+ | False -> genericParseJSON defaultOptions
314+
315+incoherentInstancesNeededToJSON :: ToJSON a => IncoherentInstancesNeeded a -> Value
316+incoherentInstancesNeededToJSON = case () of
317+ _ | True -> $(mkToJSON defaultOptions ''IncoherentInstancesNeeded)
318+ | False -> genericToJSON defaultOptions
319+#endif
320+
235-------------------------------------------------------------------------------
321-------------------------------------------------------------------------------
236-- EitherTextInt encoders/decodes
322-- EitherTextInt encoders/decodes
237-------------------------------------------------------------------------------
323-------------------------------------------------------------------------------
@@ -319,3 +405,54 @@ thGADTToEncodingDefault = $(mkToEncoding defaultOptions ''GADT)
319
405
320thGADTParseJSONDefault :: Value -> Parser (GADT String)
406thGADTParseJSONDefault :: Value -> Parser (GADT String)
321thGADTParseJSONDefault = $(mkParseJSON defaultOptions ''GADT)
407thGADTParseJSONDefault = $(mkParseJSON defaultOptions ''GADT)
408+
409+--------------------------------------------------------------------------------
410+-- OneConstructor encoders/decoders
411+--------------------------------------------------------------------------------
412+
413+thOneConstructorToJSONDefault :: OneConstructor -> Value
414+thOneConstructorToJSONDefault = $(mkToJSON defaultOptions ''OneConstructor)
415+
416+thOneConstructorToEncodingDefault :: OneConstructor -> Encoding
417+thOneConstructorToEncodingDefault = $(mkToEncoding defaultOptions ''OneConstructor)
418+
419+thOneConstructorParseJSONDefault :: Value -> Parser OneConstructor
420+thOneConstructorParseJSONDefault = $(mkParseJSON defaultOptions ''OneConstructor)
421+
422+thOneConstructorToJSONTagged :: OneConstructor -> Value
423+thOneConstructorToJSONTagged = $(mkToJSON optsTagSingleConstructors ''OneConstructor)
424+
425+thOneConstructorToEncodingTagged :: OneConstructor -> Encoding
426+thOneConstructorToEncodingTagged = $(mkToEncoding optsTagSingleConstructors ''OneConstructor)
427+
428+thOneConstructorParseJSONTagged :: Value -> Parser OneConstructor
429+thOneConstructorParseJSONTagged = $(mkParseJSON optsTagSingleConstructors ''OneConstructor)
430+
431+
432+gOneConstructorToJSONDefault :: OneConstructor -> Value
433+gOneConstructorToJSONDefault = genericToJSON defaultOptions
434+
435+gOneConstructorToEncodingDefault :: OneConstructor -> Encoding
436+gOneConstructorToEncodingDefault = genericToEncoding defaultOptions
437+
438+gOneConstructorParseJSONDefault :: Value -> Parser OneConstructor
439+gOneConstructorParseJSONDefault = genericParseJSON defaultOptions
440+
441+gOneConstructorToJSONTagged :: OneConstructor -> Value
442+gOneConstructorToJSONTagged = genericToJSON optsTagSingleConstructors
443+
444+gOneConstructorToEncodingTagged :: OneConstructor -> Encoding
445+gOneConstructorToEncodingTagged = genericToEncoding optsTagSingleConstructors
446+
447+gOneConstructorParseJSONTagged :: Value -> Parser OneConstructor
448+gOneConstructorParseJSONTagged = 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.hsnew file+224
@@ -0,0 +1,224 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+
5+module ErrorMessages
6+ (
7+ tests
8+ ) where
9+
10+import Prelude.Compat
11+
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)
17+import Data.Proxy (Proxy(..))
18+import Data.Semigroup ((<>))
19+import Data.Sequence (Seq)
20+import Instances ()
21+import Numeric.Natural (Natural)
22+import Test.Tasty (TestTree, TestName)
23+import Test.Tasty.Golden.Advanced (goldenTest)
24+import qualified Data.ByteString.Lazy.Char8 as L
25+import qualified Data.HashMap.Strict as HM
26+
27+import Encoders
28+import Types
29+
30+tests :: [TestTree]
31+tests =
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
tests/Functions.hs+4
@@ -1,9 +1,13 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Functions
3module Functions
2 (
4 (
3 approxEq
5 approxEq
4 , approxEqWith
6 , approxEqWith
5 ) where
7 ) where
6
8
9+import Prelude.Compat
10+
7approxEq :: (Fractional a, Ord a) => a -> a -> Bool
11approxEq :: (Fractional a, Ord a) => a -> a -> Bool
8approxEq = approxEqWith 1e-15 1e-15
12approxEq = approxEqWith 1e-15 1e-15
9
13
tests/Instances.hs+9−55
@@ -1,48 +1,35 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE RecordWildCards #-}
6−{-# LANGUAGE StandaloneDeriving #-}
6+
7{-# OPTIONS_GHC -fno-warn-orphans #-}
7{-# OPTIONS_GHC -fno-warn-orphans #-}
8
8
9module Instances () where
9module Instances () where
10
10
11−import Prelude ()
12import Prelude.Compat
11import Prelude.Compat
13
12
14−import Control.Applicative (Const(..), empty)
13+import Control.Applicative (empty)
15import Control.Monad
14import Control.Monad
16import Data.Aeson.Types
15import Data.Aeson.Types
17import Data.Function (on)
16import Data.Function (on)
18−import Data.Functor.Compose (Compose (..))
19−import Data.Functor.Identity (Identity (..))
20−import Data.List.NonEmpty (NonEmpty(..))
21−import Data.Proxy (Proxy(..))
22−import Data.Tagged (Tagged(..))
23import Data.Time (ZonedTime(..), TimeZone(..))
17import Data.Time (ZonedTime(..), TimeZone(..))
24import Data.Time.Clock (UTCTime(..))
18import Data.Time.Clock (UTCTime(..))
25−import Data.Version
26import Functions
19import Functions
27−import Test.QuickCheck (Arbitrary(..), getNonNegative, elements, listOf1, oneof, resize)
20+import Test.QuickCheck (Arbitrary(..), elements, oneof)
28import Types
21import Types
29import qualified Data.DList as DList
22import qualified Data.DList as DList
30import qualified Data.HashMap.Strict as HM
23import qualified Data.HashMap.Strict as HM
31
24
32import Data.Orphans ()
25import Data.Orphans ()
33import Test.QuickCheck.Instances ()
26import Test.QuickCheck.Instances ()
34−#if MIN_VERSION_base(4,7,0)
35import Data.Hashable.Time ()
27import Data.Hashable.Time ()
36−#endif
37−
38−#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
39−import Numeric.Natural
40−#endif
41
28
42-- "System" types.
29-- "System" types.
43
30
44instance Arbitrary DotNetTime where
31instance Arbitrary DotNetTime where
45− arbitrary = DotNetTime `liftM` arbitrary
32+ arbitrary = DotNetTime `fmap` arbitrary
46 shrink = map DotNetTime . shrink . fromDotNetTime
33 shrink = map DotNetTime . shrink . fromDotNetTime
47
34
48-- | Compare timezone part only on 'timeZoneMinutes'
35-- | Compare timezone part only on 'timeZoneMinutes'
@@ -166,48 +153,15 @@ instance Arbitrary EitherTextInt where
166instance Arbitrary (GADT String) where
153instance Arbitrary (GADT String) where
167 arbitrary = GADT <$> arbitrary
154 arbitrary = GADT <$> arbitrary
168
155
156+instance Arbitrary OptionField where
157+ arbitrary = OptionField <$> arbitrary
158+
159+
169instance ApproxEq Char where
160instance ApproxEq Char where
170 (=~) = (==)
161 (=~) = (==)
171
162
172instance (ApproxEq a) => ApproxEq [a] where
163instance (ApproxEq a) => ApproxEq [a] where
173 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
164 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
174
165
175−instance Arbitrary Version where
176− arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
177−
178−instance Arbitrary a => Arbitrary (NonEmpty a) where
179− arbitrary = (:|) <$> arbitrary <*> arbitrary
180−
181−-- Version tags are deprecated, so we avoid using them in the Arbitrary
182−-- instance. However, the recommended constructor 'makeVersion' is not
183−-- exported by "Data.Version" until base-4.8.0.0. For previous versions,
184−-- a definition is given below.
185−
186−#if !MIN_VERSION_base(4,8,0)
187−makeVersion :: [Int] -> Version
188−makeVersion b = Version b []
189−#endif
190−
191−#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
192−instance Arbitrary Natural where
193− arbitrary = fromInteger . abs <$> arbitrary
194−#endif
195−
196−instance Arbitrary (Proxy a) where
197− arbitrary = pure Proxy
198−
199−instance Arbitrary b => Arbitrary (Tagged a b) where
200− arbitrary = Tagged <$> arbitrary
201−
202−instance Arbitrary a => Arbitrary (Const a b) where
203− arbitrary = Const <$> arbitrary
204−
205instance Arbitrary a => Arbitrary (DList.DList a) where
166instance Arbitrary a => Arbitrary (DList.DList a) where
206 arbitrary = DList.fromList <$> arbitrary
167 arbitrary = DList.fromList <$> arbitrary
207−
208−-- would like to have -- https://github.com/nick8325/quickcheck/issues/73
209−instance Arbitrary a => Arbitrary (Identity a) where
210− arbitrary = Identity <$> arbitrary
211−
212−instance Arbitrary (f (g a)) => Arbitrary (Compose f g a) where
213− arbitrary = Compose <$> arbitrary
tests/JSONTestSuite/LICENSEnew file+21
@@ -0,0 +1,21 @@
1+MIT License
2+
3+Copyright (c) 2016 Nicolas Seriot
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
tests/JSONTestSuite/README.mdnew file+50
@@ -0,0 +1,50 @@
1+# JSON Parsing Test Suite
2+A comprehensive test suite for RFC 7159 compliant JSON parsers
3+
4+This repository was created as an appendix to the article [Parsing JSON is a Minefield 💣](http://seriot.ch/parsing_json.php).
5+
6+**/parsers/**
7+
8+This directory contains several parsers and tiny wrappers to turn the parsers into JSON validators, by returning a specific value.
9+
10+- `0` the parser did accept the content
11+- `1` the parser did reject the content
12+- `>=1` the process did crash
13+- `timeout` happens after 5 seconds
14+
15+**/test\_parsing/**
16+
17+The name of these files tell if their contents should be accepted or rejected.
18+
19+- `y_` content must be accepted by parsers
20+- `n_` content must be rejected by parsers
21+- `i_` parsers are free to accept or reject content
22+
23+**/test\_transform/**
24+
25+These files contain weird structures and characters that parsers may understand differently, eg:
26+
27+- huge numbers
28+- dictionaries with similar keys
29+- NULL characters
30+- escaped invalid strings
31+
32+These files were used to produce `results/transform.html`.
33+
34+**/run_tests.py**
35+
36+Run all parsers with all files:
37+
38+ $ python3 run_tests.py
39+
40+Run all parsers with a specific file:
41+
42+ $ python3 run_tests.py file.json
43+
44+The script writes logs in `results/logs.txt`.
45+
46+The script then reads `logs.txt` and generates `results/parsing.html`.
47+
48+**/results/**
49+
50+<img src="results/pruned_results.png" alt="JSON Parsing Tests" />
tests/JSONTestSuite/parsers/test-AppleJSONSerialization/bin/test-AppleJSONSerializationnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization.xcodeproj/project.pbxprojnew file+260
@@ -0,0 +1,260 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 46;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 03997F941D70CE0C0001C89F /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03997F931D70CE0C0001C89F /* main.swift */; };
11+/* End PBXBuildFile section */
12+
13+/* Begin PBXCopyFilesBuildPhase section */
14+ 03997F8E1D70CE0C0001C89F /* CopyFiles */ = {
15+ isa = PBXCopyFilesBuildPhase;
16+ buildActionMask = 2147483647;
17+ dstPath = /usr/share/man/man1/;
18+ dstSubfolderSpec = 0;
19+ files = (
20+ );
21+ runOnlyForDeploymentPostprocessing = 1;
22+ };
23+/* End PBXCopyFilesBuildPhase section */
24+
25+/* Begin PBXFileReference section */
26+ 03997F901D70CE0C0001C89F /* test-AppleJSONSerialization */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "test-AppleJSONSerialization"; sourceTree = BUILT_PRODUCTS_DIR; };
27+ 03997F931D70CE0C0001C89F /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
28+/* End PBXFileReference section */
29+
30+/* Begin PBXFrameworksBuildPhase section */
31+ 03997F8D1D70CE0C0001C89F /* Frameworks */ = {
32+ isa = PBXFrameworksBuildPhase;
33+ buildActionMask = 2147483647;
34+ files = (
35+ );
36+ runOnlyForDeploymentPostprocessing = 0;
37+ };
38+/* End PBXFrameworksBuildPhase section */
39+
40+/* Begin PBXGroup section */
41+ 03997F871D70CE0C0001C89F = {
42+ isa = PBXGroup;
43+ children = (
44+ 03997F921D70CE0C0001C89F /* test-AppleJSONSerialization */,
45+ 03997F911D70CE0C0001C89F /* Products */,
46+ );
47+ sourceTree = "<group>";
48+ };
49+ 03997F911D70CE0C0001C89F /* Products */ = {
50+ isa = PBXGroup;
51+ children = (
52+ 03997F901D70CE0C0001C89F /* test-AppleJSONSerialization */,
53+ );
54+ name = Products;
55+ sourceTree = "<group>";
56+ };
57+ 03997F921D70CE0C0001C89F /* test-AppleJSONSerialization */ = {
58+ isa = PBXGroup;
59+ children = (
60+ 03997F931D70CE0C0001C89F /* main.swift */,
61+ );
62+ path = "test-AppleJSONSerialization";
63+ sourceTree = "<group>";
64+ };
65+/* End PBXGroup section */
66+
67+/* Begin PBXNativeTarget section */
68+ 03997F8F1D70CE0C0001C89F /* test-AppleJSONSerialization */ = {
69+ isa = PBXNativeTarget;
70+ buildConfigurationList = 03997F971D70CE0C0001C89F /* Build configuration list for PBXNativeTarget "test-AppleJSONSerialization" */;
71+ buildPhases = (
72+ 03997F8C1D70CE0C0001C89F /* Sources */,
73+ 03997F8D1D70CE0C0001C89F /* Frameworks */,
74+ 03997F8E1D70CE0C0001C89F /* CopyFiles */,
75+ );
76+ buildRules = (
77+ );
78+ dependencies = (
79+ );
80+ name = "test-AppleJSONSerialization";
81+ productName = "test-AppleJSONSerialization";
82+ productReference = 03997F901D70CE0C0001C89F /* test-AppleJSONSerialization */;
83+ productType = "com.apple.product-type.tool";
84+ };
85+/* End PBXNativeTarget section */
86+
87+/* Begin PBXProject section */
88+ 03997F881D70CE0C0001C89F /* Project object */ = {
89+ isa = PBXProject;
90+ attributes = {
91+ LastSwiftUpdateCheck = 0800;
92+ LastUpgradeCheck = 0800;
93+ ORGANIZATIONNAME = "Nicolas Seriot";
94+ TargetAttributes = {
95+ 03997F8F1D70CE0C0001C89F = {
96+ CreatedOnToolsVersion = 8.0;
97+ DevelopmentTeam = VBYRKYS73S;
98+ ProvisioningStyle = Automatic;
99+ };
100+ };
101+ };
102+ buildConfigurationList = 03997F8B1D70CE0C0001C89F /* Build configuration list for PBXProject "test-AppleJSONSerialization" */;
103+ compatibilityVersion = "Xcode 3.2";
104+ developmentRegion = English;
105+ hasScannedForEncodings = 0;
106+ knownRegions = (
107+ en,
108+ );
109+ mainGroup = 03997F871D70CE0C0001C89F;
110+ productRefGroup = 03997F911D70CE0C0001C89F /* Products */;
111+ projectDirPath = "";
112+ projectRoot = "";
113+ targets = (
114+ 03997F8F1D70CE0C0001C89F /* test-AppleJSONSerialization */,
115+ );
116+ };
117+/* End PBXProject section */
118+
119+/* Begin PBXSourcesBuildPhase section */
120+ 03997F8C1D70CE0C0001C89F /* Sources */ = {
121+ isa = PBXSourcesBuildPhase;
122+ buildActionMask = 2147483647;
123+ files = (
124+ 03997F941D70CE0C0001C89F /* main.swift in Sources */,
125+ );
126+ runOnlyForDeploymentPostprocessing = 0;
127+ };
128+/* End PBXSourcesBuildPhase section */
129+
130+/* Begin XCBuildConfiguration section */
131+ 03997F951D70CE0C0001C89F /* Debug */ = {
132+ isa = XCBuildConfiguration;
133+ buildSettings = {
134+ ALWAYS_SEARCH_USER_PATHS = NO;
135+ CLANG_ANALYZER_NONNULL = YES;
136+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
137+ CLANG_CXX_LIBRARY = "libc++";
138+ CLANG_ENABLE_MODULES = YES;
139+ CLANG_ENABLE_OBJC_ARC = YES;
140+ CLANG_WARN_BOOL_CONVERSION = YES;
141+ CLANG_WARN_CONSTANT_CONVERSION = YES;
142+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
143+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
144+ CLANG_WARN_EMPTY_BODY = YES;
145+ CLANG_WARN_ENUM_CONVERSION = YES;
146+ CLANG_WARN_INFINITE_RECURSION = YES;
147+ CLANG_WARN_INT_CONVERSION = YES;
148+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
149+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
150+ CLANG_WARN_UNREACHABLE_CODE = YES;
151+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
152+ CODE_SIGN_IDENTITY = "-";
153+ COPY_PHASE_STRIP = NO;
154+ DEBUG_INFORMATION_FORMAT = dwarf;
155+ ENABLE_STRICT_OBJC_MSGSEND = YES;
156+ ENABLE_TESTABILITY = YES;
157+ GCC_C_LANGUAGE_STANDARD = gnu99;
158+ GCC_DYNAMIC_NO_PIC = NO;
159+ GCC_NO_COMMON_BLOCKS = YES;
160+ GCC_OPTIMIZATION_LEVEL = 0;
161+ GCC_PREPROCESSOR_DEFINITIONS = (
162+ "DEBUG=1",
163+ "$(inherited)",
164+ );
165+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
166+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
167+ GCC_WARN_UNDECLARED_SELECTOR = YES;
168+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
169+ GCC_WARN_UNUSED_FUNCTION = YES;
170+ GCC_WARN_UNUSED_VARIABLE = YES;
171+ MACOSX_DEPLOYMENT_TARGET = 10.11;
172+ MTL_ENABLE_DEBUG_INFO = YES;
173+ ONLY_ACTIVE_ARCH = YES;
174+ SDKROOT = macosx;
175+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
176+ };
177+ name = Debug;
178+ };
179+ 03997F961D70CE0C0001C89F /* Release */ = {
180+ isa = XCBuildConfiguration;
181+ buildSettings = {
182+ ALWAYS_SEARCH_USER_PATHS = NO;
183+ CLANG_ANALYZER_NONNULL = YES;
184+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
185+ CLANG_CXX_LIBRARY = "libc++";
186+ CLANG_ENABLE_MODULES = YES;
187+ CLANG_ENABLE_OBJC_ARC = YES;
188+ CLANG_WARN_BOOL_CONVERSION = YES;
189+ CLANG_WARN_CONSTANT_CONVERSION = YES;
190+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
191+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
192+ CLANG_WARN_EMPTY_BODY = YES;
193+ CLANG_WARN_ENUM_CONVERSION = YES;
194+ CLANG_WARN_INFINITE_RECURSION = YES;
195+ CLANG_WARN_INT_CONVERSION = YES;
196+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
197+ CLANG_WARN_SUSPICIOUS_MOVES = YES;
198+ CLANG_WARN_UNREACHABLE_CODE = YES;
199+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
200+ CODE_SIGN_IDENTITY = "-";
201+ COPY_PHASE_STRIP = NO;
202+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
203+ ENABLE_NS_ASSERTIONS = NO;
204+ ENABLE_STRICT_OBJC_MSGSEND = YES;
205+ GCC_C_LANGUAGE_STANDARD = gnu99;
206+ GCC_NO_COMMON_BLOCKS = YES;
207+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
208+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
209+ GCC_WARN_UNDECLARED_SELECTOR = YES;
210+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
211+ GCC_WARN_UNUSED_FUNCTION = YES;
212+ GCC_WARN_UNUSED_VARIABLE = YES;
213+ MACOSX_DEPLOYMENT_TARGET = 10.11;
214+ MTL_ENABLE_DEBUG_INFO = NO;
215+ SDKROOT = macosx;
216+ };
217+ name = Release;
218+ };
219+ 03997F981D70CE0C0001C89F /* Debug */ = {
220+ isa = XCBuildConfiguration;
221+ buildSettings = {
222+ DEVELOPMENT_TEAM = VBYRKYS73S;
223+ PRODUCT_NAME = "$(TARGET_NAME)";
224+ SWIFT_VERSION = 3.0;
225+ };
226+ name = Debug;
227+ };
228+ 03997F991D70CE0C0001C89F /* Release */ = {
229+ isa = XCBuildConfiguration;
230+ buildSettings = {
231+ DEVELOPMENT_TEAM = VBYRKYS73S;
232+ PRODUCT_NAME = "$(TARGET_NAME)";
233+ SWIFT_VERSION = 3.0;
234+ };
235+ name = Release;
236+ };
237+/* End XCBuildConfiguration section */
238+
239+/* Begin XCConfigurationList section */
240+ 03997F8B1D70CE0C0001C89F /* Build configuration list for PBXProject "test-AppleJSONSerialization" */ = {
241+ isa = XCConfigurationList;
242+ buildConfigurations = (
243+ 03997F951D70CE0C0001C89F /* Debug */,
244+ 03997F961D70CE0C0001C89F /* Release */,
245+ );
246+ defaultConfigurationIsVisible = 0;
247+ defaultConfigurationName = Release;
248+ };
249+ 03997F971D70CE0C0001C89F /* Build configuration list for PBXNativeTarget "test-AppleJSONSerialization" */ = {
250+ isa = XCConfigurationList;
251+ buildConfigurations = (
252+ 03997F981D70CE0C0001C89F /* Debug */,
253+ 03997F991D70CE0C0001C89F /* Release */,
254+ );
255+ defaultConfigurationIsVisible = 0;
256+ };
257+/* End XCConfigurationList section */
258+ };
259+ rootObject = 03997F881D70CE0C0001C89F /* Project object */;
260+}
tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:test-AppleJSONSerialization.xcodeproj">
6+ </FileRef>
7+</Workspace>
tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test-AppleJSONSerialization.xcschemenew file+91
@@ -0,0 +1,91 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "0800"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "03997F8F1D70CE0C0001C89F"
18+ BuildableName = "test-AppleJSONSerialization"
19+ BlueprintName = "test-AppleJSONSerialization"
20+ ReferencedContainer = "container:test-AppleJSONSerialization.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "03997F8F1D70CE0C0001C89F"
36+ BuildableName = "test-AppleJSONSerialization"
37+ BlueprintName = "test-AppleJSONSerialization"
38+ ReferencedContainer = "container:test-AppleJSONSerialization.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "03997F8F1D70CE0C0001C89F"
59+ BuildableName = "test-AppleJSONSerialization"
60+ BlueprintName = "test-AppleJSONSerialization"
61+ ReferencedContainer = "container:test-AppleJSONSerialization.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ </AdditionalOptions>
66+ </LaunchAction>
67+ <ProfileAction
68+ buildConfiguration = "Release"
69+ shouldUseLaunchSchemeArgsEnv = "YES"
70+ savedToolIdentifier = ""
71+ useCustomWorkingDirectory = "NO"
72+ debugDocumentVersioning = "YES">
73+ <BuildableProductRunnable
74+ runnableDebuggingMode = "0">
75+ <BuildableReference
76+ BuildableIdentifier = "primary"
77+ BlueprintIdentifier = "03997F8F1D70CE0C0001C89F"
78+ BuildableName = "test-AppleJSONSerialization"
79+ BlueprintName = "test-AppleJSONSerialization"
80+ ReferencedContainer = "container:test-AppleJSONSerialization.xcodeproj">
81+ </BuildableReference>
82+ </BuildableProductRunnable>
83+ </ProfileAction>
84+ <AnalyzeAction
85+ buildConfiguration = "Debug">
86+ </AnalyzeAction>
87+ <ArchiveAction
88+ buildConfiguration = "Release"
89+ revealArchiveInOrganizer = "YES">
90+ </ArchiveAction>
91+</Scheme>
tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22
@@ -0,0 +1,22 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>SchemeUserState</key>
6+ <dict>
7+ <key>test-AppleJSONSerialization.xcscheme</key>
8+ <dict>
9+ <key>orderHint</key>
10+ <integer>0</integer>
11+ </dict>
12+ </dict>
13+ <key>SuppressBuildableAutocreation</key>
14+ <dict>
15+ <key>03997F8F1D70CE0C0001C89F</key>
16+ <dict>
17+ <key>primary</key>
18+ <true/>
19+ </dict>
20+ </dict>
21+</dict>
22+</plist>
tests/JSONTestSuite/parsers/test-AppleJSONSerialization/test-AppleJSONSerialization/main.swiftnew file+34
@@ -0,0 +1,34 @@
1+//
2+// main.swift
3+// test-AppleJSONSerialization
4+//
5+// Created by nst on 26/08/16.
6+// Copyright © 2016 Nicolas Seriot. All rights reserved.
7+//
8+
9+import Foundation
10+
11+func main() {
12+
13+ guard ProcessInfo.processInfo.arguments.count == 2 else {
14+ let url = NSURL(fileURLWithPath: ProcessInfo.processInfo.arguments[0])
15+ guard let programName = url.lastPathComponent else { exit(1) }
16+ print("Usage: ./\(programName) file.json")
17+ exit(1)
18+ }
19+
20+ let path = ProcessInfo.processInfo.arguments[1]
21+ let url = NSURL.fileURL(withPath:path)
22+
23+ do {
24+ let data = try Data(contentsOf:url)
25+
26+ let _ = try JSONSerialization.jsonObject(with: data, options: [.allowFragments])
27+
28+ exit(0)
29+ } catch {
30+ exit(1)
31+ }
32+}
33+
34+main()
tests/JSONTestSuite/parsers/test_Bash_JSON/JSON.shnew file+208
@@ -0,0 +1,208 @@
1+#!/bin/sh
2+
3+throw() {
4+ echo "$*" >&2
5+ exit 1
6+}
7+
8+BRIEF=0
9+LEAFONLY=0
10+PRUNE=0
11+NO_HEAD=0
12+NORMALIZE_SOLIDUS=0
13+
14+usage() {
15+ echo
16+ echo "Usage: JSON.sh [-b] [-l] [-p] [-s] [-h]"
17+ echo
18+ echo "-p - Prune empty. Exclude fields with empty values."
19+ echo "-l - Leaf only. Only show leaf nodes, which stops data duplication."
20+ echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options."
21+ echo "-n - No-head. Do not show nodes that have no path (lines that start with [])."
22+ echo "-s - Remove escaping of the solidus symbol (straight slash)."
23+ echo "-h - This help text."
24+ echo
25+}
26+
27+parse_options() {
28+ set -- "$@"
29+ local ARGN=$#
30+ while [ "$ARGN" -ne 0 ]
31+ do
32+ case $1 in
33+ -h) usage
34+ exit 0
35+ ;;
36+ -b) BRIEF=1
37+ LEAFONLY=1
38+ PRUNE=1
39+ ;;
40+ -l) LEAFONLY=1
41+ ;;
42+ -p) PRUNE=1
43+ ;;
44+ -n) NO_HEAD=1
45+ ;;
46+ -s) NORMALIZE_SOLIDUS=1
47+ ;;
48+ ?*) echo "ERROR: Unknown option."
49+ usage
50+ exit 0
51+ ;;
52+ esac
53+ shift 1
54+ ARGN=$((ARGN-1))
55+ done
56+}
57+
58+awk_egrep () {
59+ local pattern_string=$1
60+
61+ gawk '{
62+ while ($0) {
63+ start=match($0, pattern);
64+ token=substr($0, start, RLENGTH);
65+ print token;
66+ $0=substr($0, start+RLENGTH);
67+ }
68+ }' pattern="$pattern_string"
69+}
70+
71+tokenize () {
72+ local GREP
73+ local ESCAPE
74+ local CHAR
75+
76+ if echo "test string" | egrep -ao --color=never "test" >/dev/null 2>&1
77+ then
78+ GREP='egrep -ao --color=never'
79+ else
80+ GREP='egrep -ao'
81+ fi
82+
83+ if echo "test string" | egrep -o "test" >/dev/null 2>&1
84+ then
85+ ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
86+ CHAR='[^[:cntrl:]"\\]'
87+ else
88+ GREP=awk_egrep
89+ ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
90+ CHAR='[^[:cntrl:]"\\\\]'
91+ fi
92+
93+ local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
94+ local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
95+ local KEYWORD='null|false|true'
96+ local SPACE='[[:space:]]+'
97+
98+ # Force zsh to expand $A into multiple words
99+ local is_wordsplit_disabled=$(unsetopt 2>/dev/null | grep -c '^shwordsplit$')
100+ if [ $is_wordsplit_disabled != 0 ]; then setopt shwordsplit; fi
101+ $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$"
102+ if [ $is_wordsplit_disabled != 0 ]; then unsetopt shwordsplit; fi
103+}
104+
105+parse_array () {
106+ local index=0
107+ local ary=''
108+ read -r token
109+ case "$token" in
110+ ']') ;;
111+ *)
112+ while :
113+ do
114+ parse_value "$1" "$index"
115+ index=$((index+1))
116+ ary="$ary""$value"
117+ read -r token
118+ case "$token" in
119+ ']') break ;;
120+ ',') ary="$ary," ;;
121+ *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;;
122+ esac
123+ read -r token
124+ done
125+ ;;
126+ esac
127+ [ "$BRIEF" -eq 0 ] && value=$(printf '[%s]' "$ary") || value=
128+ :
129+}
130+
131+parse_object () {
132+ local key
133+ local obj=''
134+ read -r token
135+ case "$token" in
136+ '}') ;;
137+ *)
138+ while :
139+ do
140+ case "$token" in
141+ '"'*'"') key=$token ;;
142+ *) throw "EXPECTED string GOT ${token:-EOF}" ;;
143+ esac
144+ read -r token
145+ case "$token" in
146+ ':') ;;
147+ *) throw "EXPECTED : GOT ${token:-EOF}" ;;
148+ esac
149+ read -r token
150+ parse_value "$1" "$key"
151+ obj="$obj$key:$value"
152+ read -r token
153+ case "$token" in
154+ '}') break ;;
155+ ',') obj="$obj," ;;
156+ *) throw "EXPECTED , or } GOT ${token:-EOF}" ;;
157+ esac
158+ read -r token
159+ done
160+ ;;
161+ esac
162+ [ "$BRIEF" -eq 0 ] && value=$(printf '{%s}' "$obj") || value=
163+ :
164+}
165+
166+parse_value () {
167+ local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0
168+ case "$token" in
169+ '{') parse_object "$jpath" ;;
170+ '[') parse_array "$jpath" ;;
171+ # At this point, the only valid single-character tokens are digits.
172+ ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;;
173+ *) value=$token
174+ # if asked, replace solidus ("\/") in json strings with normalized value: "/"
175+ [ "$NORMALIZE_SOLIDUS" -eq 1 ] && value=$(echo "$value" | sed 's#\\/#/#g')
176+ isleaf=1
177+ [ "$value" = '""' ] && isempty=1
178+ ;;
179+ esac
180+ [ "$value" = '' ] && return
181+ [ "$NO_HEAD" -eq 1 ] && [ -z "$jpath" ] && return
182+
183+ [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1
184+ [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1
185+ [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1
186+ [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \
187+ [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1
188+ [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value"
189+ :
190+}
191+
192+parse () {
193+ read -r token
194+ parse_value
195+ read -r token
196+ case "$token" in
197+ '') ;;
198+ *) throw "EXPECTED EOF GOT $token" ;;
199+ esac
200+}
201+
202+if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]);
203+then
204+ parse_options "$@"
205+ tokenize | parse
206+fi
207+
208+# vi: expandtab sw=2 ts=2
tests/JSONTestSuite/parsers/test_Freddy_20160830/bin/test_Freddynew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/project.pbxprojnew file+300
@@ -0,0 +1,300 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 46;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D84ABA1D5B0A1000776402 /* main.swift */; };
11+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7781D5B75600021630C /* JSON.swift */; };
12+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7791D5B75600021630C /* JSONDecodable.swift */; };
13+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77A1D5B75600021630C /* JSONEncodable.swift */; };
14+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */; };
15+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */; };
16+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77D1D5B75600021630C /* JSONParser.swift */; };
17+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77E1D5B75600021630C /* JSONParsing.swift */; };
18+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77F1D5B75600021630C /* JSONSerializing.swift */; };
19+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7801D5B75600021630C /* JSONSubscripting.swift */; };
20+/* End PBXBuildFile section */
21+
22+/* Begin PBXCopyFilesBuildPhase section */
23+ 03D84AB51D5B0A1000776402 /* CopyFiles */ = {
24+ isa = PBXCopyFilesBuildPhase;
25+ buildActionMask = 2147483647;
26+ dstPath = /usr/share/man/man1;
27+ dstSubfolderSpec = 0;
28+ files = (
29+ );
30+ runOnlyForDeploymentPostprocessing = 1;
31+ };
32+/* End PBXCopyFilesBuildPhase section */
33+
34+/* Begin PBXFileReference section */
35+ 03D84AB71D5B0A1000776402 /* test_Freddy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test_Freddy; sourceTree = BUILT_PRODUCTS_DIR; };
36+ 03D84ABA1D5B0A1000776402 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
37+ 03F8F7781D5B75600021630C /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSON.swift; path = test_Freddy/JSON.swift; sourceTree = "<group>"; };
38+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONDecodable.swift; path = test_Freddy/JSONDecodable.swift; sourceTree = "<group>"; };
39+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodable.swift; path = test_Freddy/JSONEncodable.swift; sourceTree = "<group>"; };
40+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodingDetector.swift; path = test_Freddy/JSONEncodingDetector.swift; sourceTree = "<group>"; };
41+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONLiteralConvertible.swift; path = test_Freddy/JSONLiteralConvertible.swift; sourceTree = "<group>"; };
42+ 03F8F77D1D5B75600021630C /* JSONParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParser.swift; path = test_Freddy/JSONParser.swift; sourceTree = "<group>"; };
43+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParsing.swift; path = test_Freddy/JSONParsing.swift; sourceTree = "<group>"; };
44+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSerializing.swift; path = test_Freddy/JSONSerializing.swift; sourceTree = "<group>"; };
45+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSubscripting.swift; path = test_Freddy/JSONSubscripting.swift; sourceTree = "<group>"; };
46+/* End PBXFileReference section */
47+
48+/* Begin PBXFrameworksBuildPhase section */
49+ 03D84AB41D5B0A1000776402 /* Frameworks */ = {
50+ isa = PBXFrameworksBuildPhase;
51+ buildActionMask = 2147483647;
52+ files = (
53+ );
54+ runOnlyForDeploymentPostprocessing = 0;
55+ };
56+/* End PBXFrameworksBuildPhase section */
57+
58+/* Begin PBXGroup section */
59+ 03D84AAE1D5B0A1000776402 = {
60+ isa = PBXGroup;
61+ children = (
62+ 03F8F7781D5B75600021630C /* JSON.swift */,
63+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */,
64+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */,
65+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */,
66+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */,
67+ 03F8F77D1D5B75600021630C /* JSONParser.swift */,
68+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */,
69+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */,
70+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */,
71+ 03D84AB91D5B0A1000776402 /* test_Freddy */,
72+ 03D84AB81D5B0A1000776402 /* Products */,
73+ );
74+ sourceTree = "<group>";
75+ };
76+ 03D84AB81D5B0A1000776402 /* Products */ = {
77+ isa = PBXGroup;
78+ children = (
79+ 03D84AB71D5B0A1000776402 /* test_Freddy */,
80+ );
81+ name = Products;
82+ sourceTree = "<group>";
83+ };
84+ 03D84AB91D5B0A1000776402 /* test_Freddy */ = {
85+ isa = PBXGroup;
86+ children = (
87+ 03D84ABA1D5B0A1000776402 /* main.swift */,
88+ );
89+ path = test_Freddy;
90+ sourceTree = "<group>";
91+ };
92+/* End PBXGroup section */
93+
94+/* Begin PBXNativeTarget section */
95+ 03D84AB61D5B0A1000776402 /* test_Freddy */ = {
96+ isa = PBXNativeTarget;
97+ buildConfigurationList = 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */;
98+ buildPhases = (
99+ 03D84AB31D5B0A1000776402 /* Sources */,
100+ 03D84AB41D5B0A1000776402 /* Frameworks */,
101+ 03D84AB51D5B0A1000776402 /* CopyFiles */,
102+ );
103+ buildRules = (
104+ );
105+ dependencies = (
106+ );
107+ name = test_Freddy;
108+ productName = test_Freddy;
109+ productReference = 03D84AB71D5B0A1000776402 /* test_Freddy */;
110+ productType = "com.apple.product-type.tool";
111+ };
112+/* End PBXNativeTarget section */
113+
114+/* Begin PBXProject section */
115+ 03D84AAF1D5B0A1000776402 /* Project object */ = {
116+ isa = PBXProject;
117+ attributes = {
118+ LastSwiftUpdateCheck = 0800;
119+ LastUpgradeCheck = 0800;
120+ ORGANIZATIONNAME = "Nicolas Seriot";
121+ TargetAttributes = {
122+ 03D84AB61D5B0A1000776402 = {
123+ CreatedOnToolsVersion = 8.0;
124+ DevelopmentTeam = VBYRKYS73S;
125+ DevelopmentTeamName = "Nicolas Seriot";
126+ ProvisioningStyle = Automatic;
127+ };
128+ };
129+ };
130+ buildConfigurationList = 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */;
131+ compatibilityVersion = "Xcode 3.2";
132+ developmentRegion = English;
133+ hasScannedForEncodings = 0;
134+ knownRegions = (
135+ en,
136+ );
137+ mainGroup = 03D84AAE1D5B0A1000776402;
138+ productRefGroup = 03D84AB81D5B0A1000776402 /* Products */;
139+ projectDirPath = "";
140+ projectRoot = "";
141+ targets = (
142+ 03D84AB61D5B0A1000776402 /* test_Freddy */,
143+ );
144+ };
145+/* End PBXProject section */
146+
147+/* Begin PBXSourcesBuildPhase section */
148+ 03D84AB31D5B0A1000776402 /* Sources */ = {
149+ isa = PBXSourcesBuildPhase;
150+ buildActionMask = 2147483647;
151+ files = (
152+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */,
153+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */,
154+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */,
155+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */,
156+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */,
157+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */,
158+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */,
159+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */,
160+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */,
161+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */,
162+ );
163+ runOnlyForDeploymentPostprocessing = 0;
164+ };
165+/* End PBXSourcesBuildPhase section */
166+
167+/* Begin XCBuildConfiguration section */
168+ 03D84ABC1D5B0A1000776402 /* Debug */ = {
169+ isa = XCBuildConfiguration;
170+ buildSettings = {
171+ ALWAYS_SEARCH_USER_PATHS = NO;
172+ CLANG_ANALYZER_NONNULL = YES;
173+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
174+ CLANG_CXX_LIBRARY = "libc++";
175+ CLANG_ENABLE_MODULES = YES;
176+ CLANG_ENABLE_OBJC_ARC = YES;
177+ CLANG_WARN_BOOL_CONVERSION = YES;
178+ CLANG_WARN_CONSTANT_CONVERSION = YES;
179+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181+ CLANG_WARN_EMPTY_BODY = YES;
182+ CLANG_WARN_ENUM_CONVERSION = YES;
183+ CLANG_WARN_INT_CONVERSION = YES;
184+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
185+ CLANG_WARN_UNREACHABLE_CODE = YES;
186+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
187+ CODE_SIGN_IDENTITY = "-";
188+ COPY_PHASE_STRIP = NO;
189+ DEBUG_INFORMATION_FORMAT = dwarf;
190+ ENABLE_STRICT_OBJC_MSGSEND = YES;
191+ ENABLE_TESTABILITY = YES;
192+ GCC_C_LANGUAGE_STANDARD = gnu99;
193+ GCC_DYNAMIC_NO_PIC = NO;
194+ GCC_NO_COMMON_BLOCKS = YES;
195+ GCC_OPTIMIZATION_LEVEL = 0;
196+ GCC_PREPROCESSOR_DEFINITIONS = (
197+ "DEBUG=1",
198+ "$(inherited)",
199+ );
200+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
201+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
202+ GCC_WARN_UNDECLARED_SELECTOR = YES;
203+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
204+ GCC_WARN_UNUSED_FUNCTION = YES;
205+ GCC_WARN_UNUSED_VARIABLE = YES;
206+ MACOSX_DEPLOYMENT_TARGET = 10.11;
207+ MTL_ENABLE_DEBUG_INFO = YES;
208+ ONLY_ACTIVE_ARCH = YES;
209+ SDKROOT = macosx;
210+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
211+ };
212+ name = Debug;
213+ };
214+ 03D84ABD1D5B0A1000776402 /* Release */ = {
215+ isa = XCBuildConfiguration;
216+ buildSettings = {
217+ ALWAYS_SEARCH_USER_PATHS = NO;
218+ CLANG_ANALYZER_NONNULL = YES;
219+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
220+ CLANG_CXX_LIBRARY = "libc++";
221+ CLANG_ENABLE_MODULES = YES;
222+ CLANG_ENABLE_OBJC_ARC = YES;
223+ CLANG_WARN_BOOL_CONVERSION = YES;
224+ CLANG_WARN_CONSTANT_CONVERSION = YES;
225+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
226+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
227+ CLANG_WARN_EMPTY_BODY = YES;
228+ CLANG_WARN_ENUM_CONVERSION = YES;
229+ CLANG_WARN_INT_CONVERSION = YES;
230+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
231+ CLANG_WARN_UNREACHABLE_CODE = YES;
232+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
233+ CODE_SIGN_IDENTITY = "-";
234+ COPY_PHASE_STRIP = NO;
235+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
236+ ENABLE_NS_ASSERTIONS = NO;
237+ ENABLE_STRICT_OBJC_MSGSEND = YES;
238+ GCC_C_LANGUAGE_STANDARD = gnu99;
239+ GCC_NO_COMMON_BLOCKS = YES;
240+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
241+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
242+ GCC_WARN_UNDECLARED_SELECTOR = YES;
243+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
244+ GCC_WARN_UNUSED_FUNCTION = YES;
245+ GCC_WARN_UNUSED_VARIABLE = YES;
246+ MACOSX_DEPLOYMENT_TARGET = 10.11;
247+ MTL_ENABLE_DEBUG_INFO = NO;
248+ SDKROOT = macosx;
249+ };
250+ name = Release;
251+ };
252+ 03D84ABF1D5B0A1000776402 /* Debug */ = {
253+ isa = XCBuildConfiguration;
254+ buildSettings = {
255+ FRAMEWORK_SEARCH_PATHS = (
256+ "$(inherited)",
257+ "$(PROJECT_DIR)",
258+ );
259+ PRODUCT_NAME = "$(TARGET_NAME)";
260+ SWIFT_VERSION = 2.3;
261+ };
262+ name = Debug;
263+ };
264+ 03D84AC01D5B0A1000776402 /* Release */ = {
265+ isa = XCBuildConfiguration;
266+ buildSettings = {
267+ FRAMEWORK_SEARCH_PATHS = (
268+ "$(inherited)",
269+ "$(PROJECT_DIR)",
270+ );
271+ PRODUCT_NAME = "$(TARGET_NAME)";
272+ SWIFT_VERSION = 2.3;
273+ };
274+ name = Release;
275+ };
276+/* End XCBuildConfiguration section */
277+
278+/* Begin XCConfigurationList section */
279+ 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */ = {
280+ isa = XCConfigurationList;
281+ buildConfigurations = (
282+ 03D84ABC1D5B0A1000776402 /* Debug */,
283+ 03D84ABD1D5B0A1000776402 /* Release */,
284+ );
285+ defaultConfigurationIsVisible = 0;
286+ defaultConfigurationName = Release;
287+ };
288+ 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */ = {
289+ isa = XCConfigurationList;
290+ buildConfigurations = (
291+ 03D84ABF1D5B0A1000776402 /* Debug */,
292+ 03D84AC01D5B0A1000776402 /* Release */,
293+ );
294+ defaultConfigurationIsVisible = 0;
295+ defaultConfigurationName = Release;
296+ };
297+/* End XCConfigurationList section */
298+ };
299+ rootObject = 03D84AAF1D5B0A1000776402 /* Project object */;
300+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:test_Freddy.xcodeproj">
6+ </FileRef>
7+</Workspace>
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/project.xcworkspace/xcshareddata/test_Freddy.xcscmblueprintnew file+30
@@ -0,0 +1,30 @@
1+{
2+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A",
3+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4+
5+ },
6+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : 0,
8+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : 0
9+ },
10+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "7B3DFA55-F9BF-41D8-8FD5-204D3162EB03",
11+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : "dropbox\/JSON\/test_Freddy\/Vendor\/Freddy\/",
13+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : "dropbox\/"
14+ },
15+ "DVTSourceControlWorkspaceBlueprintNameKey" : "test_Freddy",
16+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "JSON\/test_Freddy\/test_Freddy.xcodeproj",
18+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19+ {
20+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/bignerdranch\/Freddy.git",
21+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6ADA245F937D8ED6D99F5A7865E19F5267A169E2"
23+ },
24+ {
25+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "\/Users\/nst\/Dropbox\/dropbox.git",
26+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A"
28+ }
29+ ]
30+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_Freddy.xcschemenew file+91
@@ -0,0 +1,91 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "0800"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
18+ BuildableName = "test_Freddy"
19+ BlueprintName = "test_Freddy"
20+ ReferencedContainer = "container:test_Freddy.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
36+ BuildableName = "test_Freddy"
37+ BlueprintName = "test_Freddy"
38+ ReferencedContainer = "container:test_Freddy.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
59+ BuildableName = "test_Freddy"
60+ BlueprintName = "test_Freddy"
61+ ReferencedContainer = "container:test_Freddy.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ </AdditionalOptions>
66+ </LaunchAction>
67+ <ProfileAction
68+ buildConfiguration = "Release"
69+ shouldUseLaunchSchemeArgsEnv = "YES"
70+ savedToolIdentifier = ""
71+ useCustomWorkingDirectory = "NO"
72+ debugDocumentVersioning = "YES">
73+ <BuildableProductRunnable
74+ runnableDebuggingMode = "0">
75+ <BuildableReference
76+ BuildableIdentifier = "primary"
77+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
78+ BuildableName = "test_Freddy"
79+ BlueprintName = "test_Freddy"
80+ ReferencedContainer = "container:test_Freddy.xcodeproj">
81+ </BuildableReference>
82+ </BuildableProductRunnable>
83+ </ProfileAction>
84+ <AnalyzeAction
85+ buildConfiguration = "Debug">
86+ </AnalyzeAction>
87+ <ArchiveAction
88+ buildConfiguration = "Release"
89+ revealArchiveInOrganizer = "YES">
90+ </ArchiveAction>
91+</Scheme>
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22
@@ -0,0 +1,22 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>SchemeUserState</key>
6+ <dict>
7+ <key>test_Freddy.xcscheme</key>
8+ <dict>
9+ <key>orderHint</key>
10+ <integer>0</integer>
11+ </dict>
12+ </dict>
13+ <key>SuppressBuildableAutocreation</key>
14+ <dict>
15+ <key>03D84AB61D5B0A1000776402</key>
16+ <dict>
17+ <key>primary</key>
18+ <true/>
19+ </dict>
20+ </dict>
21+</dict>
22+</plist>
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSON.swiftnew file+95
@@ -0,0 +1,95 @@
1+//
2+// JSON.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/17/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// An enum to describe the structure of JSON.
10+public enum JSON {
11+ /// A case for denoting an array with an associated value of `[JSON]`
12+ case Array([JSON])
13+ /// A case for denoting a dictionary with an associated value of `[Swift.String: JSON]`
14+ case Dictionary([Swift.String: JSON])
15+ /// A case for denoting a double with an associated value of `Swift.Double`.
16+ case Double(Swift.Double)
17+ /// A case for denoting an integer with an associated value of `Swift.Int`.
18+ case Int(Swift.Int)
19+ /// A case for denoting a string with an associated value of `Swift.String`.
20+ case String(Swift.String)
21+ /// A case for denoting a boolean with an associated value of `Swift.Bool`.
22+ case Bool(Swift.Bool)
23+ /// A case for denoting null.
24+ case Null
25+}
26+
27+// MARK: - Errors
28+
29+extension JSON {
30+
31+ /// An enum to encapsulate errors that may arise in working with `JSON`.
32+ public enum Error: ErrorType {
33+ /// The `index` is out of bounds for a JSON array
34+ case IndexOutOfBounds(index: Swift.Int)
35+
36+ /// The `key` was not found in the JSON dictionary
37+ case KeyNotFound(key: Swift.String)
38+
39+ /// The JSON is not subscriptable with `type`
40+ case UnexpectedSubscript(type: JSONPathType.Type)
41+
42+ /// Unexpected JSON `value` was found that is not convertible `to` type
43+ case ValueNotConvertible(value: JSON, to: Any.Type)
44+ }
45+
46+}
47+
48+// MARK: - Test Equality
49+
50+/// Return `true` if `lhs` is equal to `rhs`.
51+public func ==(lhs: JSON, rhs: JSON) -> Bool {
52+ switch (lhs, rhs) {
53+ case (.Array(let arrL), .Array(let arrR)):
54+ return arrL == arrR
55+ case (.Dictionary(let dictL), .Dictionary(let dictR)):
56+ return dictL == dictR
57+ case (.String(let strL), .String(let strR)):
58+ return strL == strR
59+ case (.Double(let dubL), .Double(let dubR)):
60+ return dubL == dubR
61+ case (.Double(let dubL), .Int(let intR)):
62+ return dubL == Double(intR)
63+ case (.Int(let intL), .Int(let intR)):
64+ return intL == intR
65+ case (.Int(let intL), .Double(let dubR)):
66+ return Double(intL) == dubR
67+ case (.Bool(let bL), .Bool(let bR)):
68+ return bL == bR
69+ case (.Null, .Null):
70+ return true
71+ default:
72+ return false
73+ }
74+}
75+
76+extension JSON: Equatable {}
77+
78+// MARK: - Printing
79+
80+extension JSON: CustomStringConvertible {
81+
82+ /// A textual representation of `self`.
83+ public var description: Swift.String {
84+ switch self {
85+ case .Array(let arr): return Swift.String(arr)
86+ case .Dictionary(let dict): return Swift.String(dict)
87+ case .String(let string): return string
88+ case .Double(let double): return Swift.String(double)
89+ case .Int(let int): return Swift.String(int)
90+ case .Bool(let bool): return Swift.String(bool)
91+ case .Null: return "null"
92+ }
93+ }
94+
95+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONDecodable.swiftnew file+177
@@ -0,0 +1,177 @@
1+//
2+// JSONDecodable.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/24/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// A protocol to provide functionality for creating a model object with a `JSON`
10+/// value.
11+public protocol JSONDecodable {
12+
13+ /// Creates an instance of the model with a `JSON` instance.
14+ /// - parameter json: An instance of a `JSON` value from which to
15+ /// construct an instance of the implementing type.
16+ /// - throws: Any `JSON.Error` for errors derived from inspecting the
17+ /// `JSON` value, or any other error involved in decoding.
18+ init(json: JSON) throws
19+
20+}
21+
22+extension Double: JSONDecodable {
23+
24+ /// An initializer to create an instance of `Double` from a `JSON` value.
25+ /// - parameter json: An instance of `JSON`.
26+ /// - throws: The initializer will throw an instance of `JSON.Error` if
27+ /// an instance of `Double` cannot be created from the `JSON` value that was
28+ /// passed to this initializer.
29+ public init(json: JSON) throws {
30+ switch json {
31+ case let .Double(double):
32+ self = double
33+ case let .Int(int):
34+ self = Swift.Double(int)
35+ default:
36+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Double)
37+ }
38+ }
39+
40+}
41+
42+extension Int: JSONDecodable {
43+
44+ /// An initializer to create an instance of `Int` from a `JSON` value.
45+ /// - parameter json: An instance of `JSON`.
46+ /// - throws: The initializer will throw an instance of `JSON.Error` if
47+ /// an instance of `Int` cannot be created from the `JSON` value that was
48+ /// passed to this initializer.
49+ public init(json: JSON) throws {
50+ switch json {
51+ case let .Double(double) where double <= Double(Swift.Int.max):
52+ self = Swift.Int(double)
53+ case let .Int(int):
54+ self = int
55+ default:
56+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Int)
57+ }
58+ }
59+
60+}
61+
62+extension String: JSONDecodable {
63+
64+ /// An initializer to create an instance of `String` from a `JSON` value.
65+ /// - parameter json: An instance of `JSON`.
66+ /// - throws: The initializer will throw an instance of `JSON.Error` if
67+ /// an instance of `String` cannot be created from the `JSON` value that was
68+ /// passed to this initializer.
69+ public init(json: JSON) throws {
70+
71+ switch json {
72+ case let .String(string):
73+ self = string
74+ case let .Int(int):
75+ self = String(int)
76+ case let .Bool(bool):
77+ self = String(bool)
78+ case let .Double(double):
79+ self = String(double)
80+ default:
81+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.String)
82+ }
83+ }
84+
85+}
86+
87+extension Bool: JSONDecodable {
88+
89+ /// An initializer to create an instance of `Bool` from a `JSON` value.
90+ /// - parameter json: An instance of `JSON`.
91+ /// - throws: The initializer will throw an instance of `JSON.Error` if
92+ /// an instance of `Bool` cannot be created from the `JSON` value that was
93+ /// passed to this initializer.
94+ public init(json: JSON) throws {
95+ guard case let .Bool(bool) = json else {
96+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Bool)
97+ }
98+ self = bool
99+ }
100+
101+}
102+
103+extension RawRepresentable where RawValue: JSONDecodable {
104+
105+ /// An initializer to create an instance of `RawRepresentable` from a `JSON` value.
106+ /// - parameter json: An instance of `JSON`.
107+ /// - throws: The initializer will throw an instance of `JSON.Error` if
108+ /// an instance of `RawRepresentable` cannot be created from the `JSON` value that was
109+ /// passed to this initializer.
110+ public init(json: JSON) throws {
111+ let raw = try json.decode(type: RawValue.self)
112+ guard let value = Self(rawValue: raw) else {
113+ throw JSON.Error.ValueNotConvertible(value: json, to: Self.self)
114+ }
115+ self = value
116+ }
117+}
118+
119+internal extension JSON {
120+
121+ /// Retrieves a `[JSON]` from the JSON.
122+ /// - parameter: A `JSON` to be used to create the returned `Array`.
123+ /// - returns: An `Array` of `JSON` elements
124+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
125+ /// - seealso: `JSON.decode(_:type:)`
126+ static func getArray(json: JSON) throws -> [JSON] {
127+ // Ideally should be expressed as a conditional protocol implementation on Swift.Array.
128+ guard case let .Array(array) = json else {
129+ throw Error.ValueNotConvertible(value: json, to: Swift.Array<JSON>)
130+ }
131+ return array
132+ }
133+
134+ /// Retrieves a `[String: JSON]` from the JSON.
135+ /// - parameter: A `JSON` to be used to create the returned `Dictionary`.
136+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
137+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
138+ /// - seealso: `JSON.decode(_:type:)`
139+ static func getDictionary(json: JSON) throws -> [Swift.String: JSON] {
140+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
141+ guard case let .Dictionary(dictionary) = json else {
142+ throw Error.ValueNotConvertible(value: json, to: Swift.Dictionary<Swift.String, JSON>)
143+ }
144+ return dictionary
145+ }
146+
147+ /// Attempts to decode many values from a descendant JSON array at a path
148+ /// into JSON.
149+ /// - parameter: A `JSON` to be used to create the returned `Array` of some type conforming to `JSONDecodable`.
150+ /// - returns: An `Array` of `Decoded` elements.
151+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`, as
152+ /// well as any error that arises from decoding the contained values.
153+ /// - seealso: `JSON.decode(_:type:)`
154+ static func getArrayOf<Decoded: JSONDecodable>(json: JSON) throws -> [Decoded] {
155+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
156+ // This implementation also doesn't do the `type = Type.self` trick.
157+ return try getArray(json).map(Decoded.init)
158+ }
159+
160+ /// Attempts to decode many values from a descendant JSON object at a path
161+ /// into JSON.
162+ /// - returns: A `Dictionary` of string keys and `Decoded` values.
163+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)` or
164+ /// any error that arises from decoding the contained values.
165+ /// - seealso: `JSON.decode(_:type:)`
166+ static func getDictionaryOf<Decoded: JSONDecodable>(json: JSON) throws -> [Swift.String: Decoded] {
167+ guard case let .Dictionary(dictionary) = json else {
168+ throw Error.ValueNotConvertible(value: json, to: Swift.Dictionary<Swift.String, Decoded>)
169+ }
170+ var decodedDictionary = Swift.Dictionary<Swift.String, Decoded>(minimumCapacity: dictionary.count)
171+ for (key, value) in dictionary {
172+ decodedDictionary[key] = try Decoded(json: value)
173+ }
174+ return decodedDictionary
175+ }
176+
177+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONEncodable.swiftnew file+83
@@ -0,0 +1,83 @@
1+//
2+// JSONEncodable.swift
3+// Freddy
4+//
5+// Created by Matthew Mathias on 1/4/16.
6+// Copyright © 2016 Big Nerd Ranch. All rights reserved.
7+//
8+
9+import Foundation
10+
11+/// A protocol to facilitate encoding and decoding of `JSON`.
12+public protocol JSONEncodable {
13+ /// Converts an instance of a conforming type to `JSON`.
14+ /// - returns: An instance of `JSON`.
15+ /// - Note: If conforming to `JSONEncodable` with a custom type of your own, you should return an instance of
16+ /// `JSON.Dictionary`.
17+ func toJSON() -> JSON
18+}
19+
20+extension Array where Element: JSONEncodable {
21+ /// Converts an instance of `Array` whose elements conform to `JSONEncodable` to `JSON`.
22+ /// - returns: An instance of `JSON` where the enum case is `.Array`.
23+ public func toJSON() -> JSON {
24+ let arrayOfJSON = self.map { $0.toJSON() }
25+ return .Array(arrayOfJSON)
26+ }
27+}
28+
29+extension Dictionary where Value: JSONEncodable {
30+ /// Converts an instance of `Dictionary` whose values conform to `JSONEncodable` to `JSON`. The keys in the resulting
31+ /// `JSON.Dictionary` will be of type `String`.
32+ /// - returns: An instance of `JSON` where the enum case is `.Dictionary`.
33+ public func toJSON() -> JSON {
34+ var jsonDictionary = [String: JSON]()
35+
36+ for (k, v) in self {
37+ let key = String(k)
38+ jsonDictionary[key] = v.toJSON()
39+ }
40+
41+ return .Dictionary(jsonDictionary)
42+ }
43+}
44+
45+extension Int: JSONEncodable {
46+ /// Converts an instance of a conforming type to `JSON`.
47+ /// - returns: An instance of `JSON` where the enum case is `.Int`.
48+ public func toJSON() -> JSON {
49+ return .Int(self)
50+ }
51+}
52+
53+extension Double: JSONEncodable {
54+ /// Converts an instance of a conforming type to `JSON`.
55+ /// - returns: An instance of `JSON` where the enum case is `.Double`.
56+ public func toJSON() -> JSON {
57+ return .Double(self)
58+ }
59+}
60+
61+extension String: JSONEncodable {
62+ /// Converts an instance of a conforming type to `JSON`.
63+ /// - returns: An instance of `JSON` where the enum case is `.String`.
64+ public func toJSON() -> JSON {
65+ return .String(self)
66+ }
67+}
68+
69+extension Bool: JSONEncodable {
70+ /// Converts an instance of a conforming type to `JSON`.
71+ /// - returns: An instance of `JSON` where the enum case is `.Bool`.
72+ public func toJSON() -> JSON {
73+ return .Bool(self)
74+ }
75+}
76+
77+extension RawRepresentable where RawValue: JSONEncodable {
78+ /// Converts an instance of a conforming type to `JSON`.
79+ /// - returns: An instance of `JSON` where the enum case is whatever the underlying `RawValue` converts to.
80+ public func toJSON() -> JSON {
81+ return rawValue.toJSON()
82+ }
83+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONEncodingDetector.swiftnew file+110
@@ -0,0 +1,110 @@
1+//
2+// JSONEncodingDetector.swift
3+// Freddy
4+//
5+// Created by Robert Edwards on 1/27/16.
6+// Copyright © 2016 Big Nerd Ranch. All rights reserved.
7+//
8+
9+/// Struct for attempting to detect the Unicode encoding used with the data supplied to the JSONParser
10+public struct JSONEncodingDetector {
11+
12+ //// The Unicode encodings looked for during detection
13+ public enum Encoding {
14+ //// UTF-8
15+ case UTF8
16+ //// UTF-16 Little Endian
17+ case UTF16LE
18+ //// UTF-16 Big Endian
19+ case UTF16BE
20+ //// UTF-32 Little Endian
21+ case UTF32LE
22+ //// UTF-32 Big Endian
23+ case UTF32BE
24+ }
25+
26+ //// The Unicode encodings supported by JSONParser.swift
27+ public static let supportedEncodings: [Encoding] = [.UTF8]
28+
29+ typealias ByteStreamPrefixInformation = (encoding: Encoding, byteOrderMarkLength: Int)
30+
31+ //// Attempts to detect the Unicode encoding used for a given set of data.
32+ ////
33+ //// This function initially looks for a Byte Order Mark in the following form:
34+ ////
35+ //// Bytes | Encoding Form
36+ //// --------------|----------------
37+ //// 00 00 FE FF | UTF-32, big-endian
38+ //// FF FE 00 00 | UTF-32, little-endian
39+ //// FE FF | UTF-16, big-endian
40+ //// FF FE | UTF-16, little-endian
41+ //// EF BB BF | UTF-8
42+ ////
43+ //// If a BOM is not found then we detect using the following approach described in
44+ //// the JSON RFC http://www.ietf.org/rfc/rfc4627.txt:
45+ ////
46+ //// Since the first two characters of a JSON text will always be ASCII
47+ //// characters [RFC0020], it is possible to determine whether an octet
48+ //// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
49+ //// at the pattern of nulls in the first four octets.
50+ ////
51+ //// 00 00 00 xx UTF-32BE
52+ //// 00 xx 00 xx UTF-16BE
53+ //// xx 00 00 00 UTF-32LE
54+ //// xx 00 xx 00 UTF-16LE
55+ //// xx xx xx xx UTF-8
56+ ////
57+ //// - parameter header: The front Slice of data being read and evaluated.
58+ //// - returns: A tuple containing the detected Unicode encoding and the lenght of the byte order mark.
59+ static func detectEncoding(header: Slice<UnsafeBufferPointer<UInt8>>) -> ByteStreamPrefixInformation {
60+
61+ guard let prefix = prefixFromHeader(header) else {
62+ return (.UTF8, 0)
63+ }
64+
65+ if let prefixInfo = JSONEncodingDetector.encodingFromBOM(prefix) {
66+ return prefixInfo
67+ } else {
68+ switch prefix {
69+ case(0, 0, 0?, _):
70+ return (.UTF32BE, 0)
71+ case(_, 0, 0?, 0?):
72+ return (.UTF32LE, 0)
73+ case (0, _, 0?, _), (0, _, _, _):
74+ return (.UTF16BE, 0)
75+ case (_, 0, _, 0?), (_, 0, _, _):
76+ return (.UTF16LE, 0)
77+ default:
78+ return (.UTF8, 0)
79+ }
80+ }
81+ }
82+
83+ private typealias EncodingBytePrefix = (UInt8, UInt8, UInt8?, UInt8?)
84+
85+ private static func prefixFromHeader(header: Slice<UnsafeBufferPointer<UInt8>>) -> EncodingBytePrefix? {
86+ if header.count >= 4 {
87+ return(header[0], header[1], header[2], header[3])
88+ } else if header.count >= 2 {
89+ return (header[0], header[1], nil, nil)
90+ }
91+ return nil
92+ }
93+
94+ private static func encodingFromBOM(prefix: EncodingBytePrefix) -> ByteStreamPrefixInformation? {
95+ switch prefix {
96+ case(0xFE, 0xFF, _, _):
97+ return (.UTF16BE, 2)
98+ case(0x00, 0x00, 0xFE?, 0xFF?):
99+ return (.UTF32BE, 4)
100+ case(0xEF, 0xBB, 0xBF?, _):
101+ return (.UTF8, 3)
102+ case(0xFF, 0xFE, 0?, 0?):
103+ return (.UTF32LE, 4)
104+ case(0xFF, 0xFE, _, _):
105+ return (.UTF16LE, 2)
106+ default:
107+ return nil
108+ }
109+ }
110+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONLiteralConvertible.swiftnew file+130
@@ -0,0 +1,130 @@
1+//
2+// JSONLiteralConvertible.swift
3+// Freddy
4+//
5+// Created by Zachary Waldowski on 5/11/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+// MARK: - ArrayLiteralConvertible
10+
11+extension JSON: ArrayLiteralConvertible {
12+
13+ /// Create an instance by copying each element of the `collection` into a
14+ /// new `Array`.
15+ public init<Collection: CollectionType where Collection.Generator.Element == JSON>(_ collection: Collection) {
16+ self = .Array(Swift.Array(collection))
17+ }
18+
19+ /// Create an instance initialized with `elements`.
20+ public init(arrayLiteral elements: JSON...) {
21+ self.init(elements)
22+ }
23+
24+}
25+
26+// MARK: - DictionaryLiteralConvertible
27+
28+extension JSON: DictionaryLiteralConvertible {
29+
30+ /// Create an instance by copying each key/value pair of the `pairs` into
31+ /// a new `Dictionary`.
32+ public init<Dictionary: SequenceType where Dictionary.Generator.Element == (Swift.String, JSON)>(_ pairs: Dictionary) {
33+ var dictionary = Swift.Dictionary<Swift.String, JSON>(minimumCapacity: pairs.underestimateCount())
34+ for (key, value) in pairs {
35+ dictionary[key] = value
36+ }
37+ self = .Dictionary(dictionary)
38+ }
39+
40+ /// Create an instance initialized with `pairs`.
41+ public init(dictionaryLiteral pairs: (Swift.String, JSON)...) {
42+ self.init(pairs)
43+ }
44+
45+}
46+
47+// MARK: - FloatLiteralConvertible
48+
49+extension JSON: FloatLiteralConvertible {
50+
51+ /// Create an instance initialized to `Double` `value`.
52+ public init(_ value: Swift.Double) {
53+ self = .Double(value)
54+ }
55+
56+ /// Create a literal instance initialized to `value`.
57+ public init(floatLiteral value: Swift.Double) {
58+ self.init(value)
59+ }
60+
61+}
62+
63+// MARK: - IntegerLiteralConvertible
64+
65+extension JSON: IntegerLiteralConvertible {
66+
67+ /// Create an instance initialized to `Int` by `value`.
68+ public init(_ value: Swift.Int) {
69+ self = .Int(value)
70+ }
71+
72+ /// Create a literal instance initialized to `value`.
73+ public init(integerLiteral value: Swift.Int) {
74+ self.init(value)
75+ }
76+
77+}
78+
79+// MARK: - StringLiteralConvertible
80+
81+extension JSON: StringLiteralConvertible {
82+
83+ /// Create an instance initialized to `String` by `text`.
84+ public init(_ text: Swift.String) {
85+ self = .String(text)
86+ }
87+
88+ /// Create a literal instance initialized to `value`.
89+ public init(stringLiteral value: StringLiteralType) {
90+ self.init(value)
91+ }
92+
93+ /// Create a literal instance initialized to `value`.
94+ public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
95+ self.init(value)
96+ }
97+
98+ /// Create a literal instance initialized to `value`.
99+ public init(unicodeScalarLiteral value: StringLiteralType) {
100+ self.init(value)
101+ }
102+
103+}
104+
105+// MARK: - BooleanLiteralConvertible
106+
107+extension JSON: BooleanLiteralConvertible {
108+
109+ /// Create an instance initialized to `Bool` by `value`.
110+ public init(_ value: Swift.Bool) {
111+ self = .Bool(value)
112+ }
113+
114+ /// Create a literal instance initialized to `value`.
115+ public init(booleanLiteral value: Swift.Bool) {
116+ self.init(value)
117+ }
118+
119+}
120+
121+// MARK: - NilLiteralConvertible
122+
123+extension JSON: NilLiteralConvertible {
124+
125+ /// Create an instance initialized with `nil`.
126+ public init(nilLiteral: ()) {
127+ self = .Null
128+ }
129+
130+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONParser.swiftnew file+907
@@ -0,0 +1,907 @@
1+//
2+// JSONParser.swift
3+// Freddy
4+//
5+// Created by John Gallagher on 4/18/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+import Foundation
10+
11+private struct Literal {
12+ static let BACKSLASH = UInt8(ascii: "\\")
13+ static let BACKSPACE = UInt8(ascii: "\u{0008}")
14+ static let COLON = UInt8(ascii: ":")
15+ static let COMMA = UInt8(ascii: ",")
16+ static let DOUBLE_QUOTE = UInt8(ascii: "\"")
17+ static let FORMFEED = UInt8(ascii: "\u{000c}")
18+ static let LEFT_BRACE = UInt8(ascii: "{")
19+ static let LEFT_BRACKET = UInt8(ascii: "[")
20+ static let MINUS = UInt8(ascii: "-")
21+ static let NEWLINE = UInt8(ascii: "\n")
22+ static let PERIOD = UInt8(ascii: ".")
23+ static let PLUS = UInt8(ascii: "+")
24+ static let RETURN = UInt8(ascii: "\r")
25+ static let RIGHT_BRACE = UInt8(ascii: "}")
26+ static let RIGHT_BRACKET = UInt8(ascii: "]")
27+ static let SLASH = UInt8(ascii: "/")
28+ static let SPACE = UInt8(ascii: " ")
29+ static let TAB = UInt8(ascii: "\t")
30+
31+ static let a = UInt8(ascii: "a")
32+ static let b = UInt8(ascii: "b")
33+ static let c = UInt8(ascii: "c")
34+ static let d = UInt8(ascii: "d")
35+ static let e = UInt8(ascii: "e")
36+ static let f = UInt8(ascii: "f")
37+ static let l = UInt8(ascii: "l")
38+ static let n = UInt8(ascii: "n")
39+ static let r = UInt8(ascii: "r")
40+ static let s = UInt8(ascii: "s")
41+ static let t = UInt8(ascii: "t")
42+ static let u = UInt8(ascii: "u")
43+
44+ static let A = UInt8(ascii: "A")
45+ static let B = UInt8(ascii: "B")
46+ static let C = UInt8(ascii: "C")
47+ static let D = UInt8(ascii: "D")
48+ static let E = UInt8(ascii: "E")
49+ static let F = UInt8(ascii: "F")
50+
51+ static let zero = UInt8(ascii: "0")
52+ static let one = UInt8(ascii: "1")
53+ static let two = UInt8(ascii: "2")
54+ static let three = UInt8(ascii: "3")
55+ static let four = UInt8(ascii: "4")
56+ static let five = UInt8(ascii: "5")
57+ static let six = UInt8(ascii: "6")
58+ static let seven = UInt8(ascii: "7")
59+ static let eight = UInt8(ascii: "8")
60+ static let nine = UInt8(ascii: "9")
61+}
62+
63+private let ParserMaximumDepth = 512
64+
65+
66+/// A pure Swift JSON parser. This parser is much faster than the
67+/// `NSJSONSerialization`-based parser (due to the overhead of having to
68+/// dynamically cast the Objective-C objects to determine their type); however,
69+/// it is much newer and has restrictions that the `NSJSONSerialization` parser
70+/// does not. Two restrictions in particular are that it requires UTF-8 data as
71+/// input and it does not allow trailing commas in arrays or dictionaries.
72+public struct JSONParser {
73+
74+ private enum Sign: Int {
75+ case Positive = 1
76+ case Negative = -1
77+ }
78+
79+ private let input: UnsafeBufferPointer<UInt8>
80+ private let owner: Any?
81+ private var loc = 0
82+ private var depth = 0
83+
84+ private init<T>(buffer: UnsafeBufferPointer<UInt8>, owner: T) {
85+ self.input = buffer
86+ self.owner = owner
87+ }
88+
89+ /// Decode the root element of the `JSON` stream. This may be any fragment
90+ /// or a structural element, per RFC 7159.
91+ ///
92+ /// The beginning bytes are used to determine the stream's encoding.
93+ /// `JSONParser` currently only supports UTF-8 encoding, with or without
94+ /// a byte-order mark.
95+ ///
96+ /// - throws: `JSONParser.Error` for any decoding failures, including a
97+ /// source location if needed.
98+ public mutating func parse() throws -> JSON {
99+ try guardAgainstUnsupportedEncodings()
100+ let value = try parseValue()
101+ skipWhitespace()
102+ guard loc == input.count else {
103+ throw Error.EndOfStreamGarbage(offset: loc)
104+ }
105+ return value
106+ }
107+
108+ private mutating func parseValue() throws -> JSON {
109+ guard depth <= ParserMaximumDepth else {
110+ throw Error.ExceededNestingLimit(offset: loc)
111+ }
112+
113+ guard input.count > 0 else {
114+ throw Error.EndOfStreamUnexpected
115+ }
116+
117+ advancing: while loc < input.count {
118+ do {
119+ switch input[loc] {
120+ case Literal.LEFT_BRACKET:
121+ depth += 1
122+ defer { depth -= 1 }
123+ return try decodeArray()
124+
125+ case Literal.LEFT_BRACE:
126+ depth += 1
127+ defer { depth -= 1 }
128+ return try decodeObject()
129+
130+ case Literal.DOUBLE_QUOTE:
131+ return try decodeString()
132+
133+ case Literal.f:
134+ return try decodeFalse()
135+
136+ case Literal.n:
137+ return try decodeNull()
138+
139+ case Literal.t:
140+ return try decodeTrue()
141+
142+ case Literal.MINUS:
143+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .LeadingMinus))
144+
145+ case Literal.zero:
146+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .LeadingZero))
147+
148+ case Literal.one...Literal.nine:
149+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .PreDecimalDigits))
150+
151+ case Literal.SPACE, Literal.TAB, Literal.RETURN, Literal.NEWLINE:
152+ loc = loc.successor()
153+
154+ default:
155+ break advancing
156+ }
157+ } catch let InternalError.NumberOverflow(offset: start) {
158+ return try decodeNumberAsString(start)
159+ }
160+ }
161+
162+ if loc < input.count {
163+ throw Error.ValueInvalid(offset: loc, character: UnicodeScalar(input[loc]))
164+ } else {
165+ throw Error.EndOfStreamUnexpected
166+ }
167+ }
168+
169+ private mutating func skipWhitespace() {
170+ while loc < input.count {
171+ switch input[loc] {
172+ case Literal.SPACE, Literal.TAB, Literal.RETURN, Literal.NEWLINE:
173+ loc = loc.successor()
174+
175+ default:
176+ return
177+ }
178+ }
179+ }
180+
181+ private mutating func guardAgainstUnsupportedEncodings() throws {
182+ let header = input.prefix(4)
183+ let encodingPrefixInformation = JSONEncodingDetector.detectEncoding(header)
184+ guard JSONEncodingDetector.supportedEncodings.contains(encodingPrefixInformation.encoding) else {
185+ throw Error.InvalidUnicodeStreamEncoding(detectedEncoding: encodingPrefixInformation.encoding)
186+ }
187+ loc = loc.advancedBy(encodingPrefixInformation.byteOrderMarkLength)
188+ }
189+
190+ private mutating func decodeNull() throws -> JSON {
191+ guard loc.advancedBy(3, limit: input.count) != input.count else {
192+ throw Error.LiteralNilMisspelled(offset: loc)
193+ }
194+
195+ if input[loc+1] != Literal.u
196+ || input[loc+2] != Literal.l
197+ || input[loc+3] != Literal.l {
198+ throw Error.LiteralNilMisspelled(offset: loc)
199+ }
200+
201+ loc += 4
202+ return .Null
203+ }
204+
205+ private mutating func decodeTrue() throws -> JSON {
206+ guard loc.advancedBy(3, limit: input.count) != input.count else {
207+ throw Error.LiteralTrueMisspelled(offset: loc)
208+ }
209+
210+ if input[loc+1] != Literal.r
211+ || input[loc+2] != Literal.u
212+ || input[loc+3] != Literal.e {
213+ throw Error.LiteralTrueMisspelled(offset: loc)
214+ }
215+
216+ loc += 4
217+ return .Bool(true)
218+ }
219+
220+ private mutating func decodeFalse() throws -> JSON {
221+ guard loc.advancedBy(4, limit: input.count) != input.count else {
222+ throw Error.LiteralFalseMisspelled(offset: loc)
223+ }
224+
225+ if input[loc+1] != Literal.a
226+ || input[loc+2] != Literal.l
227+ || input[loc+3] != Literal.s
228+ || input[loc+4] != Literal.e {
229+ throw Error.LiteralFalseMisspelled(offset: loc)
230+ }
231+
232+ loc += 5
233+ return .Bool(false)
234+ }
235+
236+ private var stringDecodingBuffer = [UInt8]()
237+ private mutating func decodeString() throws -> JSON {
238+ let start = loc
239+ loc = loc.successor()
240+ stringDecodingBuffer.removeAll(keepCapacity: true)
241+ while loc < input.count {
242+ switch input[loc] {
243+ case Literal.BACKSLASH:
244+ loc = loc.successor()
245+ switch input[loc] {
246+ case Literal.DOUBLE_QUOTE: stringDecodingBuffer.append(Literal.DOUBLE_QUOTE)
247+ case Literal.BACKSLASH: stringDecodingBuffer.append(Literal.BACKSLASH)
248+ case Literal.SLASH: stringDecodingBuffer.append(Literal.SLASH)
249+ case Literal.b: stringDecodingBuffer.append(Literal.BACKSPACE)
250+ case Literal.f: stringDecodingBuffer.append(Literal.FORMFEED)
251+ case Literal.r: stringDecodingBuffer.append(Literal.RETURN)
252+ case Literal.t: stringDecodingBuffer.append(Literal.TAB)
253+ case Literal.n: stringDecodingBuffer.append(Literal.NEWLINE)
254+ case Literal.u:
255+ loc = loc.successor()
256+ try readUnicodeEscape(start: loc - 2)
257+
258+ // readUnicodeEscape() advances loc on its own, so we'll `continue` now
259+ // to skip the typical "advance past this character" for all the other escapes
260+ continue
261+
262+ default:
263+ throw Error.ControlCharacterUnrecognized(offset: loc)
264+ }
265+ loc = loc.successor()
266+
267+ case Literal.DOUBLE_QUOTE:
268+ loc = loc.successor()
269+ stringDecodingBuffer.append(0)
270+
271+ guard let string = (stringDecodingBuffer.withUnsafeBufferPointer {
272+ String.fromCString(UnsafePointer($0.baseAddress))
273+ }) else {
274+ throw Error.UnicodeEscapeInvalid(offset: start)
275+ }
276+
277+ return .String(string)
278+
279+ case let other:
280+ stringDecodingBuffer.append(other)
281+ loc = loc.successor()
282+ }
283+ }
284+
285+ throw Error.EndOfStreamUnexpected
286+ }
287+
288+ private mutating func readCodeUnit() -> UInt16? {
289+ guard loc + 4 <= input.count else {
290+ return nil
291+ }
292+ var codeUnit: UInt16 = 0
293+ for c in input[loc..<loc+4] {
294+ let nibble: UInt16
295+
296+ switch c {
297+ case Literal.zero...Literal.nine:
298+ nibble = UInt16(c - Literal.zero)
299+
300+ case Literal.a...Literal.f:
301+ nibble = 10 + UInt16(c - Literal.a)
302+
303+ case Literal.A...Literal.F:
304+ nibble = 10 + UInt16(c - Literal.A)
305+
306+ default:
307+ return nil
308+ }
309+ codeUnit = (codeUnit << 4) | nibble
310+ }
311+ loc += 4
312+ return codeUnit
313+ }
314+
315+ private mutating func readUnicodeEscape(start start: Int) throws {
316+ guard let codeUnit = readCodeUnit() else {
317+ throw Error.UnicodeEscapeInvalid(offset: start)
318+ }
319+
320+ let codeUnits: [UInt16]
321+
322+ if UTF16.isLeadSurrogate(codeUnit) {
323+ // First half of a UTF16 surrogate pair - we must parse another code unit and combine them
324+
325+ // First confirm and skip over that we have another "\u"
326+ guard loc + 6 <= input.count && input[loc] == Literal.BACKSLASH && input[loc+1] == Literal.u else {
327+ throw Error.UnicodeEscapeInvalid(offset: start)
328+ }
329+ loc += 2
330+
331+ // Ensure the second code unit is valid for the surrogate pair
332+ guard let secondCodeUnit = readCodeUnit() where UTF16.isTrailSurrogate(secondCodeUnit) else {
333+ throw Error.UnicodeEscapeInvalid(offset: start)
334+ }
335+
336+ codeUnits = [codeUnit, secondCodeUnit]
337+ } else {
338+ codeUnits = [codeUnit]
339+ }
340+
341+ let transcodeHadError = transcode(UTF16.self, UTF8.self, codeUnits.generate(), { self.stringDecodingBuffer.append($0) }, stopOnError: true)
342+
343+ if transcodeHadError {
344+ throw Error.UnicodeEscapeInvalid(offset: start)
345+ }
346+ }
347+
348+ private mutating func decodeArray() throws -> JSON {
349+ let start = loc
350+ loc = loc.successor()
351+ var items = [JSON]()
352+
353+ while loc < input.count {
354+ skipWhitespace()
355+
356+ if loc < input.count && input[loc] == Literal.RIGHT_BRACKET {
357+ loc = loc.successor()
358+ return .Array(items)
359+ }
360+
361+ if !items.isEmpty {
362+ guard loc < input.count && input[loc] == Literal.COMMA else {
363+ throw Error.CollectionMissingSeparator(offset: start)
364+ }
365+ loc = loc.successor()
366+ }
367+
368+ items.append(try parseValue())
369+ }
370+
371+ throw Error.EndOfStreamUnexpected
372+ }
373+
374+ // Decoding objects can be recursive, so we have to keep more than one
375+ // buffer around for building up key/value pairs (to reduce allocations
376+ // when parsing large JSON documents).
377+ //
378+ // Rough estimate of the difference between this and using a fresh
379+ // [(String,JSON)] for the `pairs` variable in decodeObject() below is
380+ // about 12% on an iPhone 5.
381+ private struct DecodeObjectBuffers {
382+ var buffers = [[(String,JSON)]]()
383+
384+ mutating func getBuffer() -> [(String,JSON)] {
385+ if !buffers.isEmpty {
386+ var buffer = buffers.removeLast()
387+ buffer.removeAll(keepCapacity: true)
388+ return buffer
389+ }
390+ return [(String,JSON)]()
391+ }
392+
393+ mutating func putBuffer(buffer: [(String,JSON)]) {
394+ buffers.append(buffer)
395+ }
396+ }
397+
398+ private var decodeObjectBuffers = DecodeObjectBuffers()
399+
400+ private mutating func decodeObject() throws -> JSON {
401+ let start = loc
402+ loc = loc.successor()
403+ var pairs = decodeObjectBuffers.getBuffer()
404+
405+ while loc < input.count {
406+ skipWhitespace()
407+
408+ if loc < input.count && input[loc] == Literal.RIGHT_BRACE {
409+ loc = loc.successor()
410+ var obj = [String:JSON](minimumCapacity: pairs.count)
411+ for (k, v) in pairs {
412+ obj[k] = v
413+ }
414+ decodeObjectBuffers.putBuffer(pairs)
415+ return .Dictionary(obj)
416+ }
417+
418+ if !pairs.isEmpty {
419+ guard loc < input.count && input[loc] == Literal.COMMA else {
420+ throw Error.CollectionMissingSeparator(offset: start)
421+ }
422+ loc = loc.successor()
423+
424+ skipWhitespace()
425+ }
426+
427+ guard loc < input.count && input[loc] == Literal.DOUBLE_QUOTE else {
428+ throw Error.DictionaryMissingKey(offset: start)
429+ }
430+
431+ let key = try decodeString().string()
432+ skipWhitespace()
433+
434+ guard loc < input.count && input[loc] == Literal.COLON else {
435+ throw Error.CollectionMissingSeparator(offset: start)
436+ }
437+ loc = loc.successor()
438+
439+ pairs.append((key, try parseValue()))
440+ }
441+
442+ throw Error.EndOfStreamUnexpected
443+ }
444+
445+ private mutating func decodeIntegralValue(parser: NumberParser) throws -> JSON {
446+ var sign = Sign.Positive
447+ var parser = parser
448+ var value = 0
449+
450+ // This would be more natural as `while true { ... }` with a meaningful .Done case,
451+ // but that causes compile time explosion in Swift 2.2. :-|
452+ while parser.state != .Done {
453+ switch parser.state {
454+ case .LeadingMinus:
455+ sign = .Negative
456+ try parser.parseNegative()
457+
458+ case .LeadingZero:
459+ parser.parseLeadingZero()
460+
461+ case .PreDecimalDigits:
462+ try parser.parsePreDecimalDigits { c in
463+ guard case let (exponent, false) = Int.multiplyWithOverflow(10, value) else {
464+ throw InternalError.NumberOverflow(offset: parser.start)
465+ }
466+
467+ guard case let (newValue, false) = Int.addWithOverflow(exponent, Int(c - Literal.zero)) else {
468+ throw InternalError.NumberOverflow(offset: parser.start)
469+ }
470+
471+ value = newValue
472+ }
473+
474+ case .Decimal, .Exponent:
475+ return try detectingFloatingPointErrors(parser.start) {
476+ try decodeFloatingPointValue(parser, sign: sign, value: Double(value))
477+ }
478+
479+ case .PostDecimalDigits, .ExponentSign, .ExponentDigits:
480+ assertionFailure("Invalid internal state while parsing number")
481+
482+ case .Done:
483+ fatalError("impossible condition")
484+ }
485+ }
486+
487+ guard case let (signedValue, false) = Int.multiplyWithOverflow(sign.rawValue, value) else {
488+ throw InternalError.NumberOverflow(offset: parser.start)
489+ }
490+
491+ loc = parser.loc
492+ return .Int(signedValue)
493+ }
494+
495+ private mutating func decodeFloatingPointValue(parser: NumberParser, sign: Sign, value: Double) throws -> JSON {
496+ var parser = parser
497+ var value = value
498+ var exponentSign = Sign.Positive
499+ var exponent = Double(0)
500+ var position = 0.1
501+
502+ // This would be more natural as `while true { ... }` with a meaningful .Done case,
503+ // but that causes compile time explosion in Swift 2.2. :-|
504+ while parser.state != .Done {
505+ switch parser.state {
506+ case .LeadingMinus, .LeadingZero, .PreDecimalDigits:
507+ assertionFailure("Invalid internal state while parsing number")
508+
509+ case .Decimal:
510+ try parser.parseDecimal()
511+
512+ case .PostDecimalDigits:
513+ parser.parsePostDecimalDigits { c in
514+ value += position * Double(c - Literal.zero)
515+ position /= 10
516+ }
517+
518+ case .Exponent:
519+ exponentSign = try parser.parseExponent()
520+
521+ case .ExponentSign:
522+ try parser.parseExponentSign()
523+
524+ case .ExponentDigits:
525+ parser.parseExponentDigits { c in
526+ exponent = exponent * 10 + Double(c - Literal.zero)
527+ }
528+
529+ case .Done:
530+ fatalError("impossible condition")
531+ }
532+ }
533+
534+ loc = parser.loc
535+ return .Double(Double(sign.rawValue) * value * pow(10, Double(exponentSign.rawValue) * exponent))
536+ }
537+
538+ private mutating func decodeNumberAsString(start: Int) throws -> JSON {
539+ var parser: NumberParser = {
540+ let state: NumberParser.State
541+ switch input[start] {
542+ case Literal.MINUS: state = .LeadingMinus
543+ case Literal.zero: state = .LeadingZero
544+ case Literal.one...Literal.nine: state = .PreDecimalDigits
545+ default:
546+ fatalError("Internal error: decodeNumber called on not-a-number")
547+ }
548+ return NumberParser(loc: start, input: input, state: state)
549+ }()
550+
551+ stringDecodingBuffer.removeAll(keepCapacity: true)
552+
553+ while true {
554+ switch parser.state {
555+ case .LeadingMinus:
556+ try parser.parseNegative()
557+ stringDecodingBuffer.append(Literal.MINUS)
558+
559+ case .LeadingZero:
560+ parser.parseLeadingZero()
561+ stringDecodingBuffer.append(Literal.zero)
562+
563+ case .PreDecimalDigits:
564+ parser.parsePreDecimalDigits { stringDecodingBuffer.append($0) }
565+
566+ case .Decimal:
567+ try parser.parseDecimal()
568+ stringDecodingBuffer.append(Literal.PERIOD)
569+
570+ case .PostDecimalDigits:
571+ parser.parsePostDecimalDigits { stringDecodingBuffer.append($0) }
572+
573+ case .Exponent:
574+ stringDecodingBuffer.append(input[parser.loc])
575+ _ = try parser.parseExponent()
576+
577+ case .ExponentSign:
578+ stringDecodingBuffer.append(input[parser.loc])
579+ try parser.parseExponentSign()
580+
581+ case .ExponentDigits:
582+ parser.parseExponentDigits { stringDecodingBuffer.append($0) }
583+
584+ case .Done:
585+ stringDecodingBuffer.append(0)
586+ guard let string = (stringDecodingBuffer.withUnsafeBufferPointer {
587+ String.fromCString(UnsafePointer($0.baseAddress))
588+ }) else {
589+ // Should never fail - any problems with the number string should
590+ // result in thrown errors above
591+ fatalError("Internal error: Invalid numeric string")
592+ }
593+
594+ loc = parser.loc
595+ return .String(string)
596+ }
597+ }
598+ }
599+
600+ private func detectingFloatingPointErrors<T>(loc: Int, @noescape _ f: () throws -> T) throws -> T {
601+ let flags = FE_UNDERFLOW | FE_OVERFLOW
602+ feclearexcept(flags)
603+ let value = try f()
604+ guard fetestexcept(flags) == 0 else {
605+ throw InternalError.NumberOverflow(offset: loc)
606+ }
607+ return value
608+ }
609+}
610+
611+private struct NumberParser {
612+ enum State {
613+ case LeadingMinus
614+ case LeadingZero
615+ case PreDecimalDigits
616+ case Decimal
617+ case PostDecimalDigits
618+ case Exponent
619+ case ExponentSign
620+ case ExponentDigits
621+ case Done
622+ }
623+
624+ let start: Int
625+ var loc = 0
626+ var state: State
627+ let input: UnsafeBufferPointer<UInt8>
628+
629+ init(loc: Int, input: UnsafeBufferPointer<UInt8>, state: State) {
630+ assert(loc < input.count, "Invalid input to NumberParser")
631+ self.start = loc
632+ self.loc = loc
633+ self.input = input
634+ self.state = state
635+ }
636+
637+ mutating func parseNegative() throws {
638+ assert(state == .LeadingMinus, "Unexpected state entering parseNegative")
639+
640+ loc = loc.successor()
641+ guard loc < input.count else {
642+ throw JSONParser.Error.EndOfStreamUnexpected
643+ }
644+
645+ switch input[loc] {
646+ case Literal.zero:
647+ state = .LeadingZero
648+
649+ case Literal.one...Literal.nine:
650+ state = .PreDecimalDigits
651+
652+ default:
653+ throw JSONParser.Error.NumberSymbolMissingDigits(offset: start)
654+ }
655+ }
656+
657+ mutating func parseLeadingZero() {
658+ assert(state == .LeadingZero, "Unexpected state entering parseLeadingZero")
659+
660+ loc = loc.successor()
661+ guard loc < input.count else {
662+ state = .Done
663+ return
664+ }
665+
666+ guard input[loc] == Literal.PERIOD else {
667+ state = .Done
668+ return
669+ }
670+
671+ state = .Decimal
672+ }
673+
674+ mutating func parsePreDecimalDigits(@noescape f: (UInt8) throws -> Void) rethrows {
675+ assert(state == .PreDecimalDigits, "Unexpected state entering parsePreDecimalDigits")
676+ advancing: while loc < input.count {
677+ let c = input[loc]
678+ switch c {
679+ case Literal.zero...Literal.nine:
680+ try f(c)
681+ loc = loc.successor()
682+
683+ case Literal.PERIOD:
684+ state = .Decimal
685+ return
686+
687+ case Literal.e, Literal.E:
688+ state = .Exponent
689+ return
690+
691+ default:
692+ break advancing
693+ }
694+ }
695+
696+ state = .Done
697+ }
698+
699+ mutating func parseDecimal() throws {
700+ assert(state == .Decimal, "Unexpected state entering parseDecimal")
701+ loc = loc.successor()
702+ guard loc < input.count else {
703+ throw JSONParser.Error.EndOfStreamUnexpected
704+ }
705+
706+ switch input[loc] {
707+ case Literal.zero...Literal.nine:
708+ state = .PostDecimalDigits
709+
710+ default:
711+ throw JSONParser.Error.NumberMissingFractionalDigits(offset: start)
712+ }
713+ }
714+
715+ mutating func parsePostDecimalDigits(@noescape f: (UInt8) throws -> Void) rethrows {
716+ assert(state == .PostDecimalDigits, "Unexpected state entering parsePostDecimalDigits")
717+
718+ advancing: while loc < input.count {
719+ let c = input[loc]
720+ switch c {
721+ case Literal.zero...Literal.nine:
722+ try f(c)
723+ loc = loc.successor()
724+
725+ case Literal.e, Literal.E:
726+ state = .Exponent
727+ return
728+
729+ default:
730+ break advancing
731+ }
732+ }
733+
734+ state = .Done
735+ }
736+
737+ mutating func parseExponent() throws -> JSONParser.Sign {
738+ assert(state == .Exponent, "Unexpected state entering parseExponent")
739+
740+ loc = loc.successor()
741+ guard loc < input.count else {
742+ throw JSONParser.Error.EndOfStreamUnexpected
743+ }
744+
745+ switch input[loc] {
746+ case Literal.zero...Literal.nine:
747+ state = .ExponentDigits
748+
749+ case Literal.PLUS:
750+ state = .ExponentSign
751+
752+ case Literal.MINUS:
753+ state = .ExponentSign
754+ return .Negative
755+
756+ default:
757+ throw JSONParser.Error.NumberSymbolMissingDigits(offset: start)
758+ }
759+
760+ return .Positive
761+ }
762+
763+ mutating func parseExponentSign() throws {
764+ assert(state == .ExponentSign, "Unexpected state entering parseExponentSign")
765+ loc = loc.successor()
766+ guard loc < input.count else {
767+ throw JSONParser.Error.EndOfStreamUnexpected
768+ }
769+
770+ switch input[loc] {
771+ case Literal.zero...Literal.nine:
772+ state = .ExponentDigits
773+
774+ default:
775+ throw JSONParser.Error.NumberSymbolMissingDigits(offset: start)
776+ }
777+ }
778+
779+ mutating func parseExponentDigits(@noescape f: (UInt8) throws -> Void) rethrows {
780+ assert(state == .ExponentDigits, "Unexpected state entering parseExponentDigits")
781+ advancing: while loc < input.count {
782+ let c = input[loc]
783+ switch c {
784+ case Literal.zero...Literal.nine:
785+ try f(c)
786+ loc = loc.successor()
787+
788+ default:
789+ break advancing
790+ }
791+ }
792+
793+ state = .Done
794+ }
795+}
796+
797+public extension JSONParser {
798+
799+ /// Creates a `JSONParser` ready to parse UTF-8 encoded `NSData`.
800+ ///
801+ /// If the data is mutable, it is copied before parsing. The data's lifetime
802+ /// is extended for the duration of parsing.
803+ init(utf8Data inData: NSData) {
804+ let data = inData.copy() as! NSData
805+ let buffer = UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length)
806+ self.init(buffer: buffer, owner: data)
807+ }
808+
809+ /// Creates a `JSONParser` from the code units represented by the `string`.
810+ ///
811+ /// The synthesized string is lifetime-extended for the duration of parsing.
812+ init(string: String) {
813+ let codePoints = string.nulTerminatedUTF8
814+ let buffer = codePoints.withUnsafeBufferPointer { nulTerminatedBuffer in
815+ // don't want to include the nul termination in the buffer - trim it off
816+ UnsafeBufferPointer(start: nulTerminatedBuffer.baseAddress, count: nulTerminatedBuffer.count - 1)
817+ }
818+ self.init(buffer: buffer, owner: codePoints)
819+ }
820+
821+}
822+
823+extension JSONParser: JSONParserType {
824+
825+ /// Creates an instance of `JSON` from UTF-8 encoded `NSData`.
826+ /// - parameter data: An instance of `NSData` to parse `JSON` from.
827+ /// - throws: Any `JSONParser.Error` that arises during decoding.
828+ /// - seealso: JSONParser.parse()
829+ public static func createJSONFromData(data: NSData) throws -> JSON {
830+ var parser = JSONParser(utf8Data: data)
831+ return try parser.parse()
832+ }
833+
834+}
835+
836+// MARK: - Errors
837+
838+extension JSONParser {
839+
840+ /// Enumeration describing possible errors that occur while parsing a JSON
841+ /// document. Most errors include an associated `offset`, representing the
842+ /// offset into the UTF-8 characters making up the document where the error
843+ /// occurred.
844+ public enum Error: ErrorType {
845+ /// The parser ran out of data prematurely. This usually means a value
846+ /// was not escaped, such as a string literal not ending with a double
847+ /// quote.
848+ case EndOfStreamUnexpected
849+
850+ /// Unexpected non-whitespace data was left around `offset` after
851+ /// parsing all valid JSON.
852+ case EndOfStreamGarbage(offset: Int)
853+
854+ /// Too many nested objects or arrays occured at the literal started
855+ /// around `offset`.
856+ case ExceededNestingLimit(offset: Int)
857+
858+ /// A `character` was not a valid start of a value around `offset`.
859+ case ValueInvalid(offset: Int, character: UnicodeScalar)
860+
861+ /// Badly-formed Unicode escape sequence at `offset`. A Unicode escape
862+ /// uses the text "\u" followed by 4 hex digits, such as "\uF09F\uA684"
863+ /// to represent U+1F984, "UNICORN FACE".
864+ case UnicodeEscapeInvalid(offset: Int)
865+
866+ /// Badly-formed control character around `offset`. JSON supports
867+ /// backslash-escaped double quotes, slashes, whitespace control codes,
868+ /// and Unicode escape sequences.
869+ case ControlCharacterUnrecognized(offset: Int)
870+
871+ /// Invalid token, expected `null` around `offset`
872+ case LiteralNilMisspelled(offset: Int)
873+
874+ /// Invalid token, expected `true` around `offset`
875+ case LiteralTrueMisspelled(offset: Int)
876+
877+ /// Invalid token, expected `false` around `offset`
878+ case LiteralFalseMisspelled(offset: Int)
879+
880+ /// Badly-formed collection at given `offset`, expected `,` or `:`
881+ case CollectionMissingSeparator(offset: Int)
882+
883+ /// While parsing an object literal, a value was found without a key
884+ /// around `offset`. The start of a string literal was expected.
885+ case DictionaryMissingKey(offset: Int)
886+
887+ /// Badly-formed number with no digits around `offset`. After a decimal
888+ /// point, a number must include some number of digits.
889+ case NumberMissingFractionalDigits(offset: Int)
890+
891+ /// Badly-formed number with symbols ("-" or "e") but no following
892+ /// digits around `offset`.
893+ case NumberSymbolMissingDigits(offset: Int)
894+
895+ /// Supplied data is encoded in an unsupported format.
896+ case InvalidUnicodeStreamEncoding(detectedEncoding: JSONEncodingDetector.Encoding)
897+ }
898+
899+ private enum InternalError: ErrorType {
900+ /// Attempted to parse an integer outside the range of [Int.min, Int.max]
901+ /// or a double outside the range of representable doubles. Note that
902+ /// for doubles, this could be an overflow or an underflow - we don't
903+ /// get enough information from Swift here to know which it is. The number
904+ /// causing the overflow/underflow began at `offset`.
905+ case NumberOverflow(offset: Int)
906+ }
907+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONParsing.swiftnew file+126
@@ -0,0 +1,126 @@
1+//
2+// JSONParsing.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/17/15.
6+// Copyright © 2015 Big Nerd Ranch. All rights reserved.
7+//
8+
9+import Foundation
10+
11+// MARK: - Deserialize JSON
12+
13+/// Protocol describing a backend parser that can produce `JSON` from `NSData`.
14+public protocol JSONParserType {
15+
16+ /// Creates an instance of `JSON` from `NSData`.
17+ /// - parameter data: An instance of `NSData` to use to create `JSON`.
18+ /// - throws: An error that may arise from calling `JSONObjectWithData(_:options:)` on `NSJSONSerialization` with the given data.
19+ /// - returns: An instance of `JSON`.
20+ static func createJSONFromData(data: NSData) throws -> JSON
21+
22+}
23+
24+extension JSON {
25+
26+ /// Create `JSON` from UTF-8 `data`. By default, parses using the
27+ /// Swift-native `JSONParser` backend.
28+ public init(data: NSData, usingParser parser: JSONParserType.Type = JSONParser.self) throws {
29+ self = try parser.createJSONFromData(data)
30+ }
31+
32+ /// Create `JSON` from UTF-8 `string`.
33+ public init(jsonString: Swift.String, usingParser parser: JSONParserType.Type = JSONParser.self) throws {
34+ self = try parser.createJSONFromData((jsonString as NSString).dataUsingEncoding(NSUTF8StringEncoding) ?? NSData())
35+ }
36+}
37+
38+// MARK: - NSJSONSerialization
39+
40+extension NSJSONSerialization: JSONParserType {
41+
42+ // MARK: Decode NSData
43+
44+ /// Use the built-in, Objective-C based JSON parser to create `JSON`.
45+ /// - parameter data: An instance of `NSData`.
46+ /// - returns: An instance of `JSON`.
47+ /// - throws: An error that may arise if the `NSData` cannot be parsed into an object.
48+ public static func createJSONFromData(data: NSData) throws -> JSON {
49+ return makeJSON(try NSJSONSerialization.JSONObjectWithData(data, options: []))
50+ }
51+
52+ // MARK: Make JSON
53+
54+ /// Makes a `JSON` object by matching its argument to a case in the `JSON` enum.
55+ /// - parameter object: The instance of `AnyObject` returned from serializing the JSON.
56+ /// - returns: An instance of `JSON` matching the JSON given to the function.
57+ private static func makeJSON(object: AnyObject) -> JSON {
58+ switch object {
59+ case let n as NSNumber:
60+ let numberType = CFNumberGetType(n)
61+ switch numberType {
62+ case .CharType:
63+ return .Bool(n.boolValue)
64+
65+ case .ShortType, .IntType, .LongType, .CFIndexType, .NSIntegerType, .SInt8Type, .SInt16Type, .SInt32Type:
66+ return .Int(n.integerValue)
67+
68+ case .SInt64Type, .LongLongType /* overflows 32-bit Int */:
69+ #if /* 32-bit arch */ arch(arm) || arch(i386)
70+ // Why double, when the Freddy parser would bump to String?
71+ //
72+ // Returning Double avoids making the type depend on whether you're running
73+ // 32-bit or 64-bit code when using the NSJSONSerialization parser.
74+ // NSJSONSerialization appears to bump numbers larger than Int.max to Double on
75+ // 64-bit platforms but use .SInt64Type on 32-bit platforms.
76+ // If we returned a String here, you'd get a String value on 32-bit,
77+ // but a Double value on 64-bit. Instead, we return Double.
78+ //
79+ // This means that, if you switch parsers,
80+ // you'll have to switch from .double to .string for pulling out
81+ // overflowing values, but if you stick with a single parser,
82+ // you at least won't have architecture-dependent lookups!
83+ return .Double(n.doubleValue)
84+ #else
85+ return .Int(n.integerValue)
86+ #endif
87+
88+ case .Float32Type, .Float64Type, .FloatType, .DoubleType, .CGFloatType:
89+ return .Double(n.doubleValue)
90+ }
91+
92+ case let arr as [AnyObject]:
93+ return makeJSONArray(arr)
94+
95+ case let dict as [Swift.String: AnyObject]:
96+ return makeJSONDictionary(dict)
97+
98+ case let s as Swift.String:
99+ return .String(s)
100+
101+ default:
102+ return .Null
103+ }
104+ }
105+
106+ // MARK: Make a JSON Array
107+
108+ /// Makes a `JSON` array from the object passed in.
109+ /// - parameter jsonArray: The array to transform into a `JSON`.
110+ /// - returns: An instance of `JSON` matching the array.
111+ private static func makeJSONArray(jsonArray: [AnyObject]) -> JSON {
112+ return .Array(jsonArray.map(makeJSON))
113+ }
114+
115+ // MARK: Make a JSON Dictionary
116+
117+ /// Makes a `JSON` dictionary from the Cocoa dictionary passed in.
118+ /// - parameter jsonDict: The dictionary to transform into `JSON`.
119+ /// - returns: An instance of `JSON` matching the dictionary.
120+ private static func makeJSONDictionary(jsonDict: [Swift.String: AnyObject]) -> JSON {
121+ return JSON(jsonDict.lazy.map { (key, value) in
122+ (key, makeJSON(value))
123+ })
124+ }
125+
126+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONSerializing.swiftnew file+43
@@ -0,0 +1,43 @@
1+// Copyright (C) 2016 Big Nerd Ranch, Inc. Licensed under the MIT license WITHOUT ANY WARRANTY.
2+
3+import Foundation
4+
5+// MARK: - Serialize JSON
6+
7+extension JSON {
8+
9+ /// Attempt to serialize `JSON` into an `NSData`.
10+ /// - returns: A byte-stream containing the `JSON` ready for wire transfer.
11+ /// - throws: Errors that arise from `NSJSONSerialization`.
12+ /// - see: Foundation.NSJSONSerialization
13+ public func serialize() throws -> NSData {
14+ let obj: AnyObject = toNSJSONSerializationObject()
15+ return try NSJSONSerialization.dataWithJSONObject(obj, options: [])
16+ }
17+
18+ /// A function to help with the serialization of `JSON`.
19+ /// - returns: An `AnyObject` suitable for `NSJSONSerialization`'s use.
20+ private func toNSJSONSerializationObject() -> AnyObject {
21+ switch self {
22+ case .Array(let jsonArray):
23+ return jsonArray.map { $0.toNSJSONSerializationObject() }
24+ case .Dictionary(let jsonDictionary):
25+ var cocoaDictionary = Swift.Dictionary<Swift.String, AnyObject>(minimumCapacity: jsonDictionary.count)
26+ for (key, json) in jsonDictionary {
27+ cocoaDictionary[key] = json.toNSJSONSerializationObject()
28+ }
29+ return cocoaDictionary
30+ case .String(let str):
31+ return str
32+ case .Double(let num):
33+ return num
34+ case .Int(let int):
35+ return int
36+ case .Bool(let b):
37+ return b
38+ case .Null:
39+ return NSNull()
40+ }
41+
42+ }
43+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/JSONSubscripting.swiftnew file+705
@@ -0,0 +1,705 @@
1+//
2+// JSONSubscripting.swift
3+// Freddy
4+//
5+// Created by Zachary Waldowski on 8/15/15.
6+// Copyright © 2015 Big Nerd Ranch. All rights reserved.
7+//
8+
9+// MARK: JSONPathType
10+
11+/// A protocol used to define a path within an instance of `JSON` that leads to some desired value.
12+///
13+/// A custom type, such as a `RawRepresentable` enum, may be made to conform to `JSONPathType`
14+/// and used with the subscript APIs.
15+public protocol JSONPathType {
16+ /// Use `self` to key into a `dictionary`.
17+ ///
18+ /// Unlike Swift dictionaries, failing to find a value for a key should throw
19+ /// an error rather than convert to `nil`.
20+ ///
21+ /// Upon failure, implementers should throw an error from `JSON.Error`.
22+ func valueInDictionary(dictionary: [Swift.String : JSON]) throws -> JSON
23+
24+ /// Use `self` to index into an `array`.
25+ ///
26+ /// Unlike Swift array, attempting to index outside the collection's bounds
27+ /// should throw an error rather than crash.
28+ ///
29+ /// Upon failure, implementers should throw an error from `JSON.Error`.
30+ func valueInArray(array: [JSON]) throws -> JSON
31+}
32+
33+extension JSONPathType {
34+
35+ /// The default behavior for keying into a dictionary is to throw
36+ /// `JSON.Error.UnexpectedSubscript`.
37+ public func valueInDictionary(dictionary: [Swift.String : JSON]) throws -> JSON {
38+ throw JSON.Error.UnexpectedSubscript(type: Self.self)
39+ }
40+
41+ /// The default behavior for indexing into an array is to throw
42+ /// `JSON.Error.UnexpectedSubscript`.
43+ public func valueInArray(array: [JSON]) throws -> JSON {
44+ throw JSON.Error.UnexpectedSubscript(type: Self.self)
45+ }
46+
47+}
48+
49+extension String: JSONPathType {
50+
51+ /// A method used to retrieve a value from a given dictionary for a specific key.
52+ /// - parameter dictionary: A `Dictionary` with `String` keys and `JSON` values.
53+ /// - throws: `.KeyNotFound` with an associated value of `self`, where `self` is a `String`,
54+ /// should the key not be present within the `JSON`.
55+ /// - returns: The `JSON` value associated with the given key.
56+ public func valueInDictionary(dictionary: [Swift.String : JSON]) throws -> JSON {
57+ guard let next = dictionary[self] else {
58+ throw JSON.Error.KeyNotFound(key: self)
59+ }
60+ return next
61+ }
62+
63+}
64+
65+extension Int: JSONPathType {
66+
67+ /// A method used to retrieve a value from a given array for a specific index.
68+ /// - parameter array: An `Array` of `JSON`.
69+ /// - throws: `.IndexOutOfBounds` with an associated value of `self`, where `self` is an `Int`,
70+ /// should the index not be within the valid range for the array of `JSON`.
71+ /// - returns: The `JSON` value found at the given index.
72+ public func valueInArray(array: [JSON]) throws -> JSON {
73+ guard case array.indices = self else {
74+ throw JSON.Error.IndexOutOfBounds(index: self)
75+ }
76+ return array[self]
77+ }
78+
79+}
80+
81+// MARK: - Subscripting core
82+
83+private extension JSON {
84+
85+ enum SubscriptError: ErrorType {
86+ case SubscriptIntoNull(JSONPathType)
87+ }
88+
89+ func valueForPathFragment(fragment: JSONPathType, detectNull: Swift.Bool) throws -> JSON {
90+ switch self {
91+ case .Null where detectNull:
92+ throw SubscriptError.SubscriptIntoNull(fragment)
93+ case let .Dictionary(dict):
94+ return try fragment.valueInDictionary(dict)
95+ case let .Array(array):
96+ return try fragment.valueInArray(array)
97+ default:
98+ throw Error.UnexpectedSubscript(type: fragment.dynamicType)
99+ }
100+ }
101+
102+ func valueAtPath(path: [JSONPathType], detectNull: Swift.Bool = false) throws -> JSON {
103+ var result = self
104+ for fragment in path {
105+ result = try result.valueForPathFragment(fragment, detectNull: detectNull)
106+ }
107+ return result
108+ }
109+
110+}
111+
112+// MARK: - Subscripting operator
113+
114+extension JSON {
115+
116+ public subscript(key: Swift.String) -> JSON? {
117+ return try? valueForPathFragment(key, detectNull: false)
118+ }
119+
120+ public subscript(index: Swift.Int) -> JSON? {
121+ return try? valueForPathFragment(index, detectNull: false)
122+ }
123+
124+}
125+
126+// MARK: - Simple member unpacking
127+
128+extension JSON {
129+
130+ /// Attempts to decode into the returning type from a path into JSON.
131+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`.
132+ /// - parameter type: If the context this method is called from does not
133+ /// make the return type clear, pass a type implementing `JSONDecodable`
134+ /// to disambiguate the type to decode with.
135+ /// - returns: An initialized member from the inner JSON.
136+ /// - throws: One of the following errors contained in `JSON.Error`:
137+ /// * `KeyNotFound`: A given `String` key does not exist inside a
138+ /// descendant `JSON` dictionary.
139+ /// * `IndexOutOfBounds`: A given `Int` index is outside the bounds of a
140+ /// descendant `JSON` array.
141+ /// * `UnexpectedSubscript`: A given subscript cannot be used with the
142+ /// corresponding `JSON` value.
143+ /// * `TypeNotConvertible`: The target value's type inside of
144+ /// the `JSON` instance does not match `Decoded`.
145+ public func decode<Decoded: JSONDecodable>(path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> Decoded {
146+ return try Decoded(json: valueAtPath(path))
147+ }
148+
149+ /// Retrieves a `Double` from a path into JSON.
150+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
151+ /// - returns: A floating-point `Double`
152+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
153+ /// - seealso: `JSON.decode(_:type:)`
154+ public func double(path: JSONPathType...) throws -> Swift.Double {
155+ return try Swift.Double(json: valueAtPath(path))
156+ }
157+
158+ /// Retrieves an `Int` from a path into JSON.
159+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
160+ /// - returns: A numeric `Int`
161+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
162+ /// - seealso: `JSON.decode(_:type:)`
163+ public func int(path: JSONPathType...) throws -> Swift.Int {
164+ return try Swift.Int(json: valueAtPath(path))
165+ }
166+
167+ /// Retrieves a `String` from a path into JSON.
168+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
169+ /// - returns: A textual `String`
170+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
171+ /// - seealso: `JSON.decode(_:type:)`
172+ public func string(path: JSONPathType...) throws -> Swift.String {
173+ return try Swift.String(json: valueAtPath(path))
174+ }
175+
176+ /// Retrieves a `Bool` from a path into JSON.
177+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
178+ /// - returns: A truthy `Bool`
179+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
180+ /// - seealso: `JSON.decode(_:type:)`
181+ public func bool(path: JSONPathType...) throws -> Swift.Bool {
182+ return try Swift.Bool(json: valueAtPath(path))
183+ }
184+
185+ /// Retrieves a `[JSON]` from a path into JSON.
186+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
187+ /// - returns: An `Array` of `JSON` elements
188+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
189+ /// - seealso: `JSON.decode(_:type:)`
190+ public func array(path: JSONPathType...) throws -> [JSON] {
191+ return try JSON.getArray(valueAtPath(path))
192+ }
193+
194+ /// Attempts to decode many values from a descendant JSON array at a path
195+ /// into JSON.
196+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
197+ /// - parameter type: If the context this method is called from does not
198+ /// make the return type clear, pass a type implementing `JSONDecodable`
199+ /// to disambiguate the type to decode with.
200+ /// - returns: An `Array` of decoded elements
201+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`, or
202+ /// any error that arises from decoding the contained values.
203+ /// - seealso: `JSON.decode(_:type:)`
204+ public func arrayOf<Decoded: JSONDecodable>(path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> [Decoded] {
205+ return try JSON.getArrayOf(valueAtPath(path))
206+ }
207+
208+ /// Retrieves a `[String: JSON]` from a path into JSON.
209+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
210+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
211+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)`.
212+ /// - seealso: `JSON.decode(_:type:)`
213+ public func dictionary(path: JSONPathType...) throws -> [Swift.String: JSON] {
214+ return try JSON.getDictionary(valueAtPath(path))
215+ }
216+
217+ /// Attempts to decode many values from a descendant JSON object at a path
218+ /// into JSON.
219+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
220+ /// - parameter type: If the context this method is called from does not
221+ /// make the return type clear, pass a type implementing `JSONDecodable`
222+ /// to disambiguate the value type to decode with.
223+ /// - returns: A `Dictionary` of `String` keys and decoded values.
224+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)` or
225+ /// any error that arises from decoding the contained values.
226+ /// - seealso: `JSON.decode(_:type:)`
227+ public func dictionaryOf<Decoded: JSONDecodable>(path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> [Swift.String: Decoded] {
228+ return try JSON.getDictionaryOf(valueAtPath(path))
229+ }
230+
231+}
232+
233+// MARK: - NotFound-Or-Null-to-Optional unpacking
234+
235+extension JSON {
236+
237+ /// An `OptionSetType` used to represent the different options available for subscripting `JSON` with `null` values or missing keys.
238+ /// * `.NullBecomesNil` - Treat `null` values as `nil`.
239+ /// * `.MissingKeyBecomesNil` - Treat missing keys as `nil`.
240+ public struct SubscriptingOptions: OptionSetType {
241+ public let rawValue: Swift.Int
242+ public init(rawValue: Swift.Int) {
243+ self.rawValue = rawValue
244+ }
245+
246+ /// Treat `null` values as `nil`.
247+ public static let NullBecomesNil = SubscriptingOptions(rawValue: 1 << 0)
248+ /// Treat missing keys as `nil`.
249+ public static let MissingKeyBecomesNil = SubscriptingOptions(rawValue: 1 << 1)
250+ }
251+
252+ private func mapOptionalAtPath<Value>(path: [JSONPathType], alongPath: SubscriptingOptions, @noescape transform: JSON throws -> Value) throws -> Value? {
253+ let detectNull = alongPath.contains(.NullBecomesNil)
254+ let detectNotFound = alongPath.contains(.MissingKeyBecomesNil)
255+ var json: JSON?
256+ do {
257+ json = try valueAtPath(path, detectNull: detectNull)
258+ return try json.map(transform)
259+ } catch Error.IndexOutOfBounds where detectNotFound {
260+ return nil
261+ } catch Error.KeyNotFound where detectNotFound {
262+ return nil
263+ } catch Error.ValueNotConvertible where detectNull && json == .Null {
264+ return nil
265+ } catch SubscriptError.SubscriptIntoNull where detectNull {
266+ return nil
267+ }
268+ }
269+}
270+
271+extension JSON {
272+
273+ /// Optionally decodes into the returning type from a path into JSON.
274+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
275+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
276+ /// - parameter type: If the context this method is called from does not
277+ /// make the return type clear, pass a type implementing `JSONDecodable`
278+ /// to disambiguate the type to decode with.
279+ /// - returns: A decoded value from the inner JSON if found, or `nil`.
280+ /// - throws: One of the following errors contained in `JSON.Error`:
281+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
282+ /// `JSON` dictionary.
283+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
284+ /// descendant `JSON` array.
285+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
286+ /// corresponding `JSON` value.
287+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
288+ /// instance does not match the decoded value.
289+ /// * Any error that arises from decoding the value.
290+ public func decode<Decoded: JSONDecodable>(path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> Decoded? {
291+ return try mapOptionalAtPath(path, alongPath: options, transform: Decoded.init)
292+ }
293+
294+ /// Optionally retrieves a `Double` from a path into JSON.
295+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`.
296+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
297+ /// - returns: A `Double` if a value could be found, otherwise `nil`.
298+ /// - throws: One of the following errors contained in `JSON.Error`:
299+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
300+ /// `JSON` dictionary.
301+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
302+ /// descendant `JSON` array.
303+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
304+ /// corresponding `JSON` value.
305+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
306+ /// instance does not match the decoded value.
307+ public func double(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Swift.Double? {
308+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Double.init)
309+ }
310+
311+ /// Optionally retrieves a `Int` from a path into JSON.
312+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
313+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
314+ /// - returns: A numeric `Int` if a value could be found, otherwise `nil`.
315+ /// - throws: One of the following errors contained in `JSON.Error`:
316+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
317+ /// `JSON` dictionary.
318+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
319+ /// descendant `JSON` array.
320+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
321+ /// corresponding `JSON` value.
322+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
323+ /// instance does not match the decoded value.
324+ public func int(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Swift.Int? {
325+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Int.init)
326+ }
327+
328+ /// Optionally retrieves a `String` from a path into JSON.
329+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
330+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
331+ /// - returns: A text `String` if a value could be found, otherwise `nil`.
332+ /// - throws: One of the following errors contained in `JSON.Error`:
333+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
334+ /// `JSON` dictionary.
335+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
336+ /// descendant `JSON` array.
337+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
338+ /// corresponding `JSON` value.
339+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
340+ /// instance does not match the decoded value.
341+ public func string(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Swift.String? {
342+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.String.init)
343+ }
344+
345+ /// Optionally retrieves a `Bool` from a path into JSON.
346+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
347+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
348+ /// - returns: A truthy `Bool` if a value could be found, otherwise `nil`.
349+ /// - throws: One of the following errors contained in `JSON.Error`:
350+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
351+ /// `JSON` dictionary.
352+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
353+ /// descendant `JSON` array.
354+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
355+ /// corresponding `JSON` value.
356+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
357+ /// instance does not match the decoded value.
358+ public func bool(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Swift.Bool? {
359+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Bool.init)
360+ }
361+
362+ /// Optionally retrieves a `[JSON]` from a path into the recieving structure.
363+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
364+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
365+ /// - returns: An `Array` of `JSON` elements if a value could be found,
366+ /// otherwise `nil`.
367+ /// - throws: One of the following errors contained in `JSON.Error`:
368+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
369+ /// `JSON` dictionary.
370+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
371+ /// descendant `JSON` array.
372+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
373+ /// corresponding `JSON` value.
374+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
375+ /// instance does not match the decoded value.
376+ public func array(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> [JSON]? {
377+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArray)
378+ }
379+
380+ /// Optionally decodes many values from a descendant array at a path into
381+ /// JSON.
382+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
383+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
384+ /// - parameter type: If the context this method is called from does not
385+ /// make the return type clear, pass a type implementing `JSONDecodable`
386+ /// to disambiguate the value type to decode with.
387+ /// - returns: An `Array` of decoded elements if found, otherwise `nil`.
388+ /// - throws: One of the following errors contained in `JSON.Error`:
389+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
390+ /// `JSON` dictionary.
391+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
392+ /// descendant `JSON` array.
393+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
394+ /// corresponding `JSON` value.
395+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
396+ /// instance does not match the decoded value.
397+ /// * Any error that arises from decoding the value.
398+ public func arrayOf<Decoded: JSONDecodable>(path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> [Decoded]? {
399+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArrayOf)
400+ }
401+
402+ /// Optionally retrieves a `[String: JSON]` from a path into the recieving
403+ /// structure.
404+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
405+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
406+ /// - returns: A `Dictionary` of `String` mapping to `JSON` elements if a
407+ /// value could be found, otherwise `nil`.
408+ /// - throws: One of the following errors contained in `JSON.Error`:
409+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
410+ /// `JSON` dictionary.
411+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
412+ /// descendant `JSON` array.
413+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
414+ /// corresponding `JSON` value.
415+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
416+ /// instance does not match the decoded value.
417+ public func dictionary(path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> [Swift.String: JSON]? {
418+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getDictionary)
419+ }
420+
421+ /// Optionally attempts to decode many values from a descendant object at a path
422+ /// into JSON.
423+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
424+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
425+ /// - parameter type: If the context this method is called from does not
426+ /// make the return type clear, pass a type implementing `JSONDecodable`
427+ /// to disambiguate the value type to decode with.
428+ /// - returns: A `Dictionary` of `String` mapping to decoded elements if a
429+ /// value could be found, otherwise `nil`.
430+ /// - throws: One of the following errors contained in `JSON.Error`:
431+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
432+ /// `JSON` dictionary.
433+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
434+ /// descendant `JSON` array.
435+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
436+ /// corresponding `JSON` value.
437+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
438+ /// instance does not match the decoded value.
439+ /// * Any error that arises from decoding the value.
440+ public func dictionaryOf<Decoded: JSONDecodable>(path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> [Swift.String: Decoded]? {
441+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getDictionaryOf)
442+ }
443+
444+}
445+
446+// MARK: - Missing-with-fallback unpacking
447+
448+extension JSON {
449+
450+ private func mapOptionalAtPath<Value>(path: [JSONPathType], @noescape fallback: () -> Value, @noescape transform: JSON throws -> Value) throws -> Value {
451+ return try mapOptionalAtPath(path, alongPath: .MissingKeyBecomesNil, transform: transform) ?? fallback()
452+ }
453+
454+ /// Attempts to decode into the returning type from a path into
455+ /// JSON, or returns a fallback if not found.
456+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
457+ /// - parameter fallback: Value to use when one is missing at the subscript.
458+ /// - returns: An initialized member from the inner JSON.
459+ /// - throws: One of the following errors contained in `JSON.Error`:
460+ /// * `UnexpectedSubscript`: A given subscript cannot be used with the
461+ /// corresponding `JSON` value.
462+ /// * `TypeNotConvertible`: The target value's type inside of
463+ /// the `JSON` instance does not match `Decoded`.
464+ public func decode<Decoded: JSONDecodable>(path: JSONPathType..., @autoclosure or fallback: () -> Decoded) throws -> Decoded {
465+ return try mapOptionalAtPath(path, fallback: fallback, transform: Decoded.init)
466+ }
467+
468+ /// Retrieves a `Double` from a path into JSON or a fallback if not found.
469+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
470+ /// - parameter fallback: `Double` to use when one is missing at the subscript.
471+ /// - returns: A floating-point `Double`
472+ /// - throws: One of the `JSON.Error` cases thrown by calling `mapOptionalAtPath(_:fallback:transform:)`.
473+ /// - seealso: `optionalAtPath(_:ifNotFound)`.
474+ public func double(path: JSONPathType..., @autoclosure or fallback: () -> Swift.Double) throws -> Swift.Double {
475+ return try mapOptionalAtPath(path, fallback: fallback, transform: Swift.Double.init)
476+ }
477+
478+ /// Retrieves an `Int` from a path into JSON or a fallback if not found.
479+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
480+ /// - parameter fallback: `Int` to use when one is missing at the subscript.
481+ /// - returns: A numeric `Int`
482+ /// - throws: One of the following errors contained in `JSON.Error`:
483+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
484+ /// `JSON` dictionary.
485+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
486+ /// descendant `JSON` array.
487+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
488+ /// corresponding `JSON` value.
489+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
490+ /// instance does not match the decoded value.
491+ public func int(path: JSONPathType..., @autoclosure or fallback: () -> Swift.Int) throws -> Swift.Int {
492+ return try mapOptionalAtPath(path, fallback: fallback, transform: Swift.Int.init)
493+ }
494+
495+ /// Retrieves a `String` from a path into JSON or a fallback if not found.
496+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
497+ /// - parameter fallback: `String` to use when one is missing at the subscript.
498+ /// - returns: A textual `String`
499+ /// - throws: One of the following errors contained in `JSON.Error`:
500+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
501+ /// `JSON` dictionary.
502+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
503+ /// descendant `JSON` array.
504+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
505+ /// corresponding `JSON` value.
506+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
507+ /// instance does not match the decoded value.
508+ public func string(path: JSONPathType..., @autoclosure or fallback: () -> Swift.String) throws -> Swift.String {
509+ return try mapOptionalAtPath(path, fallback: fallback, transform: Swift.String.init)
510+ }
511+
512+ /// Retrieves a `Bool` from a path into JSON or a fallback if not found.
513+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
514+ /// - parameter fallback: `Bool` to use when one is missing at the subscript.
515+ /// - returns: A truthy `Bool`
516+ /// - throws: One of the following errors contained in `JSON.Error`:
517+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
518+ /// `JSON` dictionary.
519+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
520+ /// descendant `JSON` array.
521+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
522+ /// corresponding `JSON` value.
523+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
524+ /// instance does not match the decoded value.
525+ public func bool(path: JSONPathType..., @autoclosure or fallback: () -> Swift.Bool) throws -> Swift.Bool {
526+ return try mapOptionalAtPath(path, fallback: fallback, transform: Swift.Bool.init)
527+ }
528+
529+ /// Retrieves a `[JSON]` from a path into JSON or a fallback if not found.
530+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
531+ /// - parameter fallback: `Array` to use when one is missing at the subscript.
532+ /// - returns: An `Array` of `JSON` elements
533+ /// - throws: One of the following errors contained in `JSON.Error`:
534+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
535+ /// `JSON` dictionary.
536+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
537+ /// descendant `JSON` array.
538+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
539+ /// corresponding `JSON` value.
540+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
541+ /// instance does not match the decoded value.
542+ public func array(path: JSONPathType..., @autoclosure or fallback: () -> [JSON]) throws -> [JSON] {
543+ return try mapOptionalAtPath(path, fallback: fallback, transform: JSON.getArray)
544+ }
545+
546+ /// Attempts to decodes many values from a desendant JSON array at a path
547+ /// into the recieving structure, returning a fallback if not found.
548+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
549+ /// - parameter fallback: `Array` to use when one is missing at the subscript.
550+ /// - returns: An `Array` of decoded elements
551+ /// - throws: One of the following errors contained in `JSON.Error`:
552+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
553+ /// `JSON` dictionary.
554+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
555+ /// descendant `JSON` array.
556+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
557+ /// corresponding `JSON` value.
558+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
559+ /// instance does not match the decoded value.
560+ /// * Any error that arises from decoding the value.
561+ public func arrayOf<Decoded: JSONDecodable>(path: JSONPathType..., @autoclosure or fallback: () -> [Decoded]) throws -> [Decoded] {
562+ return try mapOptionalAtPath(path, fallback: fallback, transform: JSON.getArrayOf)
563+ }
564+
565+ /// Retrieves a `[String: JSON]` from a path into JSON or a fallback if not
566+ /// found.
567+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
568+ /// - parameter fallback: `Dictionary` to use when one is missing at the subscript.
569+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
570+ /// - throws: One of the following errors contained in `JSON.Error`:
571+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
572+ /// `JSON` dictionary.
573+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
574+ /// descendant `JSON` array.
575+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
576+ /// corresponding `JSON` value.
577+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
578+ /// instance does not match the decoded value.
579+ public func dictionary(path: JSONPathType..., @autoclosure or fallback: () -> [Swift.String: JSON]) throws -> [Swift.String: JSON] {
580+ return try mapOptionalAtPath(path, fallback: fallback, transform: JSON.getDictionary)
581+ }
582+
583+ /// Attempts to decode many values from a descendant JSON object at a path
584+ /// into the receiving structure, returning a fallback if not found.
585+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
586+ /// - parameter fallback: Value to use when one is missing at the subscript
587+ /// - returns: A `Dictionary` of `String` mapping to decoded elements.
588+ /// - throws: One of the following errors contained in `JSON.Error`:
589+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
590+ /// `JSON` dictionary.
591+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
592+ /// descendant `JSON` array.
593+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
594+ /// corresponding `JSON` value.
595+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
596+ /// instance does not match the decoded value.
597+ /// * Any error that arises from decoding the value.
598+ public func dictionaryOf<Decoded: JSONDecodable>(path: JSONPathType..., @autoclosure or fallback: () -> [Swift.String: Decoded]) throws -> [Swift.String: Decoded] {
599+ return try mapOptionalAtPath(path, fallback: fallback, transform: JSON.getDictionaryOf)
600+ }
601+
602+
603+}
604+
605+// MARK: - Deprecated methods
606+
607+extension JSON {
608+
609+ @available(*, deprecated, message="Use 'decode(_:alongPath:type:)' with options '[.MissingKeyBecomesNil]'")
610+ public func decode<Decoded: JSONDecodable>(path: JSONPathType..., ifNotFound: Swift.Bool, type: Decoded.Type = Decoded.self) throws -> Decoded? {
611+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
612+ return try mapOptionalAtPath(path, alongPath: options, transform: Decoded.init)
613+ }
614+
615+ @available(*, deprecated, message="Use 'decode(_:alongPath:type:)' with options '[.NullBecomesNil]'")
616+ public func decode<Decoded: JSONDecodable>(path: JSONPathType..., ifNull: Swift.Bool, type: Decoded.Type = Decoded.self) throws -> Decoded? {
617+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
618+ return try mapOptionalAtPath(path, alongPath: options, transform: Decoded.init)
619+ }
620+
621+ @available(*, deprecated, message="Use 'double(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
622+ public func double(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> Swift.Double? {
623+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
624+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Double.init)
625+ }
626+
627+ @available(*, deprecated, message="Use 'double(_:alongPath:)' with options '[.NullBecomesNil]'")
628+ public func double(path: JSONPathType..., ifNull: Swift.Bool) throws -> Swift.Double? {
629+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
630+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Double.init)
631+ }
632+
633+ @available(*, deprecated, message="Use 'int(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
634+ public func int(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> Swift.Int? {
635+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
636+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Int.init)
637+ }
638+
639+ @available(*, deprecated, message="Use 'int(_:alongPath:)' with options '[.NullBecomesNil]'")
640+ public func int(path: JSONPathType..., ifNull: Swift.Bool) throws -> Swift.Int? {
641+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
642+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Int.init)
643+ }
644+
645+ @available(*, deprecated, message="Use 'string(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
646+ public func string(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> Swift.String? {
647+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
648+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.String.init)
649+ }
650+
651+ @available(*, deprecated, message="Use 'string(_:alongPath:)' with options '[.NullBecomesNil]'")
652+ public func string(path: JSONPathType..., ifNull: Swift.Bool) throws -> Swift.String? {
653+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
654+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.String.init)
655+ }
656+
657+ @available(*, deprecated, message="Use 'bool(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
658+ public func bool(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> Swift.Bool? {
659+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
660+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Bool.init)
661+ }
662+
663+ @available(*, deprecated, message="Use 'bool(_:alongPath:)' with options '[.NullBecomesNil]'")
664+ public func bool(path: JSONPathType..., ifNull: Swift.Bool) throws -> Swift.Bool? {
665+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
666+ return try mapOptionalAtPath(path, alongPath: options, transform: Swift.Bool.init)
667+ }
668+
669+ @available(*, deprecated, message="Use 'array(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
670+ public func array(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> [JSON]? {
671+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
672+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArray)
673+ }
674+
675+ @available(*, deprecated, message="Use 'array(_:alongPath:)' with options '[.NullBecomesNil]'")
676+ public func array(path: JSONPathType..., ifNull: Swift.Bool) throws -> [JSON]? {
677+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
678+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArray)
679+ }
680+
681+ @available(*, deprecated, message="Use 'arrayOf(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
682+ public func arrayOf<Decoded: JSONDecodable>(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> [Decoded]? {
683+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
684+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArrayOf)
685+ }
686+
687+ @available(*, deprecated, message="Use 'arrayOf(_:alongPath:)' with options '[.NullBecomesNil]'")
688+ public func arrayOf<Decoded: JSONDecodable>(path: JSONPathType..., ifNull: Swift.Bool) throws -> [Decoded]? {
689+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
690+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getArrayOf)
691+ }
692+
693+ @available(*, deprecated, message="Use 'dictionary(_:alongPath:)' with options '[.MissingKeyBecomesNil]'")
694+ public func dictionary(path: JSONPathType..., ifNotFound: Swift.Bool) throws -> [Swift.String: JSON]? {
695+ let options: SubscriptingOptions = ifNotFound ? [.MissingKeyBecomesNil] : []
696+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getDictionary)
697+ }
698+
699+ @available(*, deprecated, message="Use 'dictionary(_:alongPath:)' with options '[.NullBecomesNil]'")
700+ public func dictionary(path: JSONPathType..., ifNull: Swift.Bool) throws -> [Swift.String: JSON]? {
701+ let options: SubscriptingOptions = ifNull ? [.NullBecomesNil] : []
702+ return try mapOptionalAtPath(path, alongPath: options, transform: JSON.getDictionary)
703+ }
704+
705+}
tests/JSONTestSuite/parsers/test_Freddy_20160830/test_Freddy/main.swiftnew file+37
@@ -0,0 +1,37 @@
1+//
2+// main.swift
3+// test_Freddy
4+//
5+// Created by nst on 10/08/16.
6+// Copyright © 2016 Nicolas Seriot. All rights reserved.
7+//
8+
9+import Foundation
10+
11+func main() {
12+
13+ guard Process.arguments.count == 2 else {
14+ let url = NSURL(fileURLWithPath: Process.arguments[0])
15+ guard let programName = url.lastPathComponent else { exit(1) }
16+ print("Usage: ./\(programName) file.json")
17+ exit(1)
18+ }
19+
20+ let path = Process.arguments[1]
21+ let url = NSURL.fileURLWithPath(path)
22+
23+ guard let data = NSData(contentsOfURL:url) else {
24+ print("*** CANNOT READ DATA AT \(url)")
25+ return
26+ }
27+
28+ var p = JSONParser(utf8Data: data)
29+ do {
30+ try p.parse()
31+ exit(0)
32+ } catch {
33+ exit(1)
34+ }
35+}
36+
37+main()
tests/JSONTestSuite/parsers/test_Freddy_20161018/bin/test_Freddynew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/project.pbxprojnew file+300
@@ -0,0 +1,300 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 46;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D84ABA1D5B0A1000776402 /* main.swift */; };
11+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7781D5B75600021630C /* JSON.swift */; };
12+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7791D5B75600021630C /* JSONDecodable.swift */; };
13+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77A1D5B75600021630C /* JSONEncodable.swift */; };
14+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */; };
15+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */; };
16+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77D1D5B75600021630C /* JSONParser.swift */; };
17+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77E1D5B75600021630C /* JSONParsing.swift */; };
18+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77F1D5B75600021630C /* JSONSerializing.swift */; };
19+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7801D5B75600021630C /* JSONSubscripting.swift */; };
20+/* End PBXBuildFile section */
21+
22+/* Begin PBXCopyFilesBuildPhase section */
23+ 03D84AB51D5B0A1000776402 /* CopyFiles */ = {
24+ isa = PBXCopyFilesBuildPhase;
25+ buildActionMask = 2147483647;
26+ dstPath = /usr/share/man/man1;
27+ dstSubfolderSpec = 0;
28+ files = (
29+ );
30+ runOnlyForDeploymentPostprocessing = 1;
31+ };
32+/* End PBXCopyFilesBuildPhase section */
33+
34+/* Begin PBXFileReference section */
35+ 03D84AB71D5B0A1000776402 /* test_Freddy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test_Freddy; sourceTree = BUILT_PRODUCTS_DIR; };
36+ 03D84ABA1D5B0A1000776402 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
37+ 03F8F7781D5B75600021630C /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSON.swift; path = test_Freddy/JSON.swift; sourceTree = "<group>"; };
38+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONDecodable.swift; path = test_Freddy/JSONDecodable.swift; sourceTree = "<group>"; };
39+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodable.swift; path = test_Freddy/JSONEncodable.swift; sourceTree = "<group>"; };
40+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodingDetector.swift; path = test_Freddy/JSONEncodingDetector.swift; sourceTree = "<group>"; };
41+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONLiteralConvertible.swift; path = test_Freddy/JSONLiteralConvertible.swift; sourceTree = "<group>"; };
42+ 03F8F77D1D5B75600021630C /* JSONParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParser.swift; path = test_Freddy/JSONParser.swift; sourceTree = "<group>"; };
43+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParsing.swift; path = test_Freddy/JSONParsing.swift; sourceTree = "<group>"; };
44+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSerializing.swift; path = test_Freddy/JSONSerializing.swift; sourceTree = "<group>"; };
45+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSubscripting.swift; path = test_Freddy/JSONSubscripting.swift; sourceTree = "<group>"; };
46+/* End PBXFileReference section */
47+
48+/* Begin PBXFrameworksBuildPhase section */
49+ 03D84AB41D5B0A1000776402 /* Frameworks */ = {
50+ isa = PBXFrameworksBuildPhase;
51+ buildActionMask = 2147483647;
52+ files = (
53+ );
54+ runOnlyForDeploymentPostprocessing = 0;
55+ };
56+/* End PBXFrameworksBuildPhase section */
57+
58+/* Begin PBXGroup section */
59+ 03D84AAE1D5B0A1000776402 = {
60+ isa = PBXGroup;
61+ children = (
62+ 03F8F7781D5B75600021630C /* JSON.swift */,
63+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */,
64+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */,
65+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */,
66+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */,
67+ 03F8F77D1D5B75600021630C /* JSONParser.swift */,
68+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */,
69+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */,
70+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */,
71+ 03D84AB91D5B0A1000776402 /* test_Freddy */,
72+ 03D84AB81D5B0A1000776402 /* Products */,
73+ );
74+ sourceTree = "<group>";
75+ };
76+ 03D84AB81D5B0A1000776402 /* Products */ = {
77+ isa = PBXGroup;
78+ children = (
79+ 03D84AB71D5B0A1000776402 /* test_Freddy */,
80+ );
81+ name = Products;
82+ sourceTree = "<group>";
83+ };
84+ 03D84AB91D5B0A1000776402 /* test_Freddy */ = {
85+ isa = PBXGroup;
86+ children = (
87+ 03D84ABA1D5B0A1000776402 /* main.swift */,
88+ );
89+ path = test_Freddy;
90+ sourceTree = "<group>";
91+ };
92+/* End PBXGroup section */
93+
94+/* Begin PBXNativeTarget section */
95+ 03D84AB61D5B0A1000776402 /* test_Freddy */ = {
96+ isa = PBXNativeTarget;
97+ buildConfigurationList = 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */;
98+ buildPhases = (
99+ 03D84AB31D5B0A1000776402 /* Sources */,
100+ 03D84AB41D5B0A1000776402 /* Frameworks */,
101+ 03D84AB51D5B0A1000776402 /* CopyFiles */,
102+ );
103+ buildRules = (
104+ );
105+ dependencies = (
106+ );
107+ name = test_Freddy;
108+ productName = test_Freddy;
109+ productReference = 03D84AB71D5B0A1000776402 /* test_Freddy */;
110+ productType = "com.apple.product-type.tool";
111+ };
112+/* End PBXNativeTarget section */
113+
114+/* Begin PBXProject section */
115+ 03D84AAF1D5B0A1000776402 /* Project object */ = {
116+ isa = PBXProject;
117+ attributes = {
118+ LastSwiftUpdateCheck = 0800;
119+ LastUpgradeCheck = 0800;
120+ ORGANIZATIONNAME = "Nicolas Seriot";
121+ TargetAttributes = {
122+ 03D84AB61D5B0A1000776402 = {
123+ CreatedOnToolsVersion = 8.0;
124+ DevelopmentTeam = VBYRKYS73S;
125+ DevelopmentTeamName = "Nicolas Seriot";
126+ ProvisioningStyle = Automatic;
127+ };
128+ };
129+ };
130+ buildConfigurationList = 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */;
131+ compatibilityVersion = "Xcode 3.2";
132+ developmentRegion = English;
133+ hasScannedForEncodings = 0;
134+ knownRegions = (
135+ en,
136+ );
137+ mainGroup = 03D84AAE1D5B0A1000776402;
138+ productRefGroup = 03D84AB81D5B0A1000776402 /* Products */;
139+ projectDirPath = "";
140+ projectRoot = "";
141+ targets = (
142+ 03D84AB61D5B0A1000776402 /* test_Freddy */,
143+ );
144+ };
145+/* End PBXProject section */
146+
147+/* Begin PBXSourcesBuildPhase section */
148+ 03D84AB31D5B0A1000776402 /* Sources */ = {
149+ isa = PBXSourcesBuildPhase;
150+ buildActionMask = 2147483647;
151+ files = (
152+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */,
153+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */,
154+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */,
155+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */,
156+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */,
157+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */,
158+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */,
159+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */,
160+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */,
161+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */,
162+ );
163+ runOnlyForDeploymentPostprocessing = 0;
164+ };
165+/* End PBXSourcesBuildPhase section */
166+
167+/* Begin XCBuildConfiguration section */
168+ 03D84ABC1D5B0A1000776402 /* Debug */ = {
169+ isa = XCBuildConfiguration;
170+ buildSettings = {
171+ ALWAYS_SEARCH_USER_PATHS = NO;
172+ CLANG_ANALYZER_NONNULL = YES;
173+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
174+ CLANG_CXX_LIBRARY = "libc++";
175+ CLANG_ENABLE_MODULES = YES;
176+ CLANG_ENABLE_OBJC_ARC = YES;
177+ CLANG_WARN_BOOL_CONVERSION = YES;
178+ CLANG_WARN_CONSTANT_CONVERSION = YES;
179+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181+ CLANG_WARN_EMPTY_BODY = YES;
182+ CLANG_WARN_ENUM_CONVERSION = YES;
183+ CLANG_WARN_INT_CONVERSION = YES;
184+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
185+ CLANG_WARN_UNREACHABLE_CODE = YES;
186+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
187+ CODE_SIGN_IDENTITY = "-";
188+ COPY_PHASE_STRIP = NO;
189+ DEBUG_INFORMATION_FORMAT = dwarf;
190+ ENABLE_STRICT_OBJC_MSGSEND = YES;
191+ ENABLE_TESTABILITY = YES;
192+ GCC_C_LANGUAGE_STANDARD = gnu99;
193+ GCC_DYNAMIC_NO_PIC = NO;
194+ GCC_NO_COMMON_BLOCKS = YES;
195+ GCC_OPTIMIZATION_LEVEL = 0;
196+ GCC_PREPROCESSOR_DEFINITIONS = (
197+ "DEBUG=1",
198+ "$(inherited)",
199+ );
200+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
201+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
202+ GCC_WARN_UNDECLARED_SELECTOR = YES;
203+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
204+ GCC_WARN_UNUSED_FUNCTION = YES;
205+ GCC_WARN_UNUSED_VARIABLE = YES;
206+ MACOSX_DEPLOYMENT_TARGET = 10.11;
207+ MTL_ENABLE_DEBUG_INFO = YES;
208+ ONLY_ACTIVE_ARCH = YES;
209+ SDKROOT = macosx;
210+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
211+ };
212+ name = Debug;
213+ };
214+ 03D84ABD1D5B0A1000776402 /* Release */ = {
215+ isa = XCBuildConfiguration;
216+ buildSettings = {
217+ ALWAYS_SEARCH_USER_PATHS = NO;
218+ CLANG_ANALYZER_NONNULL = YES;
219+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
220+ CLANG_CXX_LIBRARY = "libc++";
221+ CLANG_ENABLE_MODULES = YES;
222+ CLANG_ENABLE_OBJC_ARC = YES;
223+ CLANG_WARN_BOOL_CONVERSION = YES;
224+ CLANG_WARN_CONSTANT_CONVERSION = YES;
225+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
226+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
227+ CLANG_WARN_EMPTY_BODY = YES;
228+ CLANG_WARN_ENUM_CONVERSION = YES;
229+ CLANG_WARN_INT_CONVERSION = YES;
230+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
231+ CLANG_WARN_UNREACHABLE_CODE = YES;
232+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
233+ CODE_SIGN_IDENTITY = "-";
234+ COPY_PHASE_STRIP = NO;
235+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
236+ ENABLE_NS_ASSERTIONS = NO;
237+ ENABLE_STRICT_OBJC_MSGSEND = YES;
238+ GCC_C_LANGUAGE_STANDARD = gnu99;
239+ GCC_NO_COMMON_BLOCKS = YES;
240+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
241+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
242+ GCC_WARN_UNDECLARED_SELECTOR = YES;
243+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
244+ GCC_WARN_UNUSED_FUNCTION = YES;
245+ GCC_WARN_UNUSED_VARIABLE = YES;
246+ MACOSX_DEPLOYMENT_TARGET = 10.11;
247+ MTL_ENABLE_DEBUG_INFO = NO;
248+ SDKROOT = macosx;
249+ };
250+ name = Release;
251+ };
252+ 03D84ABF1D5B0A1000776402 /* Debug */ = {
253+ isa = XCBuildConfiguration;
254+ buildSettings = {
255+ FRAMEWORK_SEARCH_PATHS = (
256+ "$(inherited)",
257+ "$(PROJECT_DIR)",
258+ );
259+ PRODUCT_NAME = "$(TARGET_NAME)";
260+ SWIFT_VERSION = 3.0;
261+ };
262+ name = Debug;
263+ };
264+ 03D84AC01D5B0A1000776402 /* Release */ = {
265+ isa = XCBuildConfiguration;
266+ buildSettings = {
267+ FRAMEWORK_SEARCH_PATHS = (
268+ "$(inherited)",
269+ "$(PROJECT_DIR)",
270+ );
271+ PRODUCT_NAME = "$(TARGET_NAME)";
272+ SWIFT_VERSION = 3.0;
273+ };
274+ name = Release;
275+ };
276+/* End XCBuildConfiguration section */
277+
278+/* Begin XCConfigurationList section */
279+ 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */ = {
280+ isa = XCConfigurationList;
281+ buildConfigurations = (
282+ 03D84ABC1D5B0A1000776402 /* Debug */,
283+ 03D84ABD1D5B0A1000776402 /* Release */,
284+ );
285+ defaultConfigurationIsVisible = 0;
286+ defaultConfigurationName = Release;
287+ };
288+ 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */ = {
289+ isa = XCConfigurationList;
290+ buildConfigurations = (
291+ 03D84ABF1D5B0A1000776402 /* Debug */,
292+ 03D84AC01D5B0A1000776402 /* Release */,
293+ );
294+ defaultConfigurationIsVisible = 0;
295+ defaultConfigurationName = Release;
296+ };
297+/* End XCConfigurationList section */
298+ };
299+ rootObject = 03D84AAF1D5B0A1000776402 /* Project object */;
300+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:test_Freddy.xcodeproj">
6+ </FileRef>
7+</Workspace>
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/project.xcworkspace/xcshareddata/test_Freddy.xcscmblueprintnew file+30
@@ -0,0 +1,30 @@
1+{
2+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A",
3+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4+
5+ },
6+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : 0,
8+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : 0
9+ },
10+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "7B3DFA55-F9BF-41D8-8FD5-204D3162EB03",
11+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : "dropbox\/JSON\/test_Freddy\/Vendor\/Freddy\/",
13+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : "dropbox\/"
14+ },
15+ "DVTSourceControlWorkspaceBlueprintNameKey" : "test_Freddy",
16+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "JSON\/test_Freddy\/test_Freddy.xcodeproj",
18+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19+ {
20+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/bignerdranch\/Freddy.git",
21+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6ADA245F937D8ED6D99F5A7865E19F5267A169E2"
23+ },
24+ {
25+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "\/Users\/nst\/Dropbox\/dropbox.git",
26+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A"
28+ }
29+ ]
30+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_Freddy.xcschemenew file+91
@@ -0,0 +1,91 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "0800"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
18+ BuildableName = "test_Freddy"
19+ BlueprintName = "test_Freddy"
20+ ReferencedContainer = "container:test_Freddy.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
36+ BuildableName = "test_Freddy"
37+ BlueprintName = "test_Freddy"
38+ ReferencedContainer = "container:test_Freddy.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
59+ BuildableName = "test_Freddy"
60+ BlueprintName = "test_Freddy"
61+ ReferencedContainer = "container:test_Freddy.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ </AdditionalOptions>
66+ </LaunchAction>
67+ <ProfileAction
68+ buildConfiguration = "Release"
69+ shouldUseLaunchSchemeArgsEnv = "YES"
70+ savedToolIdentifier = ""
71+ useCustomWorkingDirectory = "NO"
72+ debugDocumentVersioning = "YES">
73+ <BuildableProductRunnable
74+ runnableDebuggingMode = "0">
75+ <BuildableReference
76+ BuildableIdentifier = "primary"
77+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
78+ BuildableName = "test_Freddy"
79+ BlueprintName = "test_Freddy"
80+ ReferencedContainer = "container:test_Freddy.xcodeproj">
81+ </BuildableReference>
82+ </BuildableProductRunnable>
83+ </ProfileAction>
84+ <AnalyzeAction
85+ buildConfiguration = "Debug">
86+ </AnalyzeAction>
87+ <ArchiveAction
88+ buildConfiguration = "Release"
89+ revealArchiveInOrganizer = "YES">
90+ </ArchiveAction>
91+</Scheme>
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22
@@ -0,0 +1,22 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>SchemeUserState</key>
6+ <dict>
7+ <key>test_Freddy.xcscheme</key>
8+ <dict>
9+ <key>orderHint</key>
10+ <integer>0</integer>
11+ </dict>
12+ </dict>
13+ <key>SuppressBuildableAutocreation</key>
14+ <dict>
15+ <key>03D84AB61D5B0A1000776402</key>
16+ <dict>
17+ <key>primary</key>
18+ <true/>
19+ </dict>
20+ </dict>
21+</dict>
22+</plist>
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSON.swiftnew file+98
@@ -0,0 +1,98 @@
1+//
2+// JSON.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/17/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// An enum to describe the structure of JSON.
10+public enum JSON {
11+ /// A case for denoting an array with an associated value of `[JSON]`
12+ case array([JSON])
13+ /// A case for denoting a dictionary with an associated value of `[Swift.String: JSON]`
14+ case dictionary([String: JSON])
15+ /// A case for denoting a double with an associated value of `Swift.Double`.
16+ case double(Double)
17+ /// A case for denoting an integer with an associated value of `Swift.Int`.
18+ case int(Int)
19+ /// A case for denoting a string with an associated value of `Swift.String`.
20+ case string(String)
21+ /// A case for denoting a boolean with an associated value of `Swift.Bool`.
22+ case bool(Bool)
23+ /// A case for denoting null.
24+ case null
25+}
26+
27+// MARK: - Errors
28+
29+extension JSON {
30+
31+ /// An enum to encapsulate errors that may arise in working with `JSON`.
32+ public enum Error: Swift.Error {
33+ /// The `index` is out of bounds for a JSON array
34+ case indexOutOfBounds(index: Int)
35+
36+ /// The `key` was not found in the JSON dictionary
37+ case keyNotFound(key: String)
38+
39+ /// The JSON is not subscriptable with `type`
40+ case unexpectedSubscript(type: JSONPathType.Type)
41+
42+ /// Unexpected JSON `value` was found that is not convertible `to` type
43+ case valueNotConvertible(value: JSON, to: Any.Type)
44+
45+ /// The JSON is not serializable to a `String`.
46+ case stringSerializationError
47+ }
48+
49+}
50+
51+// MARK: - Test Equality
52+
53+/// Return `true` if `lhs` is equal to `rhs`.
54+public func ==(lhs: JSON, rhs: JSON) -> Bool {
55+ switch (lhs, rhs) {
56+ case (.array(let arrL), .array(let arrR)):
57+ return arrL == arrR
58+ case (.dictionary(let dictL), .dictionary(let dictR)):
59+ return dictL == dictR
60+ case (.string(let strL), .string(let strR)):
61+ return strL == strR
62+ case (.double(let dubL), .double(let dubR)):
63+ return dubL == dubR
64+ case (.double(let dubL), .int(let intR)):
65+ return dubL == Double(intR)
66+ case (.int(let intL), .int(let intR)):
67+ return intL == intR
68+ case (.int(let intL), .double(let dubR)):
69+ return Double(intL) == dubR
70+ case (.bool(let bL), .bool(let bR)):
71+ return bL == bR
72+ case (.null, .null):
73+ return true
74+ default:
75+ return false
76+ }
77+}
78+
79+extension JSON: Equatable {}
80+
81+// MARK: - Printing
82+
83+extension JSON: CustomStringConvertible {
84+
85+ /// A textual representation of `self`.
86+ public var description: Swift.String {
87+ switch self {
88+ case .array(let arr): return String(describing: arr)
89+ case .dictionary(let dict): return String(describing: dict)
90+ case .string(let string): return string
91+ case .double(let double): return String(describing: double)
92+ case .int(let int): return String(describing: int)
93+ case .bool(let bool): return String(describing: bool)
94+ case .null: return "null"
95+ }
96+ }
97+
98+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONDecodable.swiftnew file+186
@@ -0,0 +1,186 @@
1+//
2+// JSONDecodable.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/24/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// A protocol to provide functionality for creating a model object with a `JSON`
10+/// value.
11+public protocol JSONDecodable {
12+
13+ /// Creates an instance of the model with a `JSON` instance.
14+ /// - parameter json: An instance of a `JSON` value from which to
15+ /// construct an instance of the implementing type.
16+ /// - throws: Any `JSON.Error` for errors derived from inspecting the
17+ /// `JSON` value, or any other error involved in decoding.
18+ init(json: JSON) throws
19+
20+}
21+
22+extension Double: JSONDecodable {
23+
24+ /// An initializer to create an instance of `Double` from a `JSON` value.
25+ /// - parameter json: An instance of `JSON`.
26+ /// - throws: The initializer will throw an instance of `JSON.Error` if
27+ /// an instance of `Double` cannot be created from the `JSON` value that was
28+ /// passed to this initializer.
29+ public init(json: JSON) throws {
30+ if case let .double(double) = json {
31+ self = double
32+ } else if case let .int(int) = json {
33+ self = Double(int)
34+ } else if case let .string(string) = json, let s = Double(string) {
35+ self = s
36+ } else {
37+ throw JSON.Error.valueNotConvertible(value: json, to: Double.self)
38+ }
39+ }
40+
41+}
42+
43+extension Int: JSONDecodable {
44+
45+ /// An initializer to create an instance of `Int` from a `JSON` value.
46+ /// - parameter json: An instance of `JSON`.
47+ /// - throws: The initializer will throw an instance of `JSON.Error` if
48+ /// an instance of `Int` cannot be created from the `JSON` value that was
49+ /// passed to this initializer.
50+ public init(json: JSON) throws {
51+
52+ if case let .double(double) = json, double <= Double(Int.max) {
53+ self = Int(double)
54+ } else if case let .int(int) = json {
55+ self = int
56+ } else if case let .string(string) = json, let int = Int(string) {
57+ self = int
58+ } else if case let .string(string) = json,
59+ let double = Double(string),
60+ let decimalSeparator = string.characters.index(of: "."),
61+ let int = Int(String(string.characters.prefix(upTo: decimalSeparator))),
62+ double == Double(int) {
63+ self = int
64+ } else {
65+ throw JSON.Error.valueNotConvertible(value: json, to: Int.self)
66+ }
67+ }
68+
69+}
70+
71+extension String: JSONDecodable {
72+
73+ /// An initializer to create an instance of `String` from a `JSON` value.
74+ /// - parameter json: An instance of `JSON`.
75+ /// - throws: The initializer will throw an instance of `JSON.Error` if
76+ /// an instance of `String` cannot be created from the `JSON` value that was
77+ /// passed to this initializer.
78+ public init(json: JSON) throws {
79+ switch json {
80+ case let .string(string):
81+ self = string
82+ case let .int(int):
83+ self = String(int)
84+ case let .bool(bool):
85+ self = String(bool)
86+ case let .double(double):
87+ self = String(double)
88+ default:
89+ throw JSON.Error.valueNotConvertible(value: json, to: String.self)
90+ }
91+ }
92+
93+}
94+
95+extension Bool: JSONDecodable {
96+
97+ /// An initializer to create an instance of `Bool` from a `JSON` value.
98+ /// - parameter json: An instance of `JSON`.
99+ /// - throws: The initializer will throw an instance of `JSON.Error` if
100+ /// an instance of `Bool` cannot be created from the `JSON` value that was
101+ /// passed to this initializer.
102+ public init(json: JSON) throws {
103+ guard case let .bool(bool) = json else {
104+ throw JSON.Error.valueNotConvertible(value: json, to: Bool.self)
105+ }
106+ self = bool
107+ }
108+
109+}
110+
111+extension RawRepresentable where RawValue: JSONDecodable {
112+
113+ /// An initializer to create an instance of `RawRepresentable` from a `JSON` value.
114+ /// - parameter json: An instance of `JSON`.
115+ /// - throws: The initializer will throw an instance of `JSON.Error` if
116+ /// an instance of `RawRepresentable` cannot be created from the `JSON` value that was
117+ /// passed to this initializer.
118+ public init(json: JSON) throws {
119+ let raw = try json.decode(type: RawValue.self)
120+ guard let value = Self(rawValue: raw) else {
121+ throw JSON.Error.valueNotConvertible(value: json, to: Self.self)
122+ }
123+ self = value
124+ }
125+}
126+
127+internal extension JSON {
128+
129+ /// Retrieves a `[JSON]` from the JSON.
130+ /// - parameter: A `JSON` to be used to create the returned `Array`.
131+ /// - returns: An `Array` of `JSON` elements
132+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
133+ /// - seealso: `JSON.decode(_:type:)`
134+ static func getArray(from json: JSON) throws -> [JSON] {
135+ // Ideally should be expressed as a conditional protocol implementation on Swift.Array.
136+ guard case let .array(array) = json else {
137+ throw Error.valueNotConvertible(value: json, to: Swift.Array<JSON>)
138+ }
139+ return array
140+ }
141+
142+ /// Retrieves a `[String: JSON]` from the JSON.
143+ /// - parameter: A `JSON` to be used to create the returned `Dictionary`.
144+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
145+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
146+ /// - seealso: `JSON.decode(_:type:)`
147+ static func getDictionary(from json: JSON) throws -> [String: JSON] {
148+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
149+ guard case let .dictionary(dictionary) = json else {
150+ throw Error.valueNotConvertible(value: json, to: Swift.Dictionary<String, JSON>)
151+ }
152+ return dictionary
153+ }
154+
155+ /// Attempts to decode many values from a descendant JSON array at a path
156+ /// into JSON.
157+ /// - parameter json: A `JSON` to be used to create the returned `Array` of some type conforming to `JSONDecodable`.
158+ /// - returns: An `Array` of `Decoded` elements.
159+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`, as
160+ /// well as any error that arises from decoding the contained values.
161+ /// - seealso: `JSON.decode(_:type:)`
162+ static func decodedArray<Decoded: JSONDecodable>(from json: JSON) throws -> [Decoded] {
163+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
164+ // This implementation also doesn't do the `type = Type.self` trick.
165+ return try getArray(from: json).map(Decoded.init)
166+ }
167+
168+ /// Attempts to decode many values from a descendant JSON object at a path
169+ /// into JSON.
170+ /// - parameter json: A `JSON` to be used to create the returned `Dictionary` of some type conforming to `JSONDecodable`.
171+ /// - returns: A `Dictionary` of string keys and `Decoded` values.
172+ /// - throws: One of the `JSON.Error` cases thrown by `decode(_:type:)` or
173+ /// any error that arises from decoding the contained values.
174+ /// - seealso: `JSON.decode(_:type:)`
175+ static func decodedDictionary<Decoded: JSONDecodable>(from json: JSON) throws -> [Swift.String: Decoded] {
176+ guard case let .dictionary(dictionary) = json else {
177+ throw Error.valueNotConvertible(value: json, to: Swift.Dictionary<String, Decoded>)
178+ }
179+ var decodedDictionary = Swift.Dictionary<String, Decoded>(minimumCapacity: dictionary.count)
180+ for (key, value) in dictionary {
181+ decodedDictionary[key] = try Decoded(json: value)
182+ }
183+ return decodedDictionary
184+ }
185+
186+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONEncodable.swiftnew file+83
@@ -0,0 +1,83 @@
1+//
2+// JSONEncodable.swift
3+// Freddy
4+//
5+// Created by Matthew Mathias on 1/4/16.
6+// Copyright © 2016 Big Nerd Ranch. All rights reserved.
7+//
8+
9+import Foundation
10+
11+/// A protocol to facilitate encoding and decoding of `JSON`.
12+public protocol JSONEncodable {
13+ /// Converts an instance of a conforming type to `JSON`.
14+ /// - returns: An instance of `JSON`.
15+ /// - Note: If conforming to `JSONEncodable` with a custom type of your own, you should return an instance of
16+ /// `JSON.dictionary`.
17+ func toJSON() -> JSON
18+}
19+
20+extension Array where Element: JSONEncodable {
21+ /// Converts an instance of `Array` whose elements conform to `JSONEncodable` to `JSON`.
22+ /// - returns: An instance of `JSON` where the enum case is `.array`.
23+ public func toJSON() -> JSON {
24+ let arrayOfJSON = self.map { $0.toJSON() }
25+ return .array(arrayOfJSON)
26+ }
27+}
28+
29+extension Dictionary where Value: JSONEncodable {
30+ /// Converts an instance of `Dictionary` whose values conform to `JSONEncodable` to `JSON`. The keys in the resulting
31+ /// `JSON.dictionary` will be of type `String`.
32+ /// - returns: An instance of `JSON` where the enum case is `.dictionary`.
33+ public func toJSON() -> JSON {
34+ var jsonDictionary = [String: JSON]()
35+
36+ for (k, v) in self {
37+ let key = String(describing: k)
38+ jsonDictionary[key] = v.toJSON()
39+ }
40+
41+ return .dictionary(jsonDictionary)
42+ }
43+}
44+
45+extension Int: JSONEncodable {
46+ /// Converts an instance of a conforming type to `JSON`.
47+ /// - returns: An instance of `JSON` where the enum case is `.int`.
48+ public func toJSON() -> JSON {
49+ return .int(self)
50+ }
51+}
52+
53+extension Double: JSONEncodable {
54+ /// Converts an instance of a conforming type to `JSON`.
55+ /// - returns: An instance of `JSON` where the enum case is `.double`.
56+ public func toJSON() -> JSON {
57+ return .double(self)
58+ }
59+}
60+
61+extension String: JSONEncodable {
62+ /// Converts an instance of a conforming type to `JSON`.
63+ /// - returns: An instance of `JSON` where the enum case is `.string`.
64+ public func toJSON() -> JSON {
65+ return .string(self)
66+ }
67+}
68+
69+extension Bool: JSONEncodable {
70+ /// Converts an instance of a conforming type to `JSON`.
71+ /// - returns: An instance of `JSON` where the enum case is `.bool`.
72+ public func toJSON() -> JSON {
73+ return .bool(self)
74+ }
75+}
76+
77+extension RawRepresentable where RawValue: JSONEncodable {
78+ /// Converts an instance of a conforming type to `JSON`.
79+ /// - returns: An instance of `JSON` where the enum case is whatever the underlying `RawValue` converts to.
80+ public func toJSON() -> JSON {
81+ return rawValue.toJSON()
82+ }
83+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONEncodingDetector.swiftnew file+110
@@ -0,0 +1,110 @@
1+//
2+// JSONEncodingDetector.swift
3+// Freddy
4+//
5+// Created by Robert Edwards on 1/27/16.
6+// Copyright © 2016 Big Nerd Ranch. All rights reserved.
7+//
8+
9+/// Struct for attempting to detect the Unicode encoding used with the data supplied to the JSONParser
10+public struct JSONEncodingDetector {
11+
12+ //// The Unicode encodings looked for during detection
13+ public enum Encoding {
14+ //// UTF-8
15+ case utf8
16+ //// UTF-16 Little Endian
17+ case utf16LE
18+ //// UTF-16 Big Endian
19+ case utf16BE
20+ //// UTF-32 Little Endian
21+ case utf32LE
22+ //// UTF-32 Big Endian
23+ case utf32BE
24+ }
25+
26+ //// The Unicode encodings supported by JSONParser.swift
27+ public static let supportedEncodings: [Encoding] = [.utf8]
28+
29+ typealias ByteStreamPrefixInformation = (encoding: Encoding, byteOrderMarkLength: Int)
30+
31+ //// Attempts to detect the Unicode encoding used for a given set of data.
32+ ////
33+ //// This function initially looks for a Byte Order Mark in the following form:
34+ ////
35+ //// Bytes | Encoding Form
36+ //// --------------|----------------
37+ //// 00 00 FE FF | UTF-32, big-endian
38+ //// FF FE 00 00 | UTF-32, little-endian
39+ //// FE FF | UTF-16, big-endian
40+ //// FF FE | UTF-16, little-endian
41+ //// EF BB BF | UTF-8
42+ ////
43+ //// If a BOM is not found then we detect using the following approach described in
44+ //// the JSON RFC http://www.ietf.org/rfc/rfc4627.txt:
45+ ////
46+ //// Since the first two characters of a JSON text will always be ASCII
47+ //// characters [RFC0020], it is possible to determine whether an octet
48+ //// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
49+ //// at the pattern of nulls in the first four octets.
50+ ////
51+ //// 00 00 00 xx UTF-32BE
52+ //// 00 xx 00 xx UTF-16BE
53+ //// xx 00 00 00 UTF-32LE
54+ //// xx 00 xx 00 UTF-16LE
55+ //// xx xx xx xx UTF-8
56+ ////
57+ //// - parameter header: The front Slice of data being read and evaluated.
58+ //// - returns: A tuple containing the detected Unicode encoding and the lenght of the byte order mark.
59+ static func detectEncoding(_ header: RandomAccessSlice<UnsafeBufferPointer<UInt8>>) -> ByteStreamPrefixInformation {
60+
61+ guard let prefix = prefixFromHeader(header) else {
62+ return (.utf8, 0)
63+ }
64+
65+ if let prefixInfo = JSONEncodingDetector.encodingFromBOM(prefix) {
66+ return prefixInfo
67+ } else {
68+ switch prefix {
69+ case(0, 0, 0?, _):
70+ return (.utf32BE, 0)
71+ case(_, 0, 0?, 0?):
72+ return (.utf32LE, 0)
73+ case (0, _, 0?, _), (0, _, _, _):
74+ return (.utf16BE, 0)
75+ case (_, 0, _, 0?), (_, 0, _, _):
76+ return (.utf16LE, 0)
77+ default:
78+ return (.utf8, 0)
79+ }
80+ }
81+ }
82+
83+ private typealias EncodingBytePrefix = (UInt8, UInt8, UInt8?, UInt8?)
84+
85+ private static func prefixFromHeader(_ header: RandomAccessSlice<UnsafeBufferPointer<UInt8>>) -> EncodingBytePrefix? {
86+ if header.count >= 4 {
87+ return(header[0], header[1], header[2], header[3])
88+ } else if header.count >= 2 {
89+ return (header[0], header[1], nil, nil)
90+ }
91+ return nil
92+ }
93+
94+ private static func encodingFromBOM(_ prefix: EncodingBytePrefix) -> ByteStreamPrefixInformation? {
95+ switch prefix {
96+ case(0xFE, 0xFF, _, _):
97+ return (.utf16BE, 2)
98+ case(0x00, 0x00, 0xFE?, 0xFF?):
99+ return (.utf32BE, 4)
100+ case(0xEF, 0xBB, 0xBF?, _):
101+ return (.utf8, 3)
102+ case(0xFF, 0xFE, 0?, 0?):
103+ return (.utf32LE, 4)
104+ case(0xFF, 0xFE, _, _):
105+ return (.utf16LE, 2)
106+ default:
107+ return nil
108+ }
109+ }
110+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONLiteralConvertible.swiftnew file+134
@@ -0,0 +1,134 @@
1+//
2+// JSONLiteralConvertible.swift
3+// Freddy
4+//
5+// Created by Zachary Waldowski on 5/11/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+// MARK: - ArrayLiteralConvertible
10+
11+extension JSON: ExpressibleByArrayLiteral {
12+
13+ /// Create an instance by copying each element of the `collection` into a
14+ /// new `Array`.
15+ public init<Collection: Swift.Collection>(_ collection: Collection) where Collection.Iterator.Element == JSON {
16+ self = .array(Swift.Array(collection))
17+ }
18+
19+ /// Create an instance initialized with `elements`.
20+ public init(arrayLiteral elements: JSON...) {
21+ self.init(elements)
22+ }
23+
24+}
25+
26+// MARK: - DictionaryLiteralConvertible
27+
28+extension JSON: ExpressibleByDictionaryLiteral {
29+
30+ /// Create an instance by copying each key/value pair of the `pairs` into
31+ /// a new `Dictionary`.
32+ public init<Dictionary: Sequence>(_ pairs: Dictionary) where Dictionary.Iterator.Element == (Swift.String, JSON) {
33+ var dictionary = Swift.Dictionary<Swift.String, JSON>(minimumCapacity: pairs.underestimatedCount)
34+ for (key, value) in pairs {
35+ dictionary[key] = value
36+ }
37+ self.init(dictionary)
38+ }
39+
40+ /// Create an instance initialized with `pairs`.
41+ public init(dictionaryLiteral pairs: (Swift.String, JSON)...) {
42+ self.init(pairs)
43+ }
44+
45+ /// Create an instance initialized to `dictionary`.
46+ public init(_ dictionary: Swift.Dictionary<Swift.String, JSON>) {
47+ self = .dictionary(dictionary)
48+ }
49+}
50+
51+// MARK: - FloatLiteralConvertible
52+
53+extension JSON: ExpressibleByFloatLiteral {
54+
55+ /// Create an instance initialized to `Double` `value`.
56+ public init(_ value: Swift.Double) {
57+ self = .double(value)
58+ }
59+
60+ /// Create a literal instance initialized to `value`.
61+ public init(floatLiteral value: Swift.Double) {
62+ self.init(value)
63+ }
64+
65+}
66+
67+// MARK: - IntegerLiteralConvertible
68+
69+extension JSON: ExpressibleByIntegerLiteral {
70+
71+ /// Create an instance initialized to `Int` by `value`.
72+ public init(_ value: Swift.Int) {
73+ self = .int(value)
74+ }
75+
76+ /// Create a literal instance initialized to `value`.
77+ public init(integerLiteral value: Swift.Int) {
78+ self.init(value)
79+ }
80+
81+}
82+
83+// MARK: - StringLiteralConvertible
84+
85+extension JSON: ExpressibleByStringLiteral {
86+
87+ /// Create an instance initialized to `String` by `text`.
88+ public init(_ text: Swift.String) {
89+ self = .string(text)
90+ }
91+
92+ /// Create a literal instance initialized to `value`.
93+ public init(stringLiteral value: StringLiteralType) {
94+ self.init(value)
95+ }
96+
97+ /// Create a literal instance initialized to `value`.
98+ public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
99+ self.init(value)
100+ }
101+
102+ /// Create a literal instance initialized to `value`.
103+ public init(unicodeScalarLiteral value: StringLiteralType) {
104+ self.init(value)
105+ }
106+
107+}
108+
109+// MARK: - BooleanLiteralConvertible
110+
111+extension JSON: ExpressibleByBooleanLiteral {
112+
113+ /// Create an instance initialized to `Bool` by `value`.
114+ public init(_ value: Swift.Bool) {
115+ self = .bool(value)
116+ }
117+
118+ /// Create a literal instance initialized to `value`.
119+ public init(booleanLiteral value: Swift.Bool) {
120+ self.init(value)
121+ }
122+
123+}
124+
125+// MARK: - NilLiteralConvertible
126+
127+extension JSON: ExpressibleByNilLiteral {
128+
129+ /// Create an instance initialized with `nil`.
130+ public init(nilLiteral: ()) {
131+ self = .null
132+ }
133+
134+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONParser.swiftnew file+918
@@ -0,0 +1,918 @@
1+//
2+// JSONParser.swift
3+// Freddy
4+//
5+// Created by John Gallagher on 4/18/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+import Foundation
10+
11+private struct Literal {
12+ static let BACKSLASH = UInt8(ascii: "\\")
13+ static let BACKSPACE = UInt8(ascii: "\u{0008}")
14+ static let COLON = UInt8(ascii: ":")
15+ static let COMMA = UInt8(ascii: ",")
16+ static let DOUBLE_QUOTE = UInt8(ascii: "\"")
17+ static let FORMFEED = UInt8(ascii: "\u{000c}")
18+ static let LEFT_BRACE = UInt8(ascii: "{")
19+ static let LEFT_BRACKET = UInt8(ascii: "[")
20+ static let MINUS = UInt8(ascii: "-")
21+ static let NEWLINE = UInt8(ascii: "\n")
22+ static let PERIOD = UInt8(ascii: ".")
23+ static let PLUS = UInt8(ascii: "+")
24+ static let RETURN = UInt8(ascii: "\r")
25+ static let RIGHT_BRACE = UInt8(ascii: "}")
26+ static let RIGHT_BRACKET = UInt8(ascii: "]")
27+ static let SLASH = UInt8(ascii: "/")
28+ static let SPACE = UInt8(ascii: " ")
29+ static let TAB = UInt8(ascii: "\t")
30+
31+ static let a = UInt8(ascii: "a")
32+ static let b = UInt8(ascii: "b")
33+ static let c = UInt8(ascii: "c")
34+ static let d = UInt8(ascii: "d")
35+ static let e = UInt8(ascii: "e")
36+ static let f = UInt8(ascii: "f")
37+ static let l = UInt8(ascii: "l")
38+ static let n = UInt8(ascii: "n")
39+ static let r = UInt8(ascii: "r")
40+ static let s = UInt8(ascii: "s")
41+ static let t = UInt8(ascii: "t")
42+ static let u = UInt8(ascii: "u")
43+
44+ static let A = UInt8(ascii: "A")
45+ static let B = UInt8(ascii: "B")
46+ static let C = UInt8(ascii: "C")
47+ static let D = UInt8(ascii: "D")
48+ static let E = UInt8(ascii: "E")
49+ static let F = UInt8(ascii: "F")
50+
51+ static let zero = UInt8(ascii: "0")
52+ static let one = UInt8(ascii: "1")
53+ static let two = UInt8(ascii: "2")
54+ static let three = UInt8(ascii: "3")
55+ static let four = UInt8(ascii: "4")
56+ static let five = UInt8(ascii: "5")
57+ static let six = UInt8(ascii: "6")
58+ static let seven = UInt8(ascii: "7")
59+ static let eight = UInt8(ascii: "8")
60+ static let nine = UInt8(ascii: "9")
61+}
62+
63+private let ParserMaximumDepth = 512
64+
65+
66+/// A pure Swift JSON parser. This parser is much faster than the
67+/// `NSJSONSerialization`-based parser (due to the overhead of having to
68+/// dynamically cast the Objective-C objects to determine their type); however,
69+/// it is much newer and has restrictions that the `NSJSONSerialization` parser
70+/// does not. Two restrictions in particular are that it requires UTF-8 data as
71+/// input and it does not allow trailing commas in arrays or dictionaries.
72+public struct JSONParser {
73+
74+ fileprivate enum Sign: Int {
75+ case positive = 1
76+ case negative = -1
77+ }
78+
79+ private let input: UnsafeBufferPointer<UInt8>
80+ private var loc = 0
81+ private var depth = 0
82+
83+ fileprivate init(input: UnsafeBufferPointer<UInt8>) {
84+ self.input = input
85+ }
86+
87+ /// Decode the root element of the `JSON` stream. This may be any fragment
88+ /// or a structural element, per RFC 7159.
89+ ///
90+ /// The beginning bytes are used to determine the stream's encoding.
91+ /// `JSONParser` currently only supports UTF-8 encoding, with or without
92+ /// a byte-order mark.
93+ ///
94+ /// - throws: `JSONParser.Error` for any decoding failures, including a
95+ /// source location if needed.
96+ public mutating func parse() throws -> JSON {
97+ try guardAgainstUnsupportedEncodings()
98+ let value = try parseValue()
99+ skipWhitespace()
100+ guard loc == input.count else {
101+ throw Error.endOfStreamGarbage(offset: loc)
102+ }
103+ return value
104+ }
105+
106+ private mutating func parseValue() throws -> JSON {
107+ guard depth <= ParserMaximumDepth else {
108+ throw Error.exceededNestingLimit(offset: loc)
109+ }
110+
111+ guard input.count > 0 else {
112+ throw Error.endOfStreamUnexpected
113+ }
114+
115+ advancing: while loc < input.count {
116+ do {
117+ switch input[loc] {
118+ case Literal.LEFT_BRACKET:
119+ depth += 1
120+ defer { depth -= 1 }
121+ return try decodeArray()
122+
123+ case Literal.LEFT_BRACE:
124+ depth += 1
125+ defer { depth -= 1 }
126+ return try decodeObject()
127+
128+ case Literal.DOUBLE_QUOTE:
129+ return try decodeString()
130+
131+ case Literal.f:
132+ return try decodeFalse()
133+
134+ case Literal.n:
135+ return try decodeNull()
136+
137+ case Literal.t:
138+ return try decodeTrue()
139+
140+ case Literal.MINUS:
141+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .leadingMinus))
142+
143+ case Literal.zero:
144+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .leadingZero))
145+
146+ case Literal.one...Literal.nine:
147+ return try decodeIntegralValue(NumberParser(loc: loc, input: input, state: .preDecimalDigits))
148+
149+ case Literal.SPACE, Literal.TAB, Literal.RETURN, Literal.NEWLINE:
150+ loc = (loc + 1)
151+
152+ default:
153+ break advancing
154+ }
155+ } catch let InternalError.numberOverflow(offset: start) {
156+ return try decodeNumberAsString(from: start)
157+ }
158+ }
159+ if loc < input.count {
160+ throw Error.valueInvalid(offset: loc, character: UnicodeScalar(input[loc]))
161+ } else {
162+ throw Error.endOfStreamUnexpected
163+ }
164+ }
165+
166+ private mutating func skipWhitespace() {
167+ while loc < input.count {
168+ switch input[loc] {
169+ case Literal.SPACE, Literal.TAB, Literal.RETURN, Literal.NEWLINE:
170+ loc = (loc + 1)
171+ default:
172+ return
173+ }
174+ }
175+ }
176+
177+ private mutating func guardAgainstUnsupportedEncodings() throws {
178+ let header = input.prefix(4)
179+ let encodingPrefixInformation = JSONEncodingDetector.detectEncoding(header)
180+ guard JSONEncodingDetector.supportedEncodings.contains(encodingPrefixInformation.encoding) else {
181+ throw Error.invalidUnicodeStreamEncoding(detectedEncoding: encodingPrefixInformation.encoding)
182+ }
183+ loc = loc.advanced(by: encodingPrefixInformation.byteOrderMarkLength)
184+ }
185+
186+ private mutating func decodeNull() throws -> JSON {
187+ guard input.index(loc, offsetBy: 3, limitedBy: input.count) != input.count else {
188+ throw Error.literalNilMisspelled(offset: loc)
189+ }
190+
191+ if input[loc+1] != Literal.u
192+ || input[loc+2] != Literal.l
193+ || input[loc+3] != Literal.l {
194+ throw Error.literalNilMisspelled(offset: loc)
195+ }
196+
197+ loc += 4
198+ return .null
199+ }
200+
201+ private mutating func decodeTrue() throws -> JSON {
202+ guard input.index(loc, offsetBy: 3, limitedBy: input.count) != input.count else {
203+ throw Error.literalNilMisspelled(offset: loc)
204+ }
205+
206+ if input[loc+1] != Literal.r
207+ || input[loc+2] != Literal.u
208+ || input[loc+3] != Literal.e {
209+ throw Error.literalTrueMisspelled(offset: loc)
210+ }
211+
212+ loc += 4
213+ return .bool(true)
214+ }
215+
216+ private mutating func decodeFalse() throws -> JSON {
217+ guard input.index(loc, offsetBy: 4, limitedBy: input.count) != input.count else {
218+ throw Error.literalNilMisspelled(offset: loc)
219+ }
220+
221+ if input[loc+1] != Literal.a
222+ || input[loc+2] != Literal.l
223+ || input[loc+3] != Literal.s
224+ || input[loc+4] != Literal.e {
225+ throw Error.literalFalseMisspelled(offset: loc)
226+ }
227+
228+ loc += 5
229+ return .bool(false)
230+ }
231+
232+ private var stringDecodingBuffer = [UInt8]()
233+ private mutating func decodeString() throws -> JSON {
234+ loc = (loc + 1)
235+ stringDecodingBuffer.removeAll(keepingCapacity: true)
236+ while loc < input.count {
237+ switch input[loc] {
238+ case Literal.BACKSLASH:
239+ loc = (loc + 1)
240+ switch input[loc] {
241+ case Literal.DOUBLE_QUOTE: stringDecodingBuffer.append(Literal.DOUBLE_QUOTE)
242+ case Literal.BACKSLASH: stringDecodingBuffer.append(Literal.BACKSLASH)
243+ case Literal.SLASH: stringDecodingBuffer.append(Literal.SLASH)
244+ case Literal.b: stringDecodingBuffer.append(Literal.BACKSPACE)
245+ case Literal.f: stringDecodingBuffer.append(Literal.FORMFEED)
246+ case Literal.r: stringDecodingBuffer.append(Literal.RETURN)
247+ case Literal.t: stringDecodingBuffer.append(Literal.TAB)
248+ case Literal.n: stringDecodingBuffer.append(Literal.NEWLINE)
249+ case Literal.u:
250+ loc = (loc + 1)
251+ try readUnicodeEscape(start: loc - 2)
252+
253+ // readUnicodeEscape() advances loc on its own, so we'll `continue` now
254+ // to skip the typical "advance past this character" for all the other escapes
255+ continue
256+
257+ default:
258+ throw Error.controlCharacterUnrecognized(offset: loc)
259+ }
260+ loc = (loc + 1)
261+
262+ case Literal.DOUBLE_QUOTE:
263+ loc = (loc + 1)
264+ stringDecodingBuffer.append(0)
265+
266+ let string = stringDecodingBuffer.withUnsafeBufferPointer {
267+ String(cString: UnsafePointer($0.baseAddress!))
268+ }
269+
270+ return .string(string)
271+
272+ case let other:
273+ stringDecodingBuffer.append(other)
274+ loc = (loc + 1)
275+ }
276+ }
277+
278+ throw Error.endOfStreamUnexpected
279+ }
280+
281+ private mutating func readCodeUnit() -> UInt16? {
282+ guard loc + 4 <= input.count else {
283+ return nil
284+ }
285+ var codeUnit: UInt16 = 0
286+ for c in input[loc..<loc+4] {
287+ let nibble: UInt16
288+
289+ switch c {
290+ case Literal.zero...Literal.nine:
291+ nibble = UInt16(c - Literal.zero)
292+
293+ case Literal.a...Literal.f:
294+ nibble = 10 + UInt16(c - Literal.a)
295+
296+ case Literal.A...Literal.F:
297+ nibble = 10 + UInt16(c - Literal.A)
298+
299+ default:
300+ return nil
301+ }
302+ codeUnit = (codeUnit << 4) | nibble
303+ }
304+ loc += 4
305+ return codeUnit
306+ }
307+
308+ private mutating func readUnicodeEscape(start: Int) throws {
309+ guard let codeUnit = readCodeUnit() else {
310+ throw Error.unicodeEscapeInvalid(offset: start)
311+ }
312+
313+ let codeUnits: [UInt16]
314+
315+ if UTF16.isLeadSurrogate(codeUnit) {
316+ // First half of a UTF16 surrogate pair - we must parse another code unit and combine them
317+
318+ // First confirm and skip over that we have another "\u"
319+ guard loc + 6 <= input.count && input[loc] == Literal.BACKSLASH && input[loc+1] == Literal.u else {
320+ throw Error.unicodeEscapeInvalid(offset: start)
321+ }
322+ loc += 2
323+
324+ // Ensure the second code unit is valid for the surrogate pair
325+ guard let secondCodeUnit = readCodeUnit(), UTF16.isTrailSurrogate(secondCodeUnit) else {
326+ throw Error.unicodeEscapeInvalid(offset: start)
327+ }
328+
329+ codeUnits = [codeUnit, secondCodeUnit]
330+ } else {
331+ codeUnits = [codeUnit]
332+ }
333+
334+ let transcodeHadError = transcode(codeUnits.makeIterator(), from: UTF16.self, to: UTF8.self, stoppingOnError: true) { (outputEncodingCodeUnit) in
335+ self.stringDecodingBuffer.append(outputEncodingCodeUnit)
336+ }
337+
338+ if transcodeHadError {
339+ throw Error.unicodeEscapeInvalid(offset: start)
340+ }
341+ }
342+
343+ private mutating func decodeArray() throws -> JSON {
344+ let start = loc
345+ loc = (loc + 1)
346+ var items = [JSON]()
347+
348+ while loc < input.count {
349+ skipWhitespace()
350+
351+ if loc < input.count && input[loc] == Literal.RIGHT_BRACKET {
352+ loc = (loc + 1)
353+ return .array(items)
354+ }
355+
356+ if !items.isEmpty {
357+ guard loc < input.count && input[loc] == Literal.COMMA else {
358+ throw Error.collectionMissingSeparator(offset: start)
359+ }
360+ loc = (loc + 1)
361+ }
362+
363+ items.append(try parseValue())
364+ }
365+
366+ throw Error.endOfStreamUnexpected
367+ }
368+
369+ // Decoding objects can be recursive, so we have to keep more than one
370+ // buffer around for building up key/value pairs (to reduce allocations
371+ // when parsing large JSON documents).
372+ //
373+ // Rough estimate of the difference between this and using a fresh
374+ // [(String,JSON)] for the `pairs` variable in decodeObject() below is
375+ // about 12% on an iPhone 5.
376+ private struct DecodeObjectBuffers {
377+ var buffers = [[(String,JSON)]]()
378+
379+ mutating func getBuffer() -> [(String,JSON)] {
380+ if !buffers.isEmpty {
381+ var buffer = buffers.removeLast()
382+ buffer.removeAll(keepingCapacity: true)
383+ return buffer
384+ }
385+ return [(String,JSON)]()
386+ }
387+
388+ mutating func putBuffer(_ buffer: [(String,JSON)]) {
389+ buffers.append(buffer)
390+ }
391+ }
392+
393+ private var decodeObjectBuffers = DecodeObjectBuffers()
394+
395+ private mutating func decodeObject() throws -> JSON {
396+ let start = loc
397+ loc = (loc + 1)
398+ var pairs = decodeObjectBuffers.getBuffer()
399+
400+ while loc < input.count {
401+ skipWhitespace()
402+
403+ if loc < input.count && input[loc] == Literal.RIGHT_BRACE {
404+ loc = (loc + 1)
405+ var obj = [String:JSON](minimumCapacity: pairs.count)
406+ for (k, v) in pairs {
407+ obj[k] = v
408+ }
409+ decodeObjectBuffers.putBuffer(pairs)
410+ return .dictionary(obj)
411+ }
412+
413+ if !pairs.isEmpty {
414+ guard loc < input.count && input[loc] == Literal.COMMA else {
415+ throw Error.collectionMissingSeparator(offset: start)
416+ }
417+ loc = (loc + 1)
418+
419+ skipWhitespace()
420+ }
421+
422+ guard loc < input.count && input[loc] == Literal.DOUBLE_QUOTE else {
423+ throw Error.dictionaryMissingKey(offset: start)
424+ }
425+
426+ let key = try decodeString().getString()
427+ skipWhitespace()
428+
429+ guard loc < input.count && input[loc] == Literal.COLON else {
430+ throw Error.collectionMissingSeparator(offset: start)
431+ }
432+ loc = (loc + 1)
433+
434+ pairs.append((key, try parseValue()))
435+ }
436+
437+ throw Error.endOfStreamUnexpected
438+ }
439+
440+ private mutating func decodeIntegralValue(_ parser: NumberParser) throws -> JSON {
441+ var sign = Sign.positive
442+ var parser = parser
443+ var value = 0
444+
445+ // This would be more natural as `while true { ... }` with a meaningful .Done case,
446+ // but that causes compile time explosion in Swift 2.2. :-|
447+ while parser.state != .done {
448+ switch parser.state {
449+ case .leadingMinus:
450+ sign = .negative
451+ try parser.parseNegative()
452+
453+ case .leadingZero:
454+ parser.parseLeadingZero()
455+
456+ case .preDecimalDigits:
457+ try parser.parsePreDecimalDigits { c in
458+ guard case let (exponent, false) = Int.multiplyWithOverflow(10, value) else {
459+ throw InternalError.numberOverflow(offset: parser.start)
460+ }
461+
462+ guard case let (newValue, false) = Int.addWithOverflow(exponent, Int(c - Literal.zero)) else {
463+ throw InternalError.numberOverflow(offset: parser.start)
464+ }
465+
466+ value = newValue
467+ }
468+
469+ case .decimal, .exponent:
470+ return try detectingFloatingPointErrors(start: parser.start) {
471+ try decodeFloatingPointValue(parser, sign: sign, value: Double(value))
472+ }
473+
474+ case .postDecimalDigits, .exponentSign, .exponentDigits:
475+ assertionFailure("Invalid internal state while parsing number")
476+
477+ case .done:
478+ fatalError("impossible condition")
479+ }
480+ }
481+
482+ guard case let (signedValue, false) = Int.multiplyWithOverflow(sign.rawValue, value) else {
483+ throw InternalError.numberOverflow(offset: parser.start)
484+ }
485+
486+ loc = parser.loc
487+ return .int(signedValue)
488+ }
489+
490+ private mutating func decodeFloatingPointValue(_ parser: NumberParser, sign: Sign, value: Double) throws -> JSON {
491+ var parser = parser
492+ var value = value
493+ var exponentSign = Sign.positive
494+ var exponent = Double(0)
495+ var position = 0.1
496+
497+ // This would be more natural as `while true { ... }` with a meaningful .Done case,
498+ // but that causes compile time explosion in Swift 2.2. :-|
499+ while parser.state != .done {
500+ switch parser.state {
501+ case .leadingMinus, .leadingZero, .preDecimalDigits:
502+ assertionFailure("Invalid internal state while parsing number")
503+
504+ case .decimal:
505+ try parser.parseDecimal()
506+
507+ case .postDecimalDigits:
508+ parser.parsePostDecimalDigits { c in
509+ value += position * Double(c - Literal.zero)
510+ position /= 10
511+ }
512+
513+ case .exponent:
514+ exponentSign = try parser.parseExponent()
515+
516+ case .exponentSign:
517+ try parser.parseExponentSign()
518+
519+ case .exponentDigits:
520+ parser.parseExponentDigits { c in
521+ exponent = exponent * 10 + Double(c - Literal.zero)
522+ }
523+
524+ case .done:
525+ fatalError("impossible condition")
526+ }
527+ }
528+
529+ loc = parser.loc
530+ return .double(Double(sign.rawValue) * value * pow(10, Double(exponentSign.rawValue) * exponent))
531+ }
532+
533+
534+ private mutating func decodeNumberAsString(from position: Int) throws -> JSON {
535+ var parser: NumberParser = {
536+ let state: NumberParser.State
537+ switch input[position] {
538+ case Literal.MINUS: state = .leadingMinus
539+ case Literal.zero: state = .leadingZero
540+ case Literal.one...Literal.nine: state = .preDecimalDigits
541+ default:
542+ fatalError("Internal error: decodeNumber called on not-a-number")
543+ }
544+ return NumberParser(loc: position, input: input, state: state)
545+ }()
546+
547+ stringDecodingBuffer.removeAll(keepingCapacity: true)
548+
549+ while true {
550+ switch parser.state {
551+ case .leadingMinus:
552+ try parser.parseNegative()
553+ stringDecodingBuffer.append(Literal.MINUS)
554+
555+ case .leadingZero:
556+ parser.parseLeadingZero()
557+ stringDecodingBuffer.append(Literal.zero)
558+
559+ case .preDecimalDigits:
560+ parser.parsePreDecimalDigits { stringDecodingBuffer.append($0) }
561+
562+ case .decimal:
563+ try parser.parseDecimal()
564+ stringDecodingBuffer.append(Literal.PERIOD)
565+
566+ case .postDecimalDigits:
567+ parser.parsePostDecimalDigits { stringDecodingBuffer.append($0) }
568+
569+ case .exponent:
570+ stringDecodingBuffer.append(input[parser.loc])
571+ _ = try parser.parseExponent()
572+
573+ case .exponentSign:
574+ stringDecodingBuffer.append(input[parser.loc])
575+ try parser.parseExponentSign()
576+
577+ case .exponentDigits:
578+ parser.parseExponentDigits { stringDecodingBuffer.append($0) }
579+
580+ case .done:
581+ stringDecodingBuffer.append(0)
582+ let string = stringDecodingBuffer.withUnsafeBufferPointer {
583+ String(cString: UnsafePointer($0.baseAddress!))
584+ }
585+
586+ loc = parser.loc
587+ return .string(string)
588+ }
589+ }
590+ }
591+
592+ private func detectingFloatingPointErrors<T>(start loc: Int, _ f: () throws -> T) throws -> T {
593+ let flags = FE_UNDERFLOW | FE_OVERFLOW
594+ feclearexcept(flags)
595+ let value = try f()
596+ guard fetestexcept(flags) == 0 else {
597+ throw InternalError.numberOverflow(offset: loc)
598+ }
599+ return value
600+ }
601+}
602+
603+private struct NumberParser {
604+ enum State {
605+ case leadingMinus
606+ case leadingZero
607+ case preDecimalDigits
608+ case decimal
609+ case postDecimalDigits
610+ case exponent
611+ case exponentSign
612+ case exponentDigits
613+ case done
614+ }
615+
616+ let start: Int
617+ var loc = 0
618+ var state: State
619+ let input: UnsafeBufferPointer<UInt8>
620+
621+ init(loc: Int, input: UnsafeBufferPointer<UInt8>, state: State) {
622+ assert(loc < input.count, "Invalid input to NumberParser")
623+ self.start = loc
624+ self.loc = loc
625+ self.input = input
626+ self.state = state
627+ }
628+
629+ mutating func parseNegative() throws {
630+ assert(state == .leadingMinus, "Unexpected state entering parseNegative")
631+
632+ loc = (loc + 1)
633+ guard loc < input.count else {
634+ throw JSONParser.Error.endOfStreamUnexpected
635+ }
636+
637+ switch input[loc] {
638+ case Literal.zero:
639+ state = .leadingZero
640+
641+ case Literal.one...Literal.nine:
642+ state = .preDecimalDigits
643+
644+ default:
645+ throw JSONParser.Error.numberSymbolMissingDigits(offset: start)
646+ }
647+ }
648+
649+ mutating func parseLeadingZero() {
650+ assert(state == .leadingZero, "Unexpected state entering parseLeadingZero")
651+
652+ loc = (loc + 1)
653+ guard loc < input.count else {
654+ state = .done
655+ return
656+ }
657+
658+ switch input[loc] {
659+ case Literal.PERIOD:
660+ state = .decimal
661+
662+ case Literal.e, Literal.E:
663+ state = .exponent
664+
665+ default:
666+ state = .done
667+ }
668+ }
669+
670+ mutating func parsePreDecimalDigits(f: (UInt8) throws -> Void) rethrows {
671+ assert(state == .preDecimalDigits, "Unexpected state entering parsePreDecimalDigits")
672+ advancing: while loc < input.count {
673+ let c = input[loc]
674+ switch c {
675+ case Literal.zero...Literal.nine:
676+ try f(c)
677+ loc = (loc + 1)
678+
679+ case Literal.PERIOD:
680+ state = .decimal
681+ return
682+
683+ case Literal.e, Literal.E:
684+ state = .exponent
685+ return
686+
687+ default:
688+ break advancing
689+ }
690+ }
691+
692+ state = .done
693+ }
694+
695+ mutating func parseDecimal() throws {
696+ assert(state == .decimal, "Unexpected state entering parseDecimal")
697+ loc = (loc + 1)
698+ guard loc < input.count else {
699+ throw JSONParser.Error.endOfStreamUnexpected
700+ }
701+
702+ switch input[loc] {
703+ case Literal.zero...Literal.nine:
704+ state = .postDecimalDigits
705+
706+ default:
707+ throw JSONParser.Error.numberMissingFractionalDigits(offset: start)
708+ }
709+ }
710+
711+ mutating func parsePostDecimalDigits(f: (UInt8) throws -> Void) rethrows {
712+ assert(state == .postDecimalDigits, "Unexpected state entering parsePostDecimalDigits")
713+
714+ advancing: while loc < input.count {
715+ let c = input[loc]
716+ switch c {
717+ case Literal.zero...Literal.nine:
718+ try f(c)
719+ loc = (loc + 1)
720+
721+ case Literal.e, Literal.E:
722+ state = .exponent
723+ return
724+
725+ default:
726+ break advancing
727+ }
728+ }
729+
730+ state = .done
731+ }
732+
733+ mutating func parseExponent() throws -> JSONParser.Sign {
734+ assert(state == .exponent, "Unexpected state entering parseExponent")
735+
736+ loc = (loc + 1)
737+ guard loc < input.count else {
738+ throw JSONParser.Error.endOfStreamUnexpected
739+ }
740+
741+ switch input[loc] {
742+ case Literal.zero...Literal.nine:
743+ state = .exponentDigits
744+
745+ case Literal.PLUS:
746+ state = .exponentSign
747+
748+ case Literal.MINUS:
749+ state = .exponentSign
750+ return .negative
751+
752+ default:
753+ throw JSONParser.Error.numberSymbolMissingDigits(offset: start)
754+ }
755+
756+ return .positive
757+ }
758+
759+ mutating func parseExponentSign() throws {
760+ assert(state == .exponentSign, "Unexpected state entering parseExponentSign")
761+ loc = (loc + 1)
762+ guard loc < input.count else {
763+ throw JSONParser.Error.endOfStreamUnexpected
764+ }
765+
766+ switch input[loc] {
767+ case Literal.zero...Literal.nine:
768+ state = .exponentDigits
769+
770+ default:
771+ throw JSONParser.Error.numberSymbolMissingDigits(offset: start)
772+ }
773+ }
774+
775+ mutating func parseExponentDigits(f: (UInt8) throws -> Void) rethrows {
776+ assert(state == .exponentDigits, "Unexpected state entering parseExponentDigits")
777+ advancing: while loc < input.count {
778+ let c = input[loc]
779+ switch c {
780+ case Literal.zero...Literal.nine:
781+ try f(c)
782+ loc = (loc + 1)
783+
784+ default:
785+ break advancing
786+ }
787+ }
788+
789+ state = .done
790+ }
791+}
792+
793+public extension JSONParser {
794+
795+ /// Creates a `JSONParser` ready to parse UTF-8 encoded `Data`.
796+ ///
797+ /// If the data is mutable, it is copied before parsing. The data's lifetime
798+ /// is extended for the duration of parsing.
799+ @available(*, unavailable, message: "Replaced with parse(utf8:)")
800+ init(utf8Data inData: Data) {
801+ fatalError("unavailable code cannot be executed")
802+ }
803+
804+ /// Creates a `JSONParser` from the code units represented by the `string`.
805+ ///
806+ /// The synthesized string is lifetime-extended for the duration of parsing.
807+ @available(*, unavailable, message: "Replaced with parse(utf8:)")
808+ init(string: String) {
809+ fatalError("unavailable code cannot be executed")
810+ }
811+
812+ /// Creates an instance of `JSON` from UTF-8 encoded `data`.
813+ static func parse(utf8 data: Data) throws -> JSON {
814+ return try data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> JSON in
815+ let buffer = UnsafeBufferPointer(start: ptr, count: data.count)
816+ var parser = JSONParser(input: buffer)
817+ return try parser.parse()
818+ }
819+ }
820+
821+ /// Creates an instance of `JSON` from `string`.
822+ static func parse(_ string: String) throws -> JSON {
823+ return try string.utf8CString.withUnsafeBufferPointer { (nulTerminatedBuffer) throws -> JSON in
824+ return try nulTerminatedBuffer.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: nulTerminatedBuffer.count) { (utf8Base) throws -> JSON in
825+ // don't want to include the nul termination in the buffer - trim it off
826+ let buffer = UnsafeBufferPointer(start: utf8Base, count: nulTerminatedBuffer.count - 1)
827+ var parser = JSONParser(input: buffer)
828+ return try parser.parse()
829+ }
830+ }
831+ }
832+
833+}
834+
835+extension JSONParser: JSONParserType {
836+
837+ /// Creates an instance of `JSON` from UTF-8 encoded `Data`.
838+ /// - parameter data: An instance of `Data` to parse `JSON` from.
839+ /// - throws: Any `JSONParser.Error` that arises during decoding.
840+ /// - seealso: JSONParser.parse()
841+ public static func createJSON(from data: Data) throws -> JSON {
842+ return try parse(utf8: data)
843+ }
844+
845+}
846+
847+// MARK: - Errors
848+
849+extension JSONParser {
850+
851+ /// Enumeration describing possible errors that occur while parsing a JSON
852+ /// document. Most errors include an associated `offset`, representing the
853+ /// offset into the UTF-8 characters making up the document where the error
854+ /// occurred.
855+ public enum Error: Swift.Error {
856+ /// The parser ran out of data prematurely. This usually means a value
857+ /// was not escaped, such as a string literal not ending with a double
858+ /// quote.
859+ case endOfStreamUnexpected
860+
861+ /// Unexpected non-whitespace data was left around `offset` after
862+ /// parsing all valid JSON.
863+ case endOfStreamGarbage(offset: Int)
864+
865+ /// Too many nested objects or arrays occured at the literal started
866+ /// around `offset`.
867+ case exceededNestingLimit(offset: Int)
868+
869+ /// A `character` was not a valid start of a value around `offset`.
870+ case valueInvalid(offset: Int, character: UnicodeScalar)
871+
872+ /// Badly-formed Unicode escape sequence at `offset`. A Unicode escape
873+ /// uses the text "\u" followed by 4 hex digits, such as "\uF09F\uA684"
874+ /// to represent U+1F984, "UNICORN FACE".
875+ case unicodeEscapeInvalid(offset: Int)
876+
877+ /// Badly-formed control character around `offset`. JSON supports
878+ /// backslash-escaped double quotes, slashes, whitespace control codes,
879+ /// and Unicode escape sequences.
880+ case controlCharacterUnrecognized(offset: Int)
881+
882+ /// Invalid token, expected `null` around `offset`
883+ case literalNilMisspelled(offset: Int)
884+
885+ /// Invalid token, expected `true` around `offset`
886+ case literalTrueMisspelled(offset: Int)
887+
888+ /// Invalid token, expected `false` around `offset`
889+ case literalFalseMisspelled(offset: Int)
890+
891+ /// Badly-formed collection at given `offset`, expected `,` or `:`
892+ case collectionMissingSeparator(offset: Int)
893+
894+ /// While parsing an object literal, a value was found without a key
895+ /// around `offset`. The start of a string literal was expected.
896+ case dictionaryMissingKey(offset: Int)
897+
898+ /// Badly-formed number with no digits around `offset`. After a decimal
899+ /// point, a number must include some number of digits.
900+ case numberMissingFractionalDigits(offset: Int)
901+
902+ /// Badly-formed number with symbols ("-" or "e") but no following
903+ /// digits around `offset`.
904+ case numberSymbolMissingDigits(offset: Int)
905+
906+ /// Supplied data is encoded in an unsupported format.
907+ case invalidUnicodeStreamEncoding(detectedEncoding: JSONEncodingDetector.Encoding)
908+ }
909+
910+ fileprivate enum InternalError: Swift.Error {
911+ /// Attempted to parse an integer outside the range of [Int.min, Int.max]
912+ /// or a double outside the range of representable doubles. Note that
913+ /// for doubles, this could be an overflow or an underflow - we don't
914+ /// get enough information from Swift here to know which it is. The number
915+ /// causing the overflow/underflow began at `offset`.
916+ case numberOverflow(offset: Int)
917+ }
918+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONParsing.swiftnew file+126
@@ -0,0 +1,126 @@
1+//
2+// JSONParsing.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/17/15.
6+// Copyright © 2015 Big Nerd Ranch. All rights reserved.
7+//
8+
9+import Foundation
10+
11+// MARK: - Deserialize JSON
12+
13+/// Protocol describing a backend parser that can produce `JSON` from `Data`.
14+public protocol JSONParserType {
15+
16+ /// Creates an instance of `JSON` from `Data`.
17+ /// - parameter data: An instance of `Data` to use to create `JSON`.
18+ /// - throws: An error that may arise from calling `JSONObjectWithData(_:options:)` on `NSJSONSerialization` with the given data.
19+ /// - returns: An instance of `JSON`.
20+ static func createJSON(from data: Data) throws -> JSON
21+
22+}
23+
24+extension JSON {
25+
26+ /// Create `JSON` from UTF-8 `data`. By default, parses using the
27+ /// Swift-native `JSONParser` backend.
28+ public init(data: Data, usingParser parser: JSONParserType.Type = JSONParser.self) throws {
29+ self = try parser.createJSON(from: data)
30+ }
31+
32+ /// Create `JSON` from UTF-8 `string`.
33+ public init(jsonString: Swift.String, usingParser parser: JSONParserType.Type = JSONParser.self) throws {
34+ self = try parser.createJSON(from: jsonString.data(using: Swift.String.Encoding.utf8) ?? Data())
35+ }
36+}
37+
38+// MARK: - NSJSONSerialization
39+
40+extension JSONSerialization: JSONParserType {
41+
42+ // MARK: Decode Data
43+
44+ /// Use the built-in, Objective-C based JSON parser to create `JSON`.
45+ /// - parameter data: An instance of `Data`.
46+ /// - returns: An instance of `JSON`.
47+ /// - throws: An error that may arise if the `Data` cannot be parsed into an object.
48+ public static func createJSON(from data: Data) throws -> JSON {
49+ return makeJSON(with: try JSONSerialization.jsonObject(with: data, options: []))
50+ }
51+
52+ // MARK: Make JSON
53+
54+ /// Makes a `JSON` object by matching its argument to a case in the `JSON` enum.
55+ /// - parameter object: The instance of `Any` returned from serializing the JSON.
56+ /// - returns: An instance of `JSON` matching the JSON given to the function.
57+ private static func makeJSON(with object: Any) -> JSON {
58+ switch object {
59+ case let n as NSNumber:
60+ let numberType = CFNumberGetType(n)
61+ switch numberType {
62+ case .charType:
63+ return .bool(n.boolValue)
64+
65+ case .shortType, .intType, .longType, .cfIndexType, .nsIntegerType, .sInt8Type, .sInt16Type, .sInt32Type:
66+ return .int(n.intValue)
67+
68+ case .sInt64Type, .longLongType /* overflows 32-bit Int */:
69+ #if /* 32-bit arch */ arch(arm) || arch(i386)
70+ // Why double, when the Freddy parser would bump to String?
71+ //
72+ // Returning Double avoids making the type depend on whether you're running
73+ // 32-bit or 64-bit code when using the NSJSONSerialization parser.
74+ // NSJSONSerialization appears to bump numbers larger than Int.max to Double on
75+ // 64-bit platforms but use .SInt64Type on 32-bit platforms.
76+ // If we returned a String here, you'd get a String value on 32-bit,
77+ // but a Double value on 64-bit. Instead, we return Double.
78+ //
79+ // This means that, if you switch parsers,
80+ // you'll have to switch from .double to .string for pulling out
81+ // overflowing values, but if you stick with a single parser,
82+ // you at least won't have architecture-dependent lookups!
83+ return .double(n.doubleValue)
84+ #else
85+ return .int(n.intValue)
86+ #endif
87+
88+ case .float32Type, .float64Type, .floatType, .doubleType, .cgFloatType:
89+ return .double(n.doubleValue)
90+ }
91+
92+ case let arr as [Any]:
93+ return makeJSONArray(arr)
94+
95+ case let dict as [Swift.String: Any]:
96+ return makeJSONDictionary(dict)
97+
98+ case let s as Swift.String:
99+ return .string(s)
100+
101+ default:
102+ return .null
103+ }
104+ }
105+
106+ // MARK: Make a JSON Array
107+
108+ /// Makes a `JSON` array from the object passed in.
109+ /// - parameter jsonArray: The array to transform into a `JSON`.
110+ /// - returns: An instance of `JSON` matching the array.
111+ private static func makeJSONArray(_ jsonArray: [Any]) -> JSON {
112+ return .array(jsonArray.map(makeJSON))
113+ }
114+
115+ // MARK: Make a JSON Dictionary
116+
117+ /// Makes a `JSON` dictionary from the Cocoa dictionary passed in.
118+ /// - parameter jsonDict: The dictionary to transform into `JSON`.
119+ /// - returns: An instance of `JSON` matching the dictionary.
120+ private static func makeJSONDictionary(_ jsonDict: [Swift.String: Any]) -> JSON {
121+ return JSON(jsonDict.lazy.map { (key, value) in
122+ (key, makeJSON(with: value))
123+ })
124+ }
125+
126+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONSerializing.swiftnew file+54
@@ -0,0 +1,54 @@
1+// Copyright (C) 2016 Big Nerd Ranch, Inc. Licensed under the MIT license WITHOUT ANY WARRANTY.
2+
3+import Foundation
4+
5+// MARK: - Serialize JSON
6+
7+extension JSON {
8+
9+ /// Attempt to serialize `JSON` into an `Data`.
10+ /// - returns: A byte-stream containing the `JSON` ready for wire transfer.
11+ /// - throws: Errors that arise from `JSONSerialization`.
12+ /// - see: Foundation.JSONSerialization
13+ public func serialize() throws -> Data {
14+ return try JSONSerialization.data(withJSONObject: toJSONSerializationValue(), options: [])
15+ }
16+
17+ /// Attempt to serialize `JSON` into a `String`.
18+ /// - returns: A `String` containing the `JSON`.
19+ /// - throws: A `JSON.Error.StringSerializationError` or errors that arise from `JSONSerialization`.
20+ /// - see: Foundation.JSONSerialization
21+ public func serializeString() throws -> String {
22+ let data = try self.serialize()
23+ guard let json = String(data: data, encoding: String.Encoding.utf8) else {
24+ throw Error.stringSerializationError
25+ }
26+ return json
27+ }
28+
29+ /// A function to help with the serialization of `JSON`.
30+ /// - returns: An `Any` suitable for `JSONSerialization`'s use.
31+ private func toJSONSerializationValue() -> Any {
32+ switch self {
33+ case .array(let jsonArray):
34+ return jsonArray.map { $0.toJSONSerializationValue() }
35+ case .dictionary(let jsonDictionary):
36+ var cocoaDictionary = Swift.Dictionary<Swift.String, Any>(minimumCapacity: jsonDictionary.count)
37+ for (key, json) in jsonDictionary {
38+ cocoaDictionary[key] = json.toJSONSerializationValue()
39+ }
40+ return cocoaDictionary
41+ case .string(let str):
42+ return str
43+ case .double(let num):
44+ return NSNumber(value: num)
45+ case .int(let int):
46+ return NSNumber(value: int)
47+ case .bool(let b):
48+ return NSNumber(value: b)
49+ case .null:
50+ return NSNull()
51+ }
52+
53+ }
54+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONSubscripting.swiftnew file+613
@@ -0,0 +1,613 @@
1+//
2+// JSONSubscripting.swift
3+// Freddy
4+//
5+// Created by Zachary Waldowski on 8/15/15.
6+// Copyright © 2015 Big Nerd Ranch. All rights reserved.
7+//
8+
9+// MARK: JSONPathType
10+
11+/// A protocol used to define a path within an instance of `JSON` that leads to some desired value.
12+///
13+/// A custom type, such as a `RawRepresentable` enum, may be made to conform to `JSONPathType`
14+/// and used with the subscript APIs.
15+public protocol JSONPathType {
16+ /// Use `self` to key into a `dictionary`.
17+ ///
18+ /// Unlike Swift dictionaries, failing to find a value for a key should throw
19+ /// an error rather than convert to `nil`.
20+ ///
21+ /// Upon failure, implementers should throw an error from `JSON.Error`.
22+ func value(in dictionary: [String : JSON]) throws -> JSON
23+
24+ /// Use `self` to index into an `array`.
25+ ///
26+ /// Unlike Swift arrays, attempting to index outside the collection's bounds
27+ /// should throw an error rather than crash.
28+ ///
29+ /// Upon failure, implementers should throw an error from `JSON.Error`.
30+ func value(in array: [JSON]) throws -> JSON
31+}
32+
33+extension JSONPathType {
34+
35+ /// The default behavior for keying into a dictionary is to throw
36+ /// `JSON.Error.UnexpectedSubscript`.
37+ public func value(in dictionary: [String : JSON]) throws -> JSON {
38+ throw JSON.Error.unexpectedSubscript(type: Self.self)
39+ }
40+
41+ /// The default behavior for indexing into an array is to throw
42+ /// `JSON.Error.UnexpectedSubscript`.
43+ public func value(in array: [JSON]) throws -> JSON {
44+ throw JSON.Error.unexpectedSubscript(type: Self.self)
45+ }
46+
47+}
48+
49+extension String: JSONPathType {
50+
51+ /// A method used to retrieve a value from a given dictionary for a specific key.
52+ /// - parameter dictionary: A `Dictionary` with `String` keys and `JSON` values.
53+ /// - throws: `.KeyNotFound` with an associated value of `self`, where `self` is a `String`,
54+ /// should the key not be present within the `JSON`.
55+ /// - returns: The `JSON` value associated with the given key.
56+ public func value(in dictionary: [String : JSON]) throws -> JSON {
57+ guard let next = dictionary[self] else {
58+ throw JSON.Error.keyNotFound(key: self)
59+ }
60+ return next
61+ }
62+
63+}
64+
65+extension Int: JSONPathType {
66+
67+ /// A method used to retrieve a value from a given array for a specific index.
68+ /// - parameter array: An `Array` of `JSON`.
69+ /// - throws: `.IndexOutOfBounds` with an associated value of `self`, where `self` is an `Int`,
70+ /// should the index not be within the valid range for the array of `JSON`.
71+ /// - returns: The `JSON` value found at the given index.
72+ public func value(in array: [JSON]) throws -> JSON {
73+ guard case array.indices = self else {
74+ throw JSON.Error.indexOutOfBounds(index: self)
75+ }
76+ return array[self]
77+ }
78+
79+}
80+
81+// MARK: - Subscripting core
82+
83+private extension JSON {
84+
85+ enum SubscriptError: Swift.Error {
86+ case subscriptIntoNull(JSONPathType)
87+ }
88+
89+ func value(for pathFragment: JSONPathType, detectingNull: Bool) throws -> JSON {
90+ switch self {
91+ case .null where detectingNull:
92+ throw SubscriptError.subscriptIntoNull(pathFragment)
93+ case let .dictionary(dict):
94+ return try pathFragment.value(in: dict)
95+ case let .array(array):
96+ return try pathFragment.value(in: array)
97+ default:
98+ throw Error.unexpectedSubscript(type: type(of: pathFragment))
99+ }
100+ }
101+
102+ func value(at path: [JSONPathType], detectingNull: Bool = false) throws -> JSON {
103+ var result = self
104+ for pathFragment in path {
105+ result = try result.value(for: pathFragment, detectingNull: detectingNull)
106+ }
107+ return result
108+ }
109+
110+}
111+
112+// MARK: - Subscripting operator
113+
114+extension JSON {
115+
116+ public subscript(key: String) -> JSON? {
117+ return try? value(for: key, detectingNull: false)
118+ }
119+
120+ public subscript(index: Int) -> JSON? {
121+ return try? value(for: index, detectingNull: false)
122+ }
123+
124+}
125+
126+// MARK: - Simple member unpacking
127+
128+extension JSON {
129+
130+ /// Attempts to decode into the returning type from a path into JSON.
131+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`.
132+ /// - parameter type: If the context this method is called from does not
133+ /// make the return type clear, pass a type implementing `JSONDecodable`
134+ /// to disambiguate the type to decode with.
135+ /// - returns: An initialized member from the inner JSON.
136+ /// - throws: One of the following errors contained in `JSON.Error`:
137+ /// * `KeyNotFound`: A given `String` key does not exist inside a
138+ /// descendant `JSON` dictionary.
139+ /// * `IndexOutOfBounds`: A given `Int` index is outside the bounds of a
140+ /// descendant `JSON` array.
141+ /// * `UnexpectedSubscript`: A given subscript cannot be used with the
142+ /// corresponding `JSON` value.
143+ /// * `TypeNotConvertible`: The target value's type inside of
144+ /// the `JSON` instance does not match `Decoded`.
145+ public func decode<Decoded: JSONDecodable>(at path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> Decoded {
146+ return try Decoded(json: value(at: path))
147+ }
148+
149+ /// Retrieves a `Double` from a path into JSON.
150+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
151+ /// - returns: A floating-point `Double`
152+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
153+ /// - seealso: `JSON.decode(at:type:)`
154+ public func getDouble(at path: JSONPathType...) throws -> Double {
155+ return try Double(json: value(at: path))
156+ }
157+
158+ /// Retrieves an `Int` from a path into JSON.
159+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
160+ /// - returns: A numeric `Int`
161+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
162+ /// - seealso: `JSON.decode(at:type:)`
163+ public func getInt(at path: JSONPathType...) throws -> Int {
164+ return try Int(json: value(at: path))
165+ }
166+
167+ /// Retrieves a `String` from a path into JSON.
168+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
169+ /// - returns: A textual `String`
170+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
171+ /// - seealso: `JSON.decode(at:type:)`
172+ public func getString(at path: JSONPathType...) throws -> String {
173+ return try String(json: value(at: path))
174+ }
175+
176+ /// Retrieves a `Bool` from a path into JSON.
177+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
178+ /// - returns: A truthy `Bool`
179+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
180+ /// - seealso: `JSON.decode(at:type:)`
181+ public func getBool(at path: JSONPathType...) throws -> Bool {
182+ return try Bool(json: value(at: path))
183+ }
184+
185+ /// Retrieves a `[JSON]` from a path into JSON.
186+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
187+ /// - returns: An `Array` of `JSON` elements
188+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
189+ /// - seealso: `JSON.decode(at:type:)`
190+ public func getArray(at path: JSONPathType...) throws -> [JSON] {
191+ return try JSON.getArray(from: value(at: path))
192+ }
193+
194+ /// Attempts to decode many values from a descendant JSON array at a path
195+ /// into JSON.
196+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
197+ /// - parameter type: If the context this method is called from does not
198+ /// make the return type clear, pass a type implementing `JSONDecodable`
199+ /// to disambiguate the type to decode with.
200+ /// - returns: An `Array` of decoded elements
201+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`, or
202+ /// any error that arises from decoding the contained values.
203+ /// - seealso: `JSON.decode(at:type:)`
204+ public func decodedArray<Decoded: JSONDecodable>(at path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> [Decoded] {
205+ return try JSON.decodedArray(from: value(at: path))
206+ }
207+
208+ /// Retrieves a `[String: JSON]` from a path into JSON.
209+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
210+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
211+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)`.
212+ /// - seealso: `JSON.decode(at:type:)`
213+ public func getDictionary(at path: JSONPathType...) throws -> [String: JSON] {
214+ return try JSON.getDictionary(from: value(at: path))
215+ }
216+
217+ /// Attempts to decode many values from a descendant JSON object at a path
218+ /// into JSON.
219+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
220+ /// - parameter type: If the context this method is called from does not
221+ /// make the return type clear, pass a type implementing `JSONDecodable`
222+ /// to disambiguate the value type to decode with.
223+ /// - returns: A `Dictionary` of `String` keys and decoded values.
224+ /// - throws: One of the `JSON.Error` cases thrown by `decode(at:type:)` or
225+ /// any error that arises from decoding the contained values.
226+ /// - seealso: `JSON.decode(at:type:)`
227+ public func decodedDictionary<Decoded: JSONDecodable>(at path: JSONPathType..., type: Decoded.Type = Decoded.self) throws -> [String: Decoded] {
228+ return try JSON.decodedDictionary(from: value(at: path))
229+ }
230+
231+}
232+
233+// MARK: - NotFound-Or-Null-to-Optional unpacking
234+
235+extension JSON {
236+
237+ /// An `OptionSetType` used to represent the different options available for subscripting `JSON` with `null` values or missing keys.
238+ /// * `.NullBecomesNil` - Treat `null` values as `nil`.
239+ /// * `.MissingKeyBecomesNil` - Treat missing keys as `nil`.
240+ public struct SubscriptingOptions: OptionSet {
241+ public let rawValue: Int
242+ public init(rawValue: Int) {
243+ self.rawValue = rawValue
244+ }
245+
246+ /// Treat `null` values as `nil`.
247+ public static let NullBecomesNil = SubscriptingOptions(rawValue: 1 << 0)
248+ /// Treat missing keys as `nil`.
249+ public static let MissingKeyBecomesNil = SubscriptingOptions(rawValue: 1 << 1)
250+ }
251+
252+ fileprivate func mapOptional<Value>(at path: [JSONPathType], alongPath options: SubscriptingOptions, transform: (JSON) throws -> Value) throws -> Value? {
253+ let detectNull = options.contains(.NullBecomesNil)
254+ let detectNotFound = options.contains(.MissingKeyBecomesNil)
255+ var json: JSON?
256+ do {
257+ json = try value(at: path, detectingNull: detectNull)
258+ return try json.map(transform)
259+ } catch Error.indexOutOfBounds where detectNotFound {
260+ return nil
261+ } catch Error.keyNotFound where detectNotFound {
262+ return nil
263+ } catch Error.valueNotConvertible where detectNull && json == .null {
264+ return nil
265+ } catch SubscriptError.subscriptIntoNull where detectNull {
266+ return nil
267+ }
268+ }
269+}
270+
271+extension JSON {
272+
273+ /// Decodes a `JSON` instance if it is not `.Null`, throws otherwise.
274+ /// - parameter json: An instance of `JSON`.
275+ /// - returns: An instance of some type that conforms to `JSONDecodable`.
276+ /// - throws: `Error.ValueNotConvertible` if the `JSON` instance is `.Null`.
277+ private static func getDecoded<Decoded: JSONDecodable>(json: JSON) throws -> Decoded {
278+ guard json != .null else {
279+ throw Error.valueNotConvertible(value: json, to: Decoded.self)
280+ }
281+ return try Decoded.init(json: json)
282+ }
283+
284+ /// Optionally decodes into the returning type from a path into JSON.
285+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
286+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
287+ /// - parameter type: If the context this method is called from does not
288+ /// make the return type clear, pass a type implementing `JSONDecodable`
289+ /// to disambiguate the type to decode with.
290+ /// - returns: A decoded value from the inner JSON if found, or `nil`.
291+ /// - throws: One of the following errors contained in `JSON.Error`:
292+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
293+ /// `JSON` dictionary.
294+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
295+ /// descendant `JSON` array.
296+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
297+ /// corresponding `JSON` value.
298+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
299+ /// instance does not match the decoded value.
300+ /// * Any error that arises from decoding the value.
301+ public func decode<Decoded: JSONDecodable>(at path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> Decoded? {
302+ return try mapOptional(at: path, alongPath: options, transform: JSON.getDecoded)
303+ }
304+
305+ /// Optionally retrieves a `Double` from a path into JSON.
306+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`.
307+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
308+ /// - returns: A `Double` if a value could be found, otherwise `nil`.
309+ /// - throws: One of the following errors contained in `JSON.Error`:
310+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
311+ /// `JSON` dictionary.
312+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
313+ /// descendant `JSON` array.
314+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
315+ /// corresponding `JSON` value.
316+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
317+ /// instance does not match the decoded value.
318+ public func getDouble(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Double? {
319+ return try mapOptional(at: path, alongPath: options, transform: Double.init)
320+ }
321+
322+ /// Optionally retrieves a `Int` from a path into JSON.
323+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
324+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
325+ /// - returns: A numeric `Int` if a value could be found, otherwise `nil`.
326+ /// - throws: One of the following errors contained in `JSON.Error`:
327+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
328+ /// `JSON` dictionary.
329+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
330+ /// descendant `JSON` array.
331+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
332+ /// corresponding `JSON` value.
333+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
334+ /// instance does not match the decoded value.
335+ public func getInt(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Int? {
336+ return try mapOptional(at: path, alongPath: options, transform: Int.init)
337+ }
338+
339+ /// Optionally retrieves a `String` from a path into JSON.
340+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
341+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
342+ /// - returns: A text `String` if a value could be found, otherwise `nil`.
343+ /// - throws: One of the following errors contained in `JSON.Error`:
344+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
345+ /// `JSON` dictionary.
346+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
347+ /// descendant `JSON` array.
348+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
349+ /// corresponding `JSON` value.
350+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
351+ /// instance does not match the decoded value.
352+ public func getString(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> String? {
353+ return try mapOptional(at: path, alongPath: options, transform: String.init)
354+ }
355+
356+ /// Optionally retrieves a `Bool` from a path into JSON.
357+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
358+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
359+ /// - returns: A truthy `Bool` if a value could be found, otherwise `nil`.
360+ /// - throws: One of the following errors contained in `JSON.Error`:
361+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
362+ /// `JSON` dictionary.
363+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
364+ /// descendant `JSON` array.
365+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
366+ /// corresponding `JSON` value.
367+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
368+ /// instance does not match the decoded value.
369+ public func getBool(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> Bool? {
370+ return try mapOptional(at: path, alongPath: options, transform: Bool.init)
371+ }
372+
373+ /// Optionally retrieves a `[JSON]` from a path into the recieving structure.
374+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
375+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
376+ /// - returns: An `Array` of `JSON` elements if a value could be found,
377+ /// otherwise `nil`.
378+ /// - throws: One of the following errors contained in `JSON.Error`:
379+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
380+ /// `JSON` dictionary.
381+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
382+ /// descendant `JSON` array.
383+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
384+ /// corresponding `JSON` value.
385+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
386+ /// instance does not match the decoded value.
387+ public func getArray(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> [JSON]? {
388+ return try mapOptional(at: path, alongPath: options, transform: JSON.getArray)
389+ }
390+
391+ /// Optionally decodes many values from a descendant array at a path into
392+ /// JSON.
393+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
394+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
395+ /// - parameter type: If the context this method is called from does not
396+ /// make the return type clear, pass a type implementing `JSONDecodable`
397+ /// to disambiguate the value type to decode with.
398+ /// - returns: An `Array` of decoded elements if found, otherwise `nil`.
399+ /// - throws: One of the following errors contained in `JSON.Error`:
400+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
401+ /// `JSON` dictionary.
402+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
403+ /// descendant `JSON` array.
404+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
405+ /// corresponding `JSON` value.
406+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
407+ /// instance does not match the decoded value.
408+ /// * Any error that arises from decoding the value.
409+ public func decodedArray<Decoded: JSONDecodable>(at path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> [Decoded]? {
410+ return try mapOptional(at: path, alongPath: options, transform: JSON.decodedArray)
411+ }
412+
413+ /// Optionally retrieves a `[String: JSON]` from a path into the recieving
414+ /// structure.
415+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
416+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
417+ /// - returns: A `Dictionary` of `String` mapping to `JSON` elements if a
418+ /// value could be found, otherwise `nil`.
419+ /// - throws: One of the following errors contained in `JSON.Error`:
420+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
421+ /// `JSON` dictionary.
422+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
423+ /// descendant `JSON` array.
424+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
425+ /// corresponding `JSON` value.
426+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
427+ /// instance does not match the decoded value.
428+ public func getDictionary(at path: JSONPathType..., alongPath options: SubscriptingOptions) throws -> [String: JSON]? {
429+ return try mapOptional(at: path, alongPath: options, transform: JSON.getDictionary)
430+ }
431+
432+ /// Optionally attempts to decode many values from a descendant object at a path
433+ /// into JSON.
434+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
435+ /// - parameter alongPath: Options that control what should be done with values that are `null` or keys that are missing.
436+ /// - parameter type: If the context this method is called from does not
437+ /// make the return type clear, pass a type implementing `JSONDecodable`
438+ /// to disambiguate the value type to decode with.
439+ /// - returns: A `Dictionary` of `String` mapping to decoded elements if a
440+ /// value could be found, otherwise `nil`.
441+ /// - throws: One of the following errors contained in `JSON.Error`:
442+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
443+ /// `JSON` dictionary.
444+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
445+ /// descendant `JSON` array.
446+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
447+ /// corresponding `JSON` value.
448+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
449+ /// instance does not match the decoded value.
450+ /// * Any error that arises from decoding the value.
451+ public func decodedDictionary<Decoded: JSONDecodable>(at path: JSONPathType..., alongPath options: SubscriptingOptions, type: Decoded.Type = Decoded.self) throws -> [String: Decoded]? {
452+ return try mapOptional(at: path, alongPath: options, transform: JSON.decodedDictionary)
453+ }
454+
455+}
456+
457+// MARK: - Missing-with-fallback unpacking
458+
459+extension JSON {
460+
461+ fileprivate func mapOptional<Value>(at path: [JSONPathType], fallback: () -> Value, transform: (JSON) throws -> Value) throws -> Value {
462+ return try mapOptional(at: path, alongPath: .MissingKeyBecomesNil, transform: transform) ?? fallback()
463+ }
464+
465+ /// Attempts to decode into the returning type from a path into
466+ /// JSON, or returns a fallback if not found.
467+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
468+ /// - parameter fallback: Value to use when one is missing at the subscript.
469+ /// - returns: An initialized member from the inner JSON.
470+ /// - throws: One of the following errors contained in `JSON.Error`:
471+ /// * `UnexpectedSubscript`: A given subscript cannot be used with the
472+ /// corresponding `JSON` value.
473+ /// * `TypeNotConvertible`: The target value's type inside of
474+ /// the `JSON` instance does not match `Decoded`.
475+ public func decode<Decoded: JSONDecodable>(at path: JSONPathType..., or fallback: @autoclosure() -> Decoded) throws -> Decoded {
476+ return try mapOptional(at: path, fallback: fallback, transform: Decoded.init)
477+ }
478+
479+ /// Retrieves a `Double` from a path into JSON or a fallback if not found.
480+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
481+ /// - parameter fallback: `Double` to use when one is missing at the subscript.
482+ /// - returns: A floating-point `Double`
483+ /// - throws: One of the `JSON.Error` cases thrown by calling `mapOptional(at:fallback:transform:)`.
484+ public func getDouble(at path: JSONPathType..., or fallback: @autoclosure() -> Double) throws -> Double {
485+ return try mapOptional(at: path, fallback: fallback, transform: Double.init)
486+ }
487+
488+ /// Retrieves an `Int` from a path into JSON or a fallback if not found.
489+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
490+ /// - parameter fallback: `Int` to use when one is missing at the subscript.
491+ /// - returns: A numeric `Int`
492+ /// - throws: One of the following errors contained in `JSON.Error`:
493+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
494+ /// `JSON` dictionary.
495+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
496+ /// descendant `JSON` array.
497+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
498+ /// corresponding `JSON` value.
499+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
500+ /// instance does not match the decoded value.
501+ public func getInt(at path: JSONPathType..., or fallback: @autoclosure() -> Int) throws -> Int {
502+ return try mapOptional(at: path, fallback: fallback, transform: Int.init)
503+ }
504+
505+ /// Retrieves a `String` from a path into JSON or a fallback if not found.
506+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
507+ /// - parameter fallback: `String` to use when one is missing at the subscript.
508+ /// - returns: A textual `String`
509+ /// - throws: One of the following errors contained in `JSON.Error`:
510+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
511+ /// `JSON` dictionary.
512+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
513+ /// descendant `JSON` array.
514+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
515+ /// corresponding `JSON` value.
516+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
517+ /// instance does not match the decoded value.
518+ public func getString(at path: JSONPathType..., or fallback: @autoclosure() -> String) throws -> String {
519+ return try mapOptional(at: path, fallback: fallback, transform: String.init)
520+ }
521+
522+ /// Retrieves a `Bool` from a path into JSON or a fallback if not found.
523+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
524+ /// - parameter fallback: `Bool` to use when one is missing at the subscript.
525+ /// - returns: A truthy `Bool`
526+ /// - throws: One of the following errors contained in `JSON.Error`:
527+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
528+ /// `JSON` dictionary.
529+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
530+ /// descendant `JSON` array.
531+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
532+ /// corresponding `JSON` value.
533+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
534+ /// instance does not match the decoded value.
535+ public func getBool(at path: JSONPathType..., or fallback: @autoclosure() -> Bool) throws -> Bool {
536+ return try mapOptional(at: path, fallback: fallback, transform: Bool.init)
537+ }
538+
539+ /// Retrieves a `[JSON]` from a path into JSON or a fallback if not found.
540+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
541+ /// - parameter fallback: `Array` to use when one is missing at the subscript.
542+ /// - returns: An `Array` of `JSON` elements
543+ /// - throws: One of the following errors contained in `JSON.Error`:
544+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
545+ /// `JSON` dictionary.
546+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
547+ /// descendant `JSON` array.
548+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
549+ /// corresponding `JSON` value.
550+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
551+ /// instance does not match the decoded value.
552+ public func getArray(at path: JSONPathType..., or fallback: @autoclosure() -> [JSON]) throws -> [JSON] {
553+ return try mapOptional(at: path, fallback: fallback, transform: JSON.getArray)
554+ }
555+
556+ /// Attempts to decodes many values from a desendant JSON array at a path
557+ /// into the recieving structure, returning a fallback if not found.
558+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
559+ /// - parameter fallback: `Array` to use when one is missing at the subscript.
560+ /// - returns: An `Array` of decoded elements
561+ /// - throws: One of the following errors contained in `JSON.Error`:
562+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
563+ /// `JSON` dictionary.
564+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
565+ /// descendant `JSON` array.
566+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
567+ /// corresponding `JSON` value.
568+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
569+ /// instance does not match the decoded value.
570+ /// * Any error that arises from decoding the value.
571+ public func decodedArray<Decoded: JSONDecodable>(at path: JSONPathType..., or fallback: @autoclosure() -> [Decoded]) throws -> [Decoded] {
572+ return try mapOptional(at: path, fallback: fallback, transform: JSON.decodedArray)
573+ }
574+
575+ /// Retrieves a `[String: JSON]` from a path into JSON or a fallback if not
576+ /// found.
577+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
578+ /// - parameter fallback: `Dictionary` to use when one is missing at the subscript.
579+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
580+ /// - throws: One of the following errors contained in `JSON.Error`:
581+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
582+ /// `JSON` dictionary.
583+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
584+ /// descendant `JSON` array.
585+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
586+ /// corresponding `JSON` value.
587+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
588+ /// instance does not match the decoded value.
589+ public func getDictionary(at path: JSONPathType..., or fallback: @autoclosure() -> [String: JSON]) throws -> [String: JSON] {
590+ return try mapOptional(at: path, fallback: fallback, transform: JSON.getDictionary)
591+ }
592+
593+ /// Attempts to decode many values from a descendant JSON object at a path
594+ /// into the receiving structure, returning a fallback if not found.
595+ /// - parameter path: 0 or more `String` or `Int` that subscript the `JSON`
596+ /// - parameter fallback: Value to use when one is missing at the subscript
597+ /// - returns: A `Dictionary` of `String` mapping to decoded elements.
598+ /// - throws: One of the following errors contained in `JSON.Error`:
599+ /// * `KeyNotFound`: A key `path` does not exist inside a descendant
600+ /// `JSON` dictionary.
601+ /// * `IndexOutOfBounds`: An index `path` is outside the bounds of a
602+ /// descendant `JSON` array.
603+ /// * `UnexpectedSubscript`: A `path` item cannot be used with the
604+ /// corresponding `JSON` value.
605+ /// * `TypeNotConvertible`: The target value's type inside of the `JSON`
606+ /// instance does not match the decoded value.
607+ /// * Any error that arises from decoding the value.
608+ public func decodedDictionary<Decoded: JSONDecodable>(at path: JSONPathType..., or fallback: @autoclosure() -> [String: Decoded]) throws -> [String: Decoded] {
609+ return try mapOptional(at: path, fallback: fallback, transform: JSON.decodedDictionary)
610+ }
611+
612+
613+}
tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/main.swiftnew file+40
@@ -0,0 +1,40 @@
1+//
2+// main.swift
3+// test_Freddy
4+//
5+// Created by nst on 10/08/16.
6+// Copyright © 2016 Nicolas Seriot. All rights reserved.
7+//
8+
9+import Foundation
10+
11+func main() {
12+
13+ guard ProcessInfo.processInfo.arguments.count == 2 else {
14+ let url = URL(fileURLWithPath: ProcessInfo.processInfo.arguments[0])
15+ let programName = url.lastPathComponent
16+ print("Usage: ./\(programName) file.json")
17+ exit(1)
18+ }
19+
20+ let path = ProcessInfo.processInfo.arguments[1]
21+ let url = NSURL.fileURL(withPath:path)
22+
23+ do {
24+ let data = try Data(contentsOf:url)
25+ do {
26+ let o = try JSONParser.parse(utf8: data)
27+ exit(0)
28+ } catch {
29+ exit(1)
30+ }
31+
32+ } catch let e {
33+ print("*** CANNOT READ DATA AT \(url)")
34+ print(e)
35+ exit(1)
36+ }
37+
38+}
39+
40+main()
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/bin/test_Freddy_2_1_0new file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/project.pbxprojnew file+300
@@ -0,0 +1,300 @@
1+// !$*UTF8*$!
2+{
3+ archiveVersion = 1;
4+ classes = {
5+ };
6+ objectVersion = 46;
7+ objects = {
8+
9+/* Begin PBXBuildFile section */
10+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D84ABA1D5B0A1000776402 /* main.swift */; };
11+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7781D5B75600021630C /* JSON.swift */; };
12+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7791D5B75600021630C /* JSONDecodable.swift */; };
13+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77A1D5B75600021630C /* JSONEncodable.swift */; };
14+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */; };
15+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */; };
16+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77D1D5B75600021630C /* JSONParser.swift */; };
17+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77E1D5B75600021630C /* JSONParsing.swift */; };
18+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F77F1D5B75600021630C /* JSONSerializing.swift */; };
19+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03F8F7801D5B75600021630C /* JSONSubscripting.swift */; };
20+/* End PBXBuildFile section */
21+
22+/* Begin PBXCopyFilesBuildPhase section */
23+ 03D84AB51D5B0A1000776402 /* CopyFiles */ = {
24+ isa = PBXCopyFilesBuildPhase;
25+ buildActionMask = 2147483647;
26+ dstPath = /usr/share/man/man1;
27+ dstSubfolderSpec = 0;
28+ files = (
29+ );
30+ runOnlyForDeploymentPostprocessing = 1;
31+ };
32+/* End PBXCopyFilesBuildPhase section */
33+
34+/* Begin PBXFileReference section */
35+ 03D84AB71D5B0A1000776402 /* test_Freddy */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test_Freddy; sourceTree = BUILT_PRODUCTS_DIR; };
36+ 03D84ABA1D5B0A1000776402 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
37+ 03F8F7781D5B75600021630C /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSON.swift; path = test_Freddy/JSON.swift; sourceTree = "<group>"; };
38+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONDecodable.swift; path = test_Freddy/JSONDecodable.swift; sourceTree = "<group>"; };
39+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodable.swift; path = test_Freddy/JSONEncodable.swift; sourceTree = "<group>"; };
40+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONEncodingDetector.swift; path = test_Freddy/JSONEncodingDetector.swift; sourceTree = "<group>"; };
41+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONLiteralConvertible.swift; path = test_Freddy/JSONLiteralConvertible.swift; sourceTree = "<group>"; };
42+ 03F8F77D1D5B75600021630C /* JSONParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParser.swift; path = test_Freddy/JSONParser.swift; sourceTree = "<group>"; };
43+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONParsing.swift; path = test_Freddy/JSONParsing.swift; sourceTree = "<group>"; };
44+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSerializing.swift; path = test_Freddy/JSONSerializing.swift; sourceTree = "<group>"; };
45+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JSONSubscripting.swift; path = test_Freddy/JSONSubscripting.swift; sourceTree = "<group>"; };
46+/* End PBXFileReference section */
47+
48+/* Begin PBXFrameworksBuildPhase section */
49+ 03D84AB41D5B0A1000776402 /* Frameworks */ = {
50+ isa = PBXFrameworksBuildPhase;
51+ buildActionMask = 2147483647;
52+ files = (
53+ );
54+ runOnlyForDeploymentPostprocessing = 0;
55+ };
56+/* End PBXFrameworksBuildPhase section */
57+
58+/* Begin PBXGroup section */
59+ 03D84AAE1D5B0A1000776402 = {
60+ isa = PBXGroup;
61+ children = (
62+ 03F8F7781D5B75600021630C /* JSON.swift */,
63+ 03F8F7791D5B75600021630C /* JSONDecodable.swift */,
64+ 03F8F77A1D5B75600021630C /* JSONEncodable.swift */,
65+ 03F8F77B1D5B75600021630C /* JSONEncodingDetector.swift */,
66+ 03F8F77C1D5B75600021630C /* JSONLiteralConvertible.swift */,
67+ 03F8F77D1D5B75600021630C /* JSONParser.swift */,
68+ 03F8F77E1D5B75600021630C /* JSONParsing.swift */,
69+ 03F8F77F1D5B75600021630C /* JSONSerializing.swift */,
70+ 03F8F7801D5B75600021630C /* JSONSubscripting.swift */,
71+ 03D84AB91D5B0A1000776402 /* test_Freddy */,
72+ 03D84AB81D5B0A1000776402 /* Products */,
73+ );
74+ sourceTree = "<group>";
75+ };
76+ 03D84AB81D5B0A1000776402 /* Products */ = {
77+ isa = PBXGroup;
78+ children = (
79+ 03D84AB71D5B0A1000776402 /* test_Freddy */,
80+ );
81+ name = Products;
82+ sourceTree = "<group>";
83+ };
84+ 03D84AB91D5B0A1000776402 /* test_Freddy */ = {
85+ isa = PBXGroup;
86+ children = (
87+ 03D84ABA1D5B0A1000776402 /* main.swift */,
88+ );
89+ path = test_Freddy;
90+ sourceTree = "<group>";
91+ };
92+/* End PBXGroup section */
93+
94+/* Begin PBXNativeTarget section */
95+ 03D84AB61D5B0A1000776402 /* test_Freddy */ = {
96+ isa = PBXNativeTarget;
97+ buildConfigurationList = 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */;
98+ buildPhases = (
99+ 03D84AB31D5B0A1000776402 /* Sources */,
100+ 03D84AB41D5B0A1000776402 /* Frameworks */,
101+ 03D84AB51D5B0A1000776402 /* CopyFiles */,
102+ );
103+ buildRules = (
104+ );
105+ dependencies = (
106+ );
107+ name = test_Freddy;
108+ productName = test_Freddy;
109+ productReference = 03D84AB71D5B0A1000776402 /* test_Freddy */;
110+ productType = "com.apple.product-type.tool";
111+ };
112+/* End PBXNativeTarget section */
113+
114+/* Begin PBXProject section */
115+ 03D84AAF1D5B0A1000776402 /* Project object */ = {
116+ isa = PBXProject;
117+ attributes = {
118+ LastSwiftUpdateCheck = 0800;
119+ LastUpgradeCheck = 0800;
120+ ORGANIZATIONNAME = "Nicolas Seriot";
121+ TargetAttributes = {
122+ 03D84AB61D5B0A1000776402 = {
123+ CreatedOnToolsVersion = 8.0;
124+ DevelopmentTeam = VBYRKYS73S;
125+ DevelopmentTeamName = "Nicolas Seriot";
126+ ProvisioningStyle = Automatic;
127+ };
128+ };
129+ };
130+ buildConfigurationList = 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */;
131+ compatibilityVersion = "Xcode 3.2";
132+ developmentRegion = English;
133+ hasScannedForEncodings = 0;
134+ knownRegions = (
135+ en,
136+ );
137+ mainGroup = 03D84AAE1D5B0A1000776402;
138+ productRefGroup = 03D84AB81D5B0A1000776402 /* Products */;
139+ projectDirPath = "";
140+ projectRoot = "";
141+ targets = (
142+ 03D84AB61D5B0A1000776402 /* test_Freddy */,
143+ );
144+ };
145+/* End PBXProject section */
146+
147+/* Begin PBXSourcesBuildPhase section */
148+ 03D84AB31D5B0A1000776402 /* Sources */ = {
149+ isa = PBXSourcesBuildPhase;
150+ buildActionMask = 2147483647;
151+ files = (
152+ 03F8F7881D5B75600021630C /* JSONSerializing.swift in Sources */,
153+ 03F8F7821D5B75600021630C /* JSONDecodable.swift in Sources */,
154+ 03F8F7851D5B75600021630C /* JSONLiteralConvertible.swift in Sources */,
155+ 03F8F7871D5B75600021630C /* JSONParsing.swift in Sources */,
156+ 03F8F7861D5B75600021630C /* JSONParser.swift in Sources */,
157+ 03F8F7831D5B75600021630C /* JSONEncodable.swift in Sources */,
158+ 03D84ABB1D5B0A1000776402 /* main.swift in Sources */,
159+ 03F8F7811D5B75600021630C /* JSON.swift in Sources */,
160+ 03F8F7891D5B75600021630C /* JSONSubscripting.swift in Sources */,
161+ 03F8F7841D5B75600021630C /* JSONEncodingDetector.swift in Sources */,
162+ );
163+ runOnlyForDeploymentPostprocessing = 0;
164+ };
165+/* End PBXSourcesBuildPhase section */
166+
167+/* Begin XCBuildConfiguration section */
168+ 03D84ABC1D5B0A1000776402 /* Debug */ = {
169+ isa = XCBuildConfiguration;
170+ buildSettings = {
171+ ALWAYS_SEARCH_USER_PATHS = NO;
172+ CLANG_ANALYZER_NONNULL = YES;
173+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
174+ CLANG_CXX_LIBRARY = "libc++";
175+ CLANG_ENABLE_MODULES = YES;
176+ CLANG_ENABLE_OBJC_ARC = YES;
177+ CLANG_WARN_BOOL_CONVERSION = YES;
178+ CLANG_WARN_CONSTANT_CONVERSION = YES;
179+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
180+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
181+ CLANG_WARN_EMPTY_BODY = YES;
182+ CLANG_WARN_ENUM_CONVERSION = YES;
183+ CLANG_WARN_INT_CONVERSION = YES;
184+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
185+ CLANG_WARN_UNREACHABLE_CODE = YES;
186+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
187+ CODE_SIGN_IDENTITY = "-";
188+ COPY_PHASE_STRIP = NO;
189+ DEBUG_INFORMATION_FORMAT = dwarf;
190+ ENABLE_STRICT_OBJC_MSGSEND = YES;
191+ ENABLE_TESTABILITY = YES;
192+ GCC_C_LANGUAGE_STANDARD = gnu99;
193+ GCC_DYNAMIC_NO_PIC = NO;
194+ GCC_NO_COMMON_BLOCKS = YES;
195+ GCC_OPTIMIZATION_LEVEL = 0;
196+ GCC_PREPROCESSOR_DEFINITIONS = (
197+ "DEBUG=1",
198+ "$(inherited)",
199+ );
200+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
201+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
202+ GCC_WARN_UNDECLARED_SELECTOR = YES;
203+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
204+ GCC_WARN_UNUSED_FUNCTION = YES;
205+ GCC_WARN_UNUSED_VARIABLE = YES;
206+ MACOSX_DEPLOYMENT_TARGET = 10.11;
207+ MTL_ENABLE_DEBUG_INFO = YES;
208+ ONLY_ACTIVE_ARCH = YES;
209+ SDKROOT = macosx;
210+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
211+ };
212+ name = Debug;
213+ };
214+ 03D84ABD1D5B0A1000776402 /* Release */ = {
215+ isa = XCBuildConfiguration;
216+ buildSettings = {
217+ ALWAYS_SEARCH_USER_PATHS = NO;
218+ CLANG_ANALYZER_NONNULL = YES;
219+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
220+ CLANG_CXX_LIBRARY = "libc++";
221+ CLANG_ENABLE_MODULES = YES;
222+ CLANG_ENABLE_OBJC_ARC = YES;
223+ CLANG_WARN_BOOL_CONVERSION = YES;
224+ CLANG_WARN_CONSTANT_CONVERSION = YES;
225+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
226+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
227+ CLANG_WARN_EMPTY_BODY = YES;
228+ CLANG_WARN_ENUM_CONVERSION = YES;
229+ CLANG_WARN_INT_CONVERSION = YES;
230+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
231+ CLANG_WARN_UNREACHABLE_CODE = YES;
232+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
233+ CODE_SIGN_IDENTITY = "-";
234+ COPY_PHASE_STRIP = NO;
235+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
236+ ENABLE_NS_ASSERTIONS = NO;
237+ ENABLE_STRICT_OBJC_MSGSEND = YES;
238+ GCC_C_LANGUAGE_STANDARD = gnu99;
239+ GCC_NO_COMMON_BLOCKS = YES;
240+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
241+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
242+ GCC_WARN_UNDECLARED_SELECTOR = YES;
243+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
244+ GCC_WARN_UNUSED_FUNCTION = YES;
245+ GCC_WARN_UNUSED_VARIABLE = YES;
246+ MACOSX_DEPLOYMENT_TARGET = 10.11;
247+ MTL_ENABLE_DEBUG_INFO = NO;
248+ SDKROOT = macosx;
249+ };
250+ name = Release;
251+ };
252+ 03D84ABF1D5B0A1000776402 /* Debug */ = {
253+ isa = XCBuildConfiguration;
254+ buildSettings = {
255+ FRAMEWORK_SEARCH_PATHS = (
256+ "$(inherited)",
257+ "$(PROJECT_DIR)",
258+ );
259+ PRODUCT_NAME = "$(TARGET_NAME)";
260+ SWIFT_VERSION = 2.3;
261+ };
262+ name = Debug;
263+ };
264+ 03D84AC01D5B0A1000776402 /* Release */ = {
265+ isa = XCBuildConfiguration;
266+ buildSettings = {
267+ FRAMEWORK_SEARCH_PATHS = (
268+ "$(inherited)",
269+ "$(PROJECT_DIR)",
270+ );
271+ PRODUCT_NAME = "$(TARGET_NAME)";
272+ SWIFT_VERSION = 2.3;
273+ };
274+ name = Release;
275+ };
276+/* End XCBuildConfiguration section */
277+
278+/* Begin XCConfigurationList section */
279+ 03D84AB21D5B0A1000776402 /* Build configuration list for PBXProject "test_Freddy" */ = {
280+ isa = XCConfigurationList;
281+ buildConfigurations = (
282+ 03D84ABC1D5B0A1000776402 /* Debug */,
283+ 03D84ABD1D5B0A1000776402 /* Release */,
284+ );
285+ defaultConfigurationIsVisible = 0;
286+ defaultConfigurationName = Release;
287+ };
288+ 03D84ABE1D5B0A1000776402 /* Build configuration list for PBXNativeTarget "test_Freddy" */ = {
289+ isa = XCConfigurationList;
290+ buildConfigurations = (
291+ 03D84ABF1D5B0A1000776402 /* Debug */,
292+ 03D84AC01D5B0A1000776402 /* Release */,
293+ );
294+ defaultConfigurationIsVisible = 0;
295+ defaultConfigurationName = Release;
296+ };
297+/* End XCConfigurationList section */
298+ };
299+ rootObject = 03D84AAF1D5B0A1000776402 /* Project object */;
300+}
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7
@@ -0,0 +1,7 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Workspace
3+ version = "1.0">
4+ <FileRef
5+ location = "self:test_Freddy.xcodeproj">
6+ </FileRef>
7+</Workspace>
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/project.xcworkspace/xcshareddata/test_Freddy.xcscmblueprintnew file+30
@@ -0,0 +1,30 @@
1+{
2+ "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A",
3+ "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4+
5+ },
6+ "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : 0,
8+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : 0
9+ },
10+ "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "7B3DFA55-F9BF-41D8-8FD5-204D3162EB03",
11+ "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12+ "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : "dropbox\/JSON\/test_Freddy\/Vendor\/Freddy\/",
13+ "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A" : "dropbox\/"
14+ },
15+ "DVTSourceControlWorkspaceBlueprintNameKey" : "test_Freddy",
16+ "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17+ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "JSON\/test_Freddy\/test_Freddy.xcodeproj",
18+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19+ {
20+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/bignerdranch\/Freddy.git",
21+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6ADA245F937D8ED6D99F5A7865E19F5267A169E2"
23+ },
24+ {
25+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "\/Users\/nst\/Dropbox\/dropbox.git",
26+ "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27+ "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "ED13D74FC1ED9BC322EA1859E5AD7D001676FA6A"
28+ }
29+ ]
30+}
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_Freddy.xcschemenew file+91
@@ -0,0 +1,91 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<Scheme
3+ LastUpgradeVersion = "0800"
4+ version = "1.3">
5+ <BuildAction
6+ parallelizeBuildables = "YES"
7+ buildImplicitDependencies = "YES">
8+ <BuildActionEntries>
9+ <BuildActionEntry
10+ buildForTesting = "YES"
11+ buildForRunning = "YES"
12+ buildForProfiling = "YES"
13+ buildForArchiving = "YES"
14+ buildForAnalyzing = "YES">
15+ <BuildableReference
16+ BuildableIdentifier = "primary"
17+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
18+ BuildableName = "test_Freddy"
19+ BlueprintName = "test_Freddy"
20+ ReferencedContainer = "container:test_Freddy.xcodeproj">
21+ </BuildableReference>
22+ </BuildActionEntry>
23+ </BuildActionEntries>
24+ </BuildAction>
25+ <TestAction
26+ buildConfiguration = "Debug"
27+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29+ shouldUseLaunchSchemeArgsEnv = "YES">
30+ <Testables>
31+ </Testables>
32+ <MacroExpansion>
33+ <BuildableReference
34+ BuildableIdentifier = "primary"
35+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
36+ BuildableName = "test_Freddy"
37+ BlueprintName = "test_Freddy"
38+ ReferencedContainer = "container:test_Freddy.xcodeproj">
39+ </BuildableReference>
40+ </MacroExpansion>
41+ <AdditionalOptions>
42+ </AdditionalOptions>
43+ </TestAction>
44+ <LaunchAction
45+ buildConfiguration = "Debug"
46+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
47+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
48+ launchStyle = "0"
49+ useCustomWorkingDirectory = "NO"
50+ ignoresPersistentStateOnLaunch = "NO"
51+ debugDocumentVersioning = "YES"
52+ debugServiceExtension = "internal"
53+ allowLocationSimulation = "YES">
54+ <BuildableProductRunnable
55+ runnableDebuggingMode = "0">
56+ <BuildableReference
57+ BuildableIdentifier = "primary"
58+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
59+ BuildableName = "test_Freddy"
60+ BlueprintName = "test_Freddy"
61+ ReferencedContainer = "container:test_Freddy.xcodeproj">
62+ </BuildableReference>
63+ </BuildableProductRunnable>
64+ <AdditionalOptions>
65+ </AdditionalOptions>
66+ </LaunchAction>
67+ <ProfileAction
68+ buildConfiguration = "Release"
69+ shouldUseLaunchSchemeArgsEnv = "YES"
70+ savedToolIdentifier = ""
71+ useCustomWorkingDirectory = "NO"
72+ debugDocumentVersioning = "YES">
73+ <BuildableProductRunnable
74+ runnableDebuggingMode = "0">
75+ <BuildableReference
76+ BuildableIdentifier = "primary"
77+ BlueprintIdentifier = "03D84AB61D5B0A1000776402"
78+ BuildableName = "test_Freddy"
79+ BlueprintName = "test_Freddy"
80+ ReferencedContainer = "container:test_Freddy.xcodeproj">
81+ </BuildableReference>
82+ </BuildableProductRunnable>
83+ </ProfileAction>
84+ <AnalyzeAction
85+ buildConfiguration = "Debug">
86+ </AnalyzeAction>
87+ <ArchiveAction
88+ buildConfiguration = "Release"
89+ revealArchiveInOrganizer = "YES">
90+ </ArchiveAction>
91+</Scheme>
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22
@@ -0,0 +1,22 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+<plist version="1.0">
4+<dict>
5+ <key>SchemeUserState</key>
6+ <dict>
7+ <key>test_Freddy.xcscheme</key>
8+ <dict>
9+ <key>orderHint</key>
10+ <integer>0</integer>
11+ </dict>
12+ </dict>
13+ <key>SuppressBuildableAutocreation</key>
14+ <dict>
15+ <key>03D84AB61D5B0A1000776402</key>
16+ <dict>
17+ <key>primary</key>
18+ <true/>
19+ </dict>
20+ </dict>
21+</dict>
22+</plist>
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSON.swiftnew file+95
@@ -0,0 +1,95 @@
1+//
2+// JSON.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/17/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// An enum to describe the structure of JSON.
10+public enum JSON {
11+ /// A case for denoting an array with an associated value of `[JSON]`
12+ case Array([JSON])
13+ /// A case for denoting a dictionary with an associated value of `[Swift.String: JSON]`
14+ case Dictionary([Swift.String: JSON])
15+ /// A case for denoting a double with an associated value of `Swift.Double`.
16+ case Double(Swift.Double)
17+ /// A case for denoting an integer with an associated value of `Swift.Int`.
18+ case Int(Swift.Int)
19+ /// A case for denoting a string with an associated value of `Swift.String`.
20+ case String(Swift.String)
21+ /// A case for denoting a boolean with an associated value of `Swift.Bool`.
22+ case Bool(Swift.Bool)
23+ /// A case for denoting null.
24+ case Null
25+}
26+
27+// MARK: - Errors
28+
29+extension JSON {
30+
31+ /// An enum to encapsulate errors that may arise in working with `JSON`.
32+ public enum Error: ErrorType {
33+ /// The `index` is out of bounds for a JSON array
34+ case IndexOutOfBounds(index: Swift.Int)
35+
36+ /// The `key` was not found in the JSON dictionary
37+ case KeyNotFound(key: Swift.String)
38+
39+ /// The JSON is not subscriptable with `type`
40+ case UnexpectedSubscript(type: JSONPathType.Type)
41+
42+ /// Unexpected JSON `value` was found that is not convertible `to` type
43+ case ValueNotConvertible(value: JSON, to: Any.Type)
44+ }
45+
46+}
47+
48+// MARK: - Test Equality
49+
50+/// Return `true` if `lhs` is equal to `rhs`.
51+public func ==(lhs: JSON, rhs: JSON) -> Bool {
52+ switch (lhs, rhs) {
53+ case (.Array(let arrL), .Array(let arrR)):
54+ return arrL == arrR
55+ case (.Dictionary(let dictL), .Dictionary(let dictR)):
56+ return dictL == dictR
57+ case (.String(let strL), .String(let strR)):
58+ return strL == strR
59+ case (.Double(let dubL), .Double(let dubR)):
60+ return dubL == dubR
61+ case (.Double(let dubL), .Int(let intR)):
62+ return dubL == Double(intR)
63+ case (.Int(let intL), .Int(let intR)):
64+ return intL == intR
65+ case (.Int(let intL), .Double(let dubR)):
66+ return Double(intL) == dubR
67+ case (.Bool(let bL), .Bool(let bR)):
68+ return bL == bR
69+ case (.Null, .Null):
70+ return true
71+ default:
72+ return false
73+ }
74+}
75+
76+extension JSON: Equatable {}
77+
78+// MARK: - Printing
79+
80+extension JSON: CustomStringConvertible {
81+
82+ /// A textual representation of `self`.
83+ public var description: Swift.String {
84+ switch self {
85+ case .Array(let arr): return Swift.String(arr)
86+ case .Dictionary(let dict): return Swift.String(dict)
87+ case .String(let string): return string
88+ case .Double(let double): return Swift.String(double)
89+ case .Int(let int): return Swift.String(int)
90+ case .Bool(let bool): return Swift.String(bool)
91+ case .Null: return "null"
92+ }
93+ }
94+
95+}
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONDecodable.swiftnew file+151
@@ -0,0 +1,151 @@
1+//
2+// JSONDecodable.swift
3+// Freddy
4+//
5+// Created by Matthew D. Mathias on 3/24/15.
6+// Copyright © 2015 Big Nerd Ranch. Licensed under MIT.
7+//
8+
9+/// A protocol to provide functionality for creating a model object with a `JSON`
10+/// value.
11+public protocol JSONDecodable {
12+
13+ /// Creates an instance of the model with a `JSON` instance.
14+ /// - parameter json: An instance of a `JSON` value from which to
15+ /// construct an instance of the implementing type.
16+ /// - throws: Any `JSON.Error` for errors derived from inspecting the
17+ /// `JSON` value, or any other error involved in decoding.
18+ init(json: JSON) throws
19+
20+}
21+
22+extension Double: JSONDecodable {
23+
24+ /// An initializer to create an instance of `Double` from a `JSON` value.
25+ /// - parameter json: An instance of `JSON`.
26+ /// - throws: The initializer will throw an instance of `JSON.Error` if
27+ /// an instance of `Double` cannot be created from the `JSON` value that was
28+ /// passed to this initializer.
29+ public init(json: JSON) throws {
30+ switch json {
31+ case let .Double(double):
32+ self = double
33+ case let .Int(int):
34+ self = Swift.Double(int)
35+ default:
36+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Double)
37+ }
38+ }
39+
40+}
41+
42+extension Int: JSONDecodable {
43+
44+ /// An initializer to create an instance of `Int` from a `JSON` value.
45+ /// - parameter json: An instance of `JSON`.
46+ /// - throws: The initializer will throw an instance of `JSON.Error` if
47+ /// an instance of `Int` cannot be created from the `JSON` value that was
48+ /// passed to this initializer.
49+ public init(json: JSON) throws {
50+ switch json {
51+ case let .Double(double) where double <= Double(Swift.Int.max):
52+ self = Swift.Int(double)
53+ case let .Int(int):
54+ self = int
55+ default:
56+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Int)
57+ }
58+ }
59+
60+}
61+
62+extension String: JSONDecodable {
63+
64+ /// An initializer to create an instance of `String` from a `JSON` value.
65+ /// - parameter json: An instance of `JSON`.
66+ /// - throws: The initializer will throw an instance of `JSON.Error` if
67+ /// an instance of `String` cannot be created from the `JSON` value that was
68+ /// passed to this initializer.
69+ public init(json: JSON) throws {
70+ guard case let .String(string) = json else {
71+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.String)
72+ }
73+ self = string
74+ }
75+
76+}
77+
78+extension Bool: JSONDecodable {
79+
80+ /// An initializer to create an instance of `Bool` from a `JSON` value.
81+ /// - parameter json: An instance of `JSON`.
82+ /// - throws: The initializer will throw an instance of `JSON.Error` if
83+ /// an instance of `Bool` cannot be created from the `JSON` value that was
84+ /// passed to this initializer.
85+ public init(json: JSON) throws {
86+ guard case let .Bool(bool) = json else {
87+ throw JSON.Error.ValueNotConvertible(value: json, to: Swift.Bool)
88+ }
89+ self = bool
90+ }
91+
92+}
93+
94+extension RawRepresentable where RawValue: JSONDecodable {
95+
96+ /// An initializer to create an instance of `RawRepresentable` from a `JSON` value.
97+ /// - parameter json: An instance of `JSON`.
98+ /// - throws: The initializer will throw an instance of `JSON.Error` if
99+ /// an instance of `RawRepresentable` cannot be created from the `JSON` value that was
100+ /// passed to this initializer.
101+ public init(json: JSON) throws {
102+ let raw = try json.decode(type: RawValue.self)
103+ guard let value = Self(rawValue: raw) else {
104+ throw JSON.Error.ValueNotConvertible(value: json, to: Self.self)
105+ }
106+ self = value
107+ }
108+}
109+
110+internal extension JSON {
111+
112+ /// Retrieves a `[JSON]` from the JSON.
113+ /// - returns: An `Array` of `JSON` elements
114+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
115+ /// - seealso: `JSON.decode(_:type:)`
116+ static func getArray(json: JSON) throws -> [JSON] {
117+ // Ideally should be expressed as a conditional protocol implementation on Swift.Array.
118+ guard case let .Array(array) = json else {
119+ throw Error.ValueNotConvertible(value: json, to: Swift.Array<JSON>)
120+ }
121+ return array
122+ }
123+
124+ /// Retrieves a `[String: JSON]` from the JSON.
125+ /// - returns: An `Dictionary` of `String` mapping to `JSON` elements
126+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`.
127+ /// - seealso: `JSON.decode(_:type:)`
128+ static func getDictionary(json: JSON) throws -> [Swift.String: JSON] {
129+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
130+ guard case let .Dictionary(dictionary) = json else {
131+ throw Error.ValueNotConvertible(value: json, to: Swift.Dictionary<Swift.String, JSON>)
132+ }
133+ return dictionary
134+ }
135+
136+ /// Attempts to decode many values from a descendant JSON array at a path
137+ /// into JSON.
138+ /// - parameter type: If the context this method is called from does not
139+ /// make the return type clear, pass a type implementing `JSONDecodable`
140+ /// to disambiguate the type to decode with.
141+ /// - returns: An `Array` of decoded elements
142+ /// - throws: Any of the `JSON.Error` cases thrown by `decode(type:)`, as
143+ /// well as any error that arises from decoding the contained values.
144+ /// - seealso: `JSON.decode(_:type:)`
145+ static func getArrayOf<Decoded: JSONDecodable>(json: JSON) throws -> [Decoded] {
146+ // Ideally should be expressed as a conditional protocol implementation on Swift.Dictionary.
147+ // This implementation also doesn't do the `type = Type.self` trick.
148+ return try getArray(json).map(Decoded.init)
149+ }
150+
151+}
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONEncodable.swiftnew file+83
@@ -0,0 +1,83 @@
1+//
2+// JSONEncodable.swift
3+// Freddy
4+//
5+// Created by Matthew Mathias on 1/4/16.
6+// Copyright © 2016 Big Nerd Ranch. All rights reserved.
7+//
8+
9+import Foundation
10+
11+/// A protocol to facilitate encoding and decoding of `JSON`.
12+public protocol JSONEncodable {
13+ /// Converts an instance of a conforming type to `JSON`.
14+ /// - returns: An instance of `JSON`.
15+ /// - Note: If conforming to `JSONEncodable` with a custom type of your own, you should return an instance of
16+ /// `JSON.Dictionary`.
17+ func toJSON() -> JSON
18+}
19+
20+extension Array where Element: JSONEncodable {
21+ /// Converts an instance of `Array` whose elements conform to `JSONEncodable` to `JSON`.
22+ /// - returns: An instance of `JSON` where the enum case is `.Array`.
23+ public func toJSON() -> JSON {
24+ let arrayOfJSON = self.map { $0.toJSON() }
25+ return .Array(arrayOfJSON)
26+ }
27+}
28+
29+extension Dictionary where Value: JSONEncodable {
30+ /// Converts an instance of `Dictionary` whose values conform to `JSONEncodable` to `JSON`. The keys in the resulting
31+ /// `JSON.Dictionary` will be of type `String`.
32+ /// - returns: An instance of `JSON` where the enum case is `.Dictionary`.
33+ public func toJSON() -> JSON {
34+ var jsonDictionary = [String: JSON]()
35+
36+ for (k, v) in self {
37+ let key = String(k)
38+ jsonDictionary[key] = v.toJSON()
39+ }
40+
41+ return .Dictionary(jsonDictionary)
42+ }
43+}
44+
45+extension Int: JSONEncodable {
46+ /// Converts an instance of a conforming type to `JSON`.
47+ /// - returns: An instance of `JSON` where the enum case is `.Int`.
48+ public func toJSON() -> JSON {
49+ return .Int(self)
50+ }
51+}
52+
53+extension Double: JSONEncodable {
54+ /// Converts an instance of a conforming type to `JSON`.
55+ /// - returns: An instance of `JSON` where the enum case is `.Double`.
56+ public func toJSON() -> JSON {
57+ return .Double(self)
58+ }
59+}
60+
61+extension String: JSONEncodable {
62+ /// Converts an instance of a conforming type to `JSON`.
63+ /// - returns: An instance of `JSON` where the enum case is `.String`.
64+ public func toJSON() -> JSON {
65+ return .String(self)
66+ }
67+}
68+
69+extension Bool: JSONEncodable {
70+ /// Converts an instance of a conforming type to `JSON`.
71+ /// - returns: An instance of `JSON` where the enum case is `.Bool`.
72+ public func toJSON() -> JSON {
73+ return .Bool(self)
74+ }
75+}
76+
77+extension RawRepresentable where RawValue: JSONEncodable {
78+ /// Converts an instance of a conforming type to `JSON`.
79+ /// - returns: An instance of `JSON` where the enum case is whatever the underlying `RawValue` converts to.
80+ public func toJSON() -> JSON {
81+ return rawValue.toJSON()
82+ }
83+}
tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONEncodingDetector.swiftnew file+110

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONLiteralConvertible.swiftnew file+130

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONParser.swiftnew file+903

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONParsing.swiftnew file+126

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONSerializing.swiftnew file+43

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONSubscripting.swiftnew file+643

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/main.swiftnew file+37

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/JSONKit.hnew file+251

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/JSONKit.mnew file+3065

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/bin/test-JSONKitnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit.xcodeproj/project.pbxprojnew file+252

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test-JSONKit.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_JSONKit/test-JSONKit/main.mnew file+73

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Json.NET/Program.csnew file+46

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/Newtonsoft.Json.dllnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/System.Runtime.Serialization.Primitives.dllnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/test_Json.NET.deps.jsonnew file+49

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/test_Json.NET.dllnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/test_Json.NET.pdbnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Json.NET/bin/Release/netcoreapp1.0/publish/test_Json.NET.runtimeconfig.jsonnew file+8

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Json.NET/project.jsonnew file+21

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Json.NET/project.lock.jsonnew file+6741

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Lua_JSON/JSON.luanew file+1451

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Lua_JSON/test_JSON.luanew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/bin/test_ObjCNSJSONSerializernew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer.xcodeproj/project.pbxprojnew file+246

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_ObjCNSJSONSerializer.xcschemenew file+97

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer/a.outnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer/a.out.dSYM/Contents/Info.plistnew file+20

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer/a.out.dSYM/Contents/Resources/DWARF/a.outnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer/findings_dir/plot_datanew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ObjCNSJSONSerializer/test_ObjCNSJSONSerializer/main.mnew file+32

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/Accessors.swiftnew file+255

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/DecimalNumber.swiftnew file+236

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/Decoder.swiftnew file+294

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/Encoder.swiftnew file+167

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/JSON.swiftnew file+170

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/JSONError.swiftnew file+1619

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/JSONObject.swiftnew file+215

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/ObjectiveC.swiftnew file+233

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/Parser.swiftnew file+674

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/bin/test_PMJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON.xcodeproj/project.pbxprojnew file+296

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_PMJSON.xcschemenew file+97

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_1_0/test_PMJSON/main.swiftnew file+46

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/Accessors.swiftnew file+255

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/DecimalNumber.swiftnew file+236

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/Decoder.swiftnew file+502

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/Encoder.swiftnew file+215

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/JSON.swiftnew file+170

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/JSONError.swiftnew file+1619

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/JSONObject.swiftnew file+215

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/ObjectiveC.swiftnew file+331

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/Parser.swiftnew file+745

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/bin/test_PMJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON.xcodeproj/project.pbxprojnew file+296

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_PMJSON.xcschemenew file+97

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_PMJSON_1_2_0/test_PMJSON/main.swiftnew file+46

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4.hnew file+35

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4Parser.hnew file+248

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4Parser.mnew file+287

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamParser.hnew file+131

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamParser.mnew file+317

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamParserState.hnew file+82

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamParserState.mnew file+355

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamTokeniser.hnew file+40

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamTokeniser.mnew file+395

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamWriter.hnew file+210

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamWriter.mnew file+358

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamWriterState.hnew file+69

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4StreamWriterState.mnew file+147

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4Writer.hnew file+103

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/SBJson4Writer.mnew file+102

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/bin/test_SBJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/project.pbxprojnew file+290

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlistnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_SBJSON.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_SBJSON/test_SBJSON/main.mnew file+38

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/project.pbxprojnew file+361

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/project.xcworkspace/xcshareddata/STJSON.xcscmblueprintnew file+30

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/STJSON.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/STJSONTests.xcschemenew file+56

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+32

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON/STJSONParser.swiftnew file+663

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSON/main.swiftnew file+47

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSONTests/Info.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/STJSONTests/STJSONTests.swiftnew file+92

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_STJSON/bin/STJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Info.plistnew file+563

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/MacOS/Squeaknew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/English.lproj/Localizable.stringsnew file+9

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/English.lproj/MainMenu.nibnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/Squeak.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakChanges.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakGeneric.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakImage.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakPlugin.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakProject.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakScript.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak.app/Contents/Resources/SqueakSources.icnsnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/Squeak5.1-16549-32bit.imagenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_Squeak_JSON_tonyg/test_JSON.stnew file+9

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/bin/test_TouchJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON.xcodeproj/project.pbxprojnew file+262

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_TouchJSON.xcschemenew file+97

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON/CJSONDeserializer.hnew file+96

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON/CJSONDeserializer.mnew file+1240

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_TouchJSON/test_TouchJSON/main.mnew file+25

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/bin/test-cJSONnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_cJSON/cJSON.cnew file+754

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/cJSON.hnew file+153

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/project.pbxprojnew file+257

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlistnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test-cJSON.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_cJSON/test-cJSON/main.cnew file+69

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/bin/test_ccannew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/common.hnew file+18

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_10.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_101.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_11.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_12.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_125.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_127.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_13.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_136.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_137.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_139.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_14.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_140.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_145.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_147.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_149.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_15.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_150.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_151.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_153.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_154.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_155.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_156.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_157.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_158.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_159.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_16.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_160.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_161.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_162.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_163.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_164.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_165.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_169.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_17.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_171.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_172.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_173.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_175.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_176.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_177.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_178.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_179.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_18.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_180.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_181.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_183.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_184.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_185.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_186.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_187.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_188.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_189.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_19.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_190.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_191.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_192.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_193.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_194.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_195.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_197.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_198.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_199.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_2.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_20.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_200.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_201.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_202.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_203.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_204.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_205.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_206.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_207.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_208.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_209.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_21.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_210.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_211.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_212.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_213.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_214.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_216.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_217.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_218.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_219.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_22.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_220.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_221.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_222.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_223.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_23.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_24.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_25.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_26.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_27.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_28.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_29.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_3.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_30.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_31.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_32.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_33.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_34.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_35.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_36.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_37.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_4.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_40.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_44.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_46.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_5.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_54.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_55.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_59.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_6.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_63.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_64.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_66.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_69.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_7.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_77.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_78.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_79.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_80.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_81.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_82.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_83.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_85.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_86.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_87.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_9.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/n_91.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_100.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_102.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_103.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_104.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_105.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_106.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_107.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_108.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_109.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_110.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_111.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_112.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_113.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_114.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_115.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_116.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_117.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_118.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_119.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_120.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_121.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_122.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_123.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_124.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_126.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_128.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_129.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_130.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_131.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_132.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_133.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_134.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_135.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_138.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_141.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_142.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_143.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_144.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_146.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_148.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_152.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_166.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_167.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_168.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_170.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_174.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_182.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_196.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_215.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_38.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_39.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_41.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_42.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_43.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_45.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_47.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_48.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_49.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_50.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_51.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_52.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_53.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_56.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_57.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_58.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_60.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_61.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_62.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_65.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_67.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_68.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_70.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_71.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_72.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_73.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_74.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_75.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_76.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_84.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_88.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_89.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_90.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_92.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_93.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_94.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_95.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_96.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_97.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_98.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/nst_files/y_99.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/run-construction.cnew file+191

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/run-decode-encode.cnew file+77

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/run-stringify.cnew file+108

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/run-validate.cnew file+49

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/test-stringsnew file+224

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/test-strings-reencodednew file+90

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/_test/x.pynew file+21

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/json.cnew file+1381

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/json/json.hnew file+117

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan.xcodeproj/project.pbxprojnew file+262

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_ccan.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_ccan_json/test_ccan/test_ccan/main.cnew file+44

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_dkjson.luanew file+25

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_go/test_jsonnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_go/test_json.gonew file+30

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/.gitignorenew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/package.yamlnew file+12

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/src/Main.hsnew file+29

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/stack.yamlnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/testaesonnew file+8

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_haskell-aeson/testaeson.cabalnew file+21

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/bin/test_janssonnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jansson/dump.cnew file+467

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/error.cnew file+63

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/hashtable.cnew file+356

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/hashtable.hnew file+184

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/hashtable_seed.cnew file+277

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/jansson.hnew file+298

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/jansson_config.hnew file+43

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/jansson_private.hnew file+109

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/load.cnew file+1110

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/lookup3.hnew file+381

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/memory.cnew file+69

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/pack_unpack.cnew file+871

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/strbuffer.cnew file+111

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/strbuffer.hnew file+34

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/strconv.cnew file+145

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/project.pbxprojnew file+309

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlistnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_jansson.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/test_jansson/main.cnew file+208

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/utf.cnew file+187

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/utf.hnew file+27

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jansson/value.cnew file+1048

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/META-INF/MANIFEST.MFnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/README.txtnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/TestJSONParsing.classnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/TestJSONParsing.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/TestJSONParsing.javanew file+40

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_com_leastfixedpoint_json_1_0/com.leastfixedpoint.json-1.0.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_gson_2_7/META-INF/MANIFEST.MFnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_gson_2_7/README.txtnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_gson_2_7/TestJSONParsing.classnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_gson_2_7/TestJSONParsing.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_gson_2_7/TestJSONParsing.javanew file+51

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_gson_2_7/gson-2.7.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/META-INF/MANIFEST.MFnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/README.txtnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/TestJSONParsing.classnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/TestJSONParsing.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/TestJSONParsing.javanew file+48

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/jackson-annotations-2.2.3.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/jackson-core-2.8.4.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_jackson_2_8_4/jackson-databind-2.3.1.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/META-INF/MANIFEST.MFnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/README.txtnew file+5

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/TestJSONParsing.classnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/TestJSONParsing.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/TestJSONParsing.javanew file+45

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_nanojson_1_0/nanojson-1.0.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/META-INF/MANIFEST.MFnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/README.txtnew file+3

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/TestJSONParsing.classnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/TestJSONParsing.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/TestJSONParsing.javanew file+43

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_java_simple_json_1_1_1/json-simple-1.1.1.jarnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsmn/bin/test_jsmnnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsmn/jsmn.cnew file+311

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/jsmn.hnew file+76

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/project.pbxprojnew file+253

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/xcuserdata/nst.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlistnew file+23

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_jsmn.xcschemenew file+101

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsmn/test_jsmn/test_jsmn/main.cnew file+50

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-jq.pynew file+34

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/bin/test_json-parsernew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser.xcodeproj/project.pbxprojnew file+258

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/test_json-parser.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser/json.cnew file+1011

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser/json.hnew file+283

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-parser/test_json-parser/main.cnew file+66

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust-serde_json/rj/.gitignorenew file+2

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust-serde_json/rj/Cargo.tomlnew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust-serde_json/rj/READMEnew file+2

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust-serde_json/rj/src/main.rsnew file+30

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/Cargo.locknew file+14

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/Cargo.tomlnew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/READMEnew file+2

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/src/main.rsnew file+42

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.cargo-locknew file

No changes to the content.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/json-74a82b4ec4439e90/dep-lib-jsonnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/json-74a82b4ec4439e90/lib-jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/json-74a82b4ec4439e90/lib-json.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-b5d63a9d8da2968e/bin-tjnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-b5d63a9d8da2968e/bin-tj.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-b5d63a9d8da2968e/dep-bin-tjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bb890ae6f101bb25/bin-tjnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bb890ae6f101bb25/bin-tj.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bb890ae6f101bb25/dep-bin-tjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bf24d0bdc8d0f4d6/bin-tjnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bf24d0bdc8d0f4d6/bin-tj.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/.fingerprint/tj-bf24d0bdc8d0f4d6/dep-bin-tjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/deps/libjson-6bbf4c11dd6d7c25.rlibnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/tjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/tj.dSYM/Contents/Info.plistnew file+20

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rust/target/debug/tj.dSYM/Contents/Resources/DWARF/tjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/Cargo.locknew file+14

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/Cargo.tomlnew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/READMEnew file+2

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/src/main.rsnew file+32

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.cargo-locknew file

No changes to the content.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-8f8b750d20f3bf10/bin-rjnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-8f8b750d20f3bf10/bin-rj.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-8f8b750d20f3bf10/dep-bin-rjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-da0f8f63b87cf531/bin-rjnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-da0f8f63b87cf531/bin-rj.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rj-da0f8f63b87cf531/dep-bin-rjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rustc-serialize-80c565222c5ccdbb/dep-lib-rustc-serializenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rustc-serialize-80c565222c5ccdbb/lib-rustc-serializenew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/.fingerprint/rustc-serialize-80c565222c5ccdbb/lib-rustc-serialize.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/deps/librustc_serialize-3561541d79c18212.rlibnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/rjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/rj.dSYM/Contents/Info.plistnew file+20

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json-rustc_serialize/rj/target/debug/rj.dSYM/Contents/Resources/DWARF/rjnew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_json.crnew file+19

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json.jsnew file+13

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json.phpnew file+21

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json.plnew file+32

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json.pynew file+39

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json.rbnew file+23

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/bin/jsonCheckernew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/JSON_checker.cnew file+400

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/JSON_checker.hnew file+13

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker.xcodeproj/project.pbxprojnew file+253

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/jsonChecker.xcschemenew file+91

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker/jsonChecker/jsonChecker/main.cnew file+53

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/FSM.rtfnew file+103

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/bin/jsonChecker2new file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/JSON_checker.cnew file+402

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/JSON_checker.hnew file+13

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker/main.cnew file+53

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker2.xcodeproj/project.pbxprojnew file+253

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker2.xcodeproj/project.xcworkspace/contents.xcworkspacedatanew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker2.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/UserInterfaceState.xcuserstatenew file

Binary file not shown.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker2.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/jsonChecker.xcschemenew file+97

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonChecker2/jsonChecker2/jsonChecker2.xcodeproj/xcuserdata/nst.xcuserdatad/xcschemes/xcschememanagement.plistnew file+22

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json_re.rbnew file+33

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_json_xs.plnew file+28

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_jsonlite.rnew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_oj_compat.rbnew file+25

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_oj_strict.rbnew file+25

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_qt/build.shnew file+4

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_qt/main.cppnew file+25

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_qt/test_qt.pronew file+12

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_rjson.rnew file+7

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_yajl.rbnew file+24

This diff is too big to show here.

tests/JSONTestSuite/results/logs.txtnew file+2048

This diff is too big to show here.

tests/JSONTestSuite/results/parsing.htmlnew file+2816

This diff is too big to show here.

tests/JSONTestSuite/results/parsing_pruned.htmlnew file+2661

This diff is too big to show here.

tests/JSONTestSuite/results/pruned_results.pngnew file

Binary file not shown.

tests/JSONTestSuite/results/style.cssnew file+89

This diff is too big to show here.

tests/JSONTestSuite/results/transform.htmlnew file+1492

This diff is too big to show here.

tests/JSONTestSuite/run_tests.pynew file+673

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_object_key_lone_2nd_surrogate.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_1st_surrogate_but_2nd_missing.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_UTF-16LE_with_BOM.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/i_string_UTF-8_invalid_sequence.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_incomplete_surrogate_and_escape_valid.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_incomplete_surrogate_pair.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_incomplete_surrogates_escape_valid.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_invalid_lonely_surrogate.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_invalid_surrogate.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_inverted_surrogates_U+1D11E.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_lone_second_surrogate.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_not_in_unicode_range.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_truncated-utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_unicode_U+10FFFE_nonchar.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_unicode_U+1FFFE_nonchar.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_unicode_U+FDD0_nonchar.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_string_unicode_U+FFFE_nonchar.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_structure_500_nested_arrays.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/i_structure_UTF-8_BOM_empty_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_1_true_without_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_a_invalid_utf8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_colon_instead_of_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_comma_after_close.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_comma_and_number.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_double_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_double_extra_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_extra_close.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_extra_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_incomplete.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_incomplete_invalid_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_inner_array_no_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_invalid_utf8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_items_separated_by_semicolon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_just_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_just_minus.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_missing_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_newlines_unclosed.jsonnew file+3

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_number_and_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_number_and_several_commas.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_spaces_vertical_tab_formfeed.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_star_inside.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_unclosed.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_unclosed_trailing_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_unclosed_with_new_lines.jsonnew file+3

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_array_unclosed_with_object_inside.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_incomplete_false.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_incomplete_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_incomplete_true.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_++.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_+1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_+Inf.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_-01.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_-1.0..jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_-2..jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_-NaN.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_.-1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_.2e-3.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0.1.2.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0.3e+.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0.3e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0.e1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0_capital_E+.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0_capital_E.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0e+.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_0e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_1.0e+.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_1.0e-.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_1.0e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_1_000.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_1eE2.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_2.e+3.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_2.e-3.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_2.e3.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_9.e+.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_Inf.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_NaN.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_U+FF11_fullwidth_digit_one.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_expression.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_hex_1_digit.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_hex_2_digits.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_infinity.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_invalid+-.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_invalid-negative-real.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_invalid-utf-8-in-bigger-int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_invalid-utf-8-in-exponent.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_invalid-utf-8-in-int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_minus_infinity.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_minus_sign_with_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_minus_space_1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_neg_int_starting_with_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_neg_real_without_int_part.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_neg_with_garbage_at_end.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_real_garbage_after_e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_real_with_invalid_utf8_after_e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_real_without_fractional_part.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_starting_with_dot.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_then_00.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/n_number_with_alpha.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_with_alpha_char.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_number_with_leading_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_bad_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_bracket_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_comma_instead_of_colon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_double_colon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_emoji.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_garbage_at_end.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_key_with_single_quotes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_missing_colon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_missing_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_missing_semicolon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_missing_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_no-colon.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_non_string_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_non_string_key_but_huge_number_instead.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_pi_in_key_and_trailing_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_repeated_null_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_several_trailing_commas.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_single_quote.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_trailing_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_trailing_comment.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_trailing_comment_open.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_trailing_comment_slash_open.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_trailing_comment_slash_open_incomplete.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_two_commas_in_a_row.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_unquoted_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_unterminated-value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_with_single_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_object_with_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_single_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_1_surrogate_then_escape u.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_1_surrogate_then_escape u1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_1_surrogate_then_escape u1x.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_1_surrogate_then_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_UTF8_surrogate_U+D800.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_accentuated_char_no_quotes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_backslash_00.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/n_string_escape_x.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_escaped_backslash_bad.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_escaped_ctrl_char_tab.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_escaped_emoji.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_incomplete_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_incomplete_escaped_character.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_incomplete_surrogate.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_incomplete_surrogate_escape_invalid.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_invalid-utf-8-in-escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_invalid_backslash_esc.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_invalid_unicode_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_invalid_utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_invalid_utf8_after_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_iso_latin_1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_leading_uescaped_thinspace.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_lone_utf8_continuation_byte.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_no_quotes_with_bad_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_overlong_sequence_2_bytes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_overlong_sequence_6_bytes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_overlong_sequence_6_bytes_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_single_doublequote.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_single_quote.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_single_string_no_double_quotes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_start_escape_unclosed.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_unescaped_crtl_char.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/n_string_unescaped_newline.jsonnew file+2

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_unescaped_tab.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_unicode_CapitalU.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_string_with_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_..jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_100000_opening_arrays.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_U+2060_word_joined.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_UTF8_BOM_no_data.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_array_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_array_with_extra_array_close.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_array_with_unclosed_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_ascii-unicode-identifier.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_capitalized_True.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_close_unopened_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_comma_instead_of_closing_brace.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_double_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_end_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_incomplete_UTF8_BOM.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_lone-invalid-utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_lone-open-bracket.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_no_data.jsonnew file

No changes to the content.

tests/JSONTestSuite/test_parsing/n_structure_null-byte-outside-string.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/n_structure_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_number_with_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_object_followed_by_closing_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_object_unclosed_no_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_object_with_comment.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_object_with_trailing_garbage.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_apostrophe.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_open_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_open_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_array_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object_close_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object_comma.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object_open_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object_open_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_object_string_with_apostrophes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_open_open.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_single_point.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_single_star.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_trailing_#.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_uescaped_LF_before_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unclosed_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unclosed_array_partial_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unclosed_array_unfinished_false.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unclosed_array_unfinished_true.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unclosed_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_unicode-identifier.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_whitespace_U+2060_word_joiner.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/n_structure_whitespace_formfeed.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_arraysWithSpaces.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_empty-string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_empty.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_ending_with_newline.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_false.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_heterogeneous.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_with_1_and_newline.jsonnew file+2

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_with_leading_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_with_several_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_array_with_trailing_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_0e+1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_0e1.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_after_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_double_close_to_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_double_huge_neg_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_huge_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_int_with_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_minus_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_neg_int_huge_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_negative_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_negative_one.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_negative_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_pos_double_huge_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_capital_e.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_capital_e_neg_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_capital_e_pos_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_exponent.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_fraction_exponent.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_neg_exp.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_neg_overflow.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_pos_exponent.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_pos_overflow.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_real_underflow.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_simple_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_simple_real.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_too_big_neg_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_too_big_pos_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_number_very_big_negative_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_basic.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_duplicated_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_duplicated_key_and_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_empty.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_empty_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_escaped_null_in_key.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_extreme_numbers.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_long_strings.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_simple.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_string_unicode.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_object_with_newlines.jsonnew file+3

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_accepted_surrogate_pair.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_accepted_surrogate_pairs.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_allowed_escapes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_backslash_and_u_escaped_zero.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_backslash_doublequotes.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_comments.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_double_escape_a.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_double_escape_n.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_escaped_control_character.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_escaped_noncharacter.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_in_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_in_array_with_leading_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_last_surrogates_1_and_2.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_newline_uescaped.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_nonCharacterInUTF-8_U+1FFFF.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_null_escape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_one-byte-utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_pi.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_simple_ascii.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_space.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_three-byte-utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_two-byte-utf-8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_u+2028_line_sep.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_u+2029_par_sep.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_uEscape.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unescaped_char_delete.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicode.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicodeEscapedBackslash.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicode_2.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicode_U+2064_invisible_plus.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_unicode_escaped_double_quote.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_utf16BE_no_BOM.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/y_string_utf16LE_no_BOM.jsonnew file

Binary file not shown.

tests/JSONTestSuite/test_parsing/y_string_utf8.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_string_with_del_character.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_false.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_int.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_negative_real.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_null.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_string.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_lonely_true.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_string_empty.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_trailing_newline.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_true_in_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_parsing/y_structure_whitespace_array.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_1.0.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_1.000000000000000005.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_1000000000000000.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_10000000000000000999.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_1e-999.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/number_1e6.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/object_key_nfc_nfd.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/object_key_nfd_nfc.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/object_same_key_different_values.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/object_same_key_same_value.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/object_same_key_unclear_values.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_1_escaped_invalid_codepoint.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_1_invalid_codepoint.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_2_escaped_invalid_codepoints.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_2_invalid_codepoints.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_3_escaped_invalid_codepoints.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_3_invalid_codepoints.jsonnew file+1

This diff is too big to show here.

tests/JSONTestSuite/test_transform/string_with_escaped_NULL.jsonnew file+1

This diff is too big to show here.

tests/Options.hs+28−1

This diff is too big to show here.

tests/PropUtils.hsnew file+208

This diff is too big to show here.

tests/Properties.hs+17−444

This diff is too big to show here.

tests/PropertyGeneric.hsnew file+116

This diff is too big to show here.

tests/PropertyKeys.hsnew file+40

This diff is too big to show here.

tests/PropertyRTFunctors.hsnew file+51

This diff is too big to show here.

tests/PropertyRoundTrip.hsnew file+74

This diff is too big to show here.

tests/PropertyTH.hsnew file+130

This diff is too big to show here.

tests/SerializationFormatSpec.hsnew file+294

This diff is too big to show here.

tests/Tests.hs+7−2

This diff is too big to show here.

tests/Types.hs+72−4

This diff is too big to show here.

tests/UnitTests.hs+424−240

This diff is too big to show here.

tests/UnitTests/NullaryConstructors.hs+10−2

This diff is too big to show here.

tests/golden/generic.expectednew file+48

This diff is too big to show here.

tests/golden/simple.expectednew file+22

This diff is too big to show here.

travis/cabal_macros.hnew file+448

This diff is too big to show here.

travis/install.sh+11

This diff is too big to show here.

travis/script.sh+8−1

This diff is too big to show here.