on master help sign in
.gitattributesnew file+1
since splitsnapshots
unifiedsplit
@@ -0,0 +1 @@
1+tests/JSONTestSuite/* linguist-vendored
.gitignore+11
@@ -1,10 +1,21 @@
1dist
1dist
2+dist-newstyle
3+.ghc.environment.*
2.cabal-sandbox/
4.cabal-sandbox/
3cabal.sandbox.config
5cabal.sandbox.config
6+.stack-work/
7+.stack-work-bench/
8+*.yaml.lock
4
9
5*.o
10*.o
6*.hi
11*.hi
7*.nix
12*.nix
13+*.swp
14+.\#*
15+\#*
16+
17+# Emacs backup files
18+*~
8
19
9benchmarks/AesonEncode
20benchmarks/AesonEncode
10
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.16 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.16,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.16 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.16,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
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.mdnew file+98
@@ -0,0 +1,98 @@
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+
15+# Style guide
16+
17+* 4 space indentation
18+
19+## Pragmas
20+
21+* One pragma on each line
22+* No alignment
23+* Sort lexicographically
24+
25+```haskell
26+{-# LANGUAGE BangPatterns #-}
27+{-# LANGUAGE CPP #-}
28+{-# LANGUAGE DataKinds #-}
29+```
30+
31+## Module declaration and exports
32+
33+* Opening paren on its own line
34+* Prefix with commas
35+* End with `) where`
36+
37+```haskell
38+module Data.Aeson.Types.ToJSON
39+ (
40+ -- * Core JSON classes
41+ ToJSON(..)
42+ -- * Liftings to unary and binary type constructors
43+ , ToJSON1(..)
44+ , toJSON1
45+ ) where
46+```
47+
48+## Imports
49+
50+* No alignment of import lists or qualifications
51+* Sort lexicographically (qualified imports will be last)
52+* No space between types and constructor lists
53+* Sort identifiers in import lists lexicographically
54+
55+```haskell
56+import BarBar as B ((<>), foo)
57+import Foo as F
58+import Qux (Q(..), q)
59+import qualified A
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+99−15
@@ -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
@@ -75,20 +88,46 @@ module Data.Aeson
75 , toEncoding2
88 , toEncoding2
76 -- ** Generic JSON classes and options
89 -- ** Generic JSON classes and options
77 , GFromJSON(..)
90 , GFromJSON(..)
91+ , FromArgs(..)
78− , GToJSON(..)
92+ , GToJSON
79− , GToEncoding(..)
93+ , GToEncoding
94+ , ToArgs(..)
95+ , Zero
96+ , One
80 , genericToJSON
97 , genericToJSON
98+ , genericLiftToJSON
81 , genericToEncoding
99 , genericToEncoding
100+ , genericLiftToEncoding
82 , genericParseJSON
101 , genericParseJSON
102+ , genericLiftParseJSON
103+ -- ** Generic and TH encoding configuration
104+ , Options
83 , 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
84
123
85 -- * Inspecting @'Value's@
124 -- * Inspecting @'Value's@
86 , withObject
125 , withObject
87 , withText
126 , withText
88 , withArray
127 , withArray
89− , withNumber
90 , withScientific
128 , withScientific
91 , withBool
129 , withBool
130+ , withEmbeddedJSON
92 -- * Constructors and accessors
131 -- * Constructors and accessors
93 , Series
132 , Series
94 , pairs
133 , pairs
@@ -101,18 +140,29 @@ module Data.Aeson
101 -- * Parsing
140 -- * Parsing
102 , json
141 , json
103 , json'
142 , json'
143+ , parseIndexedJSON
104 ) where
144 ) where
105
145
146+import Prelude.Compat
147+
106−import Data.Aeson.Types.FromJSON (ifromJSON)
148+import Data.Aeson.Types.FromJSON (ifromJSON, parseIndexedJSON)
107−import Data.Aeson.Encode (encode)
149+import Data.Aeson.Encoding (encodingToLazyByteString)
108−import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith,
150+import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
109− eitherDecodeWith, eitherDecodeStrictWith,
110− jsonEOF, json, jsonEOF', json')
111import Data.Aeson.Types
151import Data.Aeson.Types
112−import Data.Aeson.Types.Internal (JSONPath, formatError)
152+import Data.Aeson.Types.Internal (JSONPath, formatError, (<?>))
113import qualified Data.ByteString as B
153import qualified Data.ByteString as B
114import qualified Data.ByteString.Lazy as L
154import qualified Data.ByteString.Lazy as L
115
155
156+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
157+--
158+-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
159+encode :: (ToJSON a) => a -> L.ByteString
160+encode = encodingToLazyByteString . toEncoding
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+
116-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
166-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
117-- If this fails due to incomplete or invalid input, 'Nothing' is
167-- If this fails due to incomplete or invalid input, 'Nothing' is
118-- returned.
168-- returned.
@@ -139,6 +189,18 @@ decodeStrict :: (FromJSON a) => B.ByteString -> Maybe a
139decodeStrict = decodeStrictWith jsonEOF fromJSON
189decodeStrict = decodeStrictWith jsonEOF fromJSON
140{-# INLINE decodeStrict #-}
190{-# INLINE decodeStrict #-}
141
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+
142-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
204-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
143-- If this fails due to incomplete or invalid input, 'Nothing' is
205-- If this fails due to incomplete or invalid input, 'Nothing' is
144-- returned.
206-- returned.
@@ -165,6 +227,18 @@ decodeStrict' :: (FromJSON a) => B.ByteString -> Maybe a
165decodeStrict' = decodeStrictWith jsonEOF' fromJSON
227decodeStrict' = decodeStrictWith jsonEOF' fromJSON
166{-# INLINE decodeStrict' #-}
228{-# INLINE decodeStrict' #-}
167
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+
168eitherFormatError :: Either (JSONPath, String) a -> Either String a
242eitherFormatError :: Either (JSONPath, String) a -> Either String a
169eitherFormatError = either (Left . uncurry formatError) Right
243eitherFormatError = either (Left . uncurry formatError) Right
170{-# INLINE eitherFormatError #-}
244{-# INLINE eitherFormatError #-}
@@ -180,6 +254,12 @@ eitherDecodeStrict =
180 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
254 eitherFormatError . eitherDecodeStrictWith jsonEOF ifromJSON
181{-# INLINE eitherDecodeStrict #-}
255{-# INLINE eitherDecodeStrict #-}
182
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+
183-- | Like 'decode'' but returns an error message when decoding fails.
263-- | Like 'decode'' but returns an error message when decoding fails.
184eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
264eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a
185eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
265eitherDecode' = eitherFormatError . eitherDecodeWith jsonEOF' ifromJSON
@@ -191,6 +271,12 @@ eitherDecodeStrict' =
191 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
271 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
192{-# INLINE eitherDecodeStrict' #-}
272{-# INLINE eitherDecodeStrict' #-}
193
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+
194-- $use
280-- $use
195--
281--
196-- This section contains basic information on the different ways to
282-- This section contains basic information on the different ways to
@@ -260,11 +346,9 @@ eitherDecodeStrict' =
260-- > {-# LANGUAGE OverloadedStrings #-}
346-- > {-# LANGUAGE OverloadedStrings #-}
261-- >
347-- >
262-- > instance FromJSON Person where
348-- > instance FromJSON Person where
263−-- > parseJSON (Object v) = Person <$>
349+-- > parseJSON = withObject "Person" $ \v -> Person
264−-- > v .: "name" <*>
350+-- > <$> v .: "name"
265−-- > v .: "age"
351+-- > <*> v .: "age"
266−-- > -- A non-Object value is of the wrong type, so fail.
267−-- > parseJSON _ = empty
268--
352--
269-- We can now parse the JSON data like so:
353-- We can now parse the JSON data like so:
270--
354--
@@ -354,7 +438,7 @@ eitherDecodeStrict' =
354-- The 'Object' type contains JSON objects:
438-- The 'Object' type contains JSON objects:
355--
439--
356-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
440-- > λ> decode "{\"name\":\"Dave\",\"age\":2}" :: Maybe Object
357−-- > Just (fromList) [("name",String "Dave"),("age",Number 2)]
441+-- > Just (fromList [("name",String "Dave"),("age",Number 2)])
358--
442--
359-- You can extract values from it with a parser using 'parse',
443-- You can extract values from it with a parser using 'parse',
360-- 'parseEither' or, in this example, 'parseMaybe':
444-- 'parseEither' or, in this example, 'parseMaybe':
Data/Aeson/Encode.hs+15−94
@@ -1,5 +1,4 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2−
3-- |
2-- |
4-- Module: Data.Aeson.Encode
3-- Module: Data.Aeson.Encode
5-- Copyright: (c) 2012-2016 Bryan O'Sullivan
4-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -9,102 +8,24 @@
9-- Stability: experimental
8-- Stability: experimental
10-- Portability: portable
9-- Portability: portable
11--
10--
12−-- Efficiently serialize a JSON value.
11+-- This module is left to supply limited backwards-compatibility.
13−--
14−-- Most frequently, you'll probably want to encode straight to UTF-8
15−-- (the standard JSON encoding) using 'encode'.
16−--
17−-- You can use the conversions to 'Builder's when embedding JSON messages as
18−-- parts of a protocol.
19−module Data.Aeson.Encode
20− ( encode
21
12
22− -- * Encoding to Builders
13+module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
23− , encodeToBuilder
14+ (
15+ encode
24 , encodeToTextBuilder
16 , encodeToTextBuilder
25−
26− -- * Deprecated
27− , fromValue
28 ) where
17 ) where
29
18
30−import Data.Aeson.Encode.Builder (encodeToBuilder)
31−import Data.Aeson.Types (Value(..), ToJSON (..))
32−import Data.Aeson.Encoding (fromEncoding)
33−import Data.Monoid ((<>))
34−import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
35−import Data.Text.Lazy.Builder
36−import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
37−
38−import Numeric (showHex)
39−import qualified Data.ByteString.Builder as B
40−import qualified Data.HashMap.Strict as H
41−import qualified Data.Text as T
42−import qualified Data.Vector as V
43−import qualified Data.ByteString.Lazy as L
44−
45−-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
46−--
47−-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
48−encode :: ToJSON a => a -> L.ByteString
49−encode = B.toLazyByteString . fromEncoding . toEncoding
50−{-# INLINE encode #-}
51−
52−-- | Encode a JSON 'Value' to a "Data.Text" 'Builder', which can be
53−-- embedded efficiently in a text-based protocol.
54−--
55−-- If you are going to immediately encode straight to a
56−-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
57−-- instead.
58−encodeToTextBuilder :: Value -> Builder
59−encodeToTextBuilder =
60− go
61− where
62− go Null = {-# SCC "go/Null" #-} "null"
63− go (Bool b) = {-# SCC "go/Bool" #-} if b then "true" else "false"
64− go (Number s) = {-# SCC "go/Number" #-} fromScientific s
65− go (String s) = {-# SCC "go/String" #-} string s
66− go (Array v)
67− | V.null v = {-# SCC "go/Array" #-} "[]"
68− | otherwise = {-# SCC "go/Array" #-}
69− singleton '[' <>
70− go (V.unsafeHead v) <>
71− V.foldr f (singleton ']') (V.unsafeTail v)
72− where f a z = singleton ',' <> go a <> z
73− go (Object m) = {-# SCC "go/Object" #-}
74− case H.toList m of
75− (x:xs) -> singleton '{' <> one x <> foldr f (singleton '}') xs
76− _ -> "{}"
77− where f a z = singleton ',' <> one a <> z
78− one (k,v) = string k <> singleton ':' <> go v
79−
80−{-# DEPRECATED fromValue "Use 'encodeToTextBuilder' instead" #-}
81−fromValue :: Value -> Builder
82−fromValue = encodeToTextBuilder
83
19
84−string :: T.Text -> Builder
20+import Data.ByteString.Lazy (ByteString)
85−string s = {-# SCC "string" #-} singleton '"' <> quote s <> singleton '"'
21+import Data.Text.Lazy.Builder (Builder)
86− where
22+import qualified Data.Aeson as A
87− quote q = case T.uncons t of
23+import qualified Data.Aeson.Text as A
88− Nothing -> fromText h
89− Just (!c,t') -> fromText h <> escape c <> quote t'
90− where (h,t) = {-# SCC "break" #-} T.break isEscape q
91− isEscape c = c == '\"' ||
92− c == '\\' ||
93− c < '\x20'
94− escape '\"' = "\\\""
95− escape '\\' = "\\\\"
96− escape '\n' = "\\n"
97− escape '\r' = "\\r"
98− escape '\t' = "\\t"
99
24
100− escape c
25+encode :: A.ToJSON a => a -> ByteString
101− | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
26+encode = A.encode
102− | otherwise = singleton c
27+{-# DEPRECATED encode "Use encode from Data.Aeson" #-}
103− where h = showHex (fromEnum c) ""
104
28
105−fromScientific :: Scientific -> Builder
29+encodeToTextBuilder :: A.Value -> Builder
106−fromScientific s = formatScientificBuilder format prec s
30+encodeToTextBuilder = A.encodeToTextBuilder
107− where
31+{-# DEPRECATED encodeToTextBuilder "Use encodeTotextBuilder from Data.Aeson.Text" #-}
108− (format, prec)
109− | base10Exponent s < 0 = (Generic, Nothing)
110− | otherwise = (Fixed, Just 0)
Data/Aeson/Encode/Functions.hsdeleted−14
@@ -1,14 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−
3−module Data.Aeson.Encode.Functions
4− (
5− builder
6− ) where
7−
8−import Data.Aeson.Encoding
9−import Data.Aeson.Types.Class
10−import Data.ByteString.Builder (Builder)
11−import Data.Monoid ((<>))
12−
13−
14−{-# INLINE builder #-}
Data/Aeson/Encoding.hs+36
@@ -1,19 +1,55 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+-- |
3+--
4+-- Functions in this module return well-formed 'Encoding''.
5+-- Polymorphic variants, which return @'Encoding' a@, return a textual JSON
6+-- value, so it can be used as both @'Encoding'' 'Text'@ and @'Encoding' = 'Encoding'' 'Value'@.
7+
1module Data.Aeson.Encoding
8module Data.Aeson.Encoding
2 (
9 (
3 -- * Encoding
10 -- * Encoding
4 Encoding
11 Encoding
12+ , Encoding'
13+ , encodingToLazyByteString
5 , fromEncoding
14 , fromEncoding
6 , unsafeToEncoding
15 , unsafeToEncoding
7 , Series
16 , Series
8 , pairs
17 , pairs
18+ , pair
19+ , pairStr
20+ , pair'
9 -- * Predicates
21 -- * Predicates
10 , nullEncoding
22 , nullEncoding
11 -- * Encoding constructors
23 -- * Encoding constructors
12 , emptyArray_
24 , emptyArray_
13 , emptyObject_
25 , emptyObject_
14 , text
26 , text
27+ , lazyText
28+ , string
15 , list
29 , list
16 , dict
30 , dict
31+ , null_
32+ , bool
33+ -- ** Decimal numbers
34+ , int8, int16, int32, int64, int
35+ , word8, word16, word32, word64, word
36+ , integer, float, double, scientific
37+
38+ -- ** Decimal numbers as Text
39+ , int8Text, int16Text, int32Text, int64Text, intText
40+ , word8Text, word16Text, word32Text, word64Text, wordText
41+ , integerText, floatText, doubleText, scientificText
42+
43+ -- ** Time
44+ , day
45+ , localTime
46+ , utcTime
47+ , timeOfDay
48+ , zonedTime
49+
50+ -- ** value
51+ , value
17 ) where
52 ) where
18
53
54+
19import Data.Aeson.Encoding.Internal
55import Data.Aeson.Encoding.Internal
Data/Aeson/Encode/Builder.hs → Data/Aeson/Encoding/Builder.hsrenamed+29−26
@@ -1,7 +1,8 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns #-}
2−
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE TupleSections #-}
3-- |
4-- |
4−-- Module: Data.Aeson.Encode.Builder
5+-- Module: Data.Aeson.Encoding.Builder
5-- Copyright: (c) 2011 MailRank, Inc.
6-- Copyright: (c) 2011 MailRank, Inc.
6-- (c) 2013 Simon Meier <iridcode@gmail.com>
7-- (c) 2013 Simon Meier <iridcode@gmail.com>
7-- License: BSD3
8-- License: BSD3
@@ -11,7 +12,7 @@
11--
12--
12-- Efficiently serialize a JSON value using the UTF-8 encoding.
13-- Efficiently serialize a JSON value using the UTF-8 encoding.
13
14
14−module Data.Aeson.Encode.Builder
15+module Data.Aeson.Encoding.Builder
15 (
16 (
16 encodeToBuilder
17 encodeToBuilder
17 , null_
18 , null_
@@ -24,7 +25,7 @@ module Data.Aeson.Encode.Builder
24 , string
25 , string
25 , unquoted
26 , unquoted
26 , quote
27 , quote
27− , number
28+ , scientific
28 , day
29 , day
29 , localTime
30 , localTime
30 , utcTime
31 , utcTime
@@ -35,21 +36,23 @@ module Data.Aeson.Encode.Builder
35 , ascii5
36 , ascii5
36 ) where
37 ) where
37
38
39+import Prelude.Compat
40+
38import Data.Aeson.Internal.Time
41import Data.Aeson.Internal.Time
39import Data.Aeson.Types.Internal (Value (..))
42import Data.Aeson.Types.Internal (Value (..))
40import Data.ByteString.Builder as B
43import Data.ByteString.Builder as B
41import Data.ByteString.Builder.Prim as BP
44import Data.ByteString.Builder.Prim as BP
42import Data.ByteString.Builder.Scientific (scientificBuilder)
45import Data.ByteString.Builder.Scientific (scientificBuilder)
43import Data.Char (chr, ord)
46import Data.Char (chr, ord)
44−import Data.Monoid ((<>))
47+import Data.Semigroup ((<>))
45import Data.Scientific (Scientific, base10Exponent, coefficient)
48import Data.Scientific (Scientific, base10Exponent, coefficient)
49+import Data.Text.Encoding (encodeUtf8BuilderEscaped)
46import Data.Time (UTCTime(..))
50import Data.Time (UTCTime(..))
47import Data.Time.Calendar (Day(..), toGregorian)
51import Data.Time.Calendar (Day(..), toGregorian)
48import Data.Time.LocalTime
52import Data.Time.LocalTime
49import Data.Word (Word8)
53import Data.Word (Word8)
50import qualified Data.HashMap.Strict as HMS
54import qualified Data.HashMap.Strict as HMS
51import qualified Data.Text as T
55import qualified Data.Text as T
52−import qualified Data.Text.Encoding as TE
53import qualified Data.Vector as V
56import qualified Data.Vector as V
54
57
55-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
58-- | Encode a JSON value to a "Data.ByteString" 'B.Builder'.
@@ -59,7 +62,7 @@ import qualified Data.Vector as V
59encodeToBuilder :: Value -> Builder
62encodeToBuilder :: Value -> Builder
60encodeToBuilder Null = null_
63encodeToBuilder Null = null_
61encodeToBuilder (Bool b) = bool b
64encodeToBuilder (Bool b) = bool b
62−encodeToBuilder (Number n) = number n
65+encodeToBuilder (Number n) = scientific n
63encodeToBuilder (String s) = text s
66encodeToBuilder (String s) = text s
64encodeToBuilder (Array v) = array v
67encodeToBuilder (Array v) = array v
65encodeToBuilder (Object m) = object m
68encodeToBuilder (Object m) = object m
@@ -98,7 +101,7 @@ text t = B.char8 '"' <> unquoted t <> B.char8 '"'
98
101
99-- | Encode a JSON string, without enclosing quotes.
102-- | Encode a JSON string, without enclosing quotes.
100unquoted :: T.Text -> Builder
103unquoted :: T.Text -> Builder
101−unquoted = TE.encodeUtf8BuilderEscaped escapeAscii
104+unquoted = encodeUtf8BuilderEscaped escapeAscii
102
105
103-- | Add quotes surrounding a builder
106-- | Add quotes surrounding a builder
104quote :: Builder -> Builder
107quote :: Builder -> Builder
@@ -117,7 +120,7 @@ escapeAscii =
117 BP.condB (== c2w '\n' ) (ascii2 ('\\','n' )) $
120 BP.condB (== c2w '\n' ) (ascii2 ('\\','n' )) $
118 BP.condB (== c2w '\r' ) (ascii2 ('\\','r' )) $
121 BP.condB (== c2w '\r' ) (ascii2 ('\\','r' )) $
119 BP.condB (== c2w '\t' ) (ascii2 ('\\','t' )) $
122 BP.condB (== c2w '\t' ) (ascii2 ('\\','t' )) $
120− (BP.liftFixedToBounded hexEscape) -- fallback for chars < 0x20
123+ BP.liftFixedToBounded hexEscape -- fallback for chars < 0x20
121 where
124 where
122 hexEscape :: BP.FixedPrim Word8
125 hexEscape :: BP.FixedPrim Word8
123 hexEscape = (\c -> ('\\', ('u', fromIntegral c))) BP.>$<
126 hexEscape = (\c -> ('\\', ('u', fromIntegral c))) BP.>$<
@@ -128,9 +131,9 @@ c2w :: Char -> Word8
128c2w c = fromIntegral (ord c)
131c2w c = fromIntegral (ord c)
129
132
130-- | Encode a JSON number.
133-- | Encode a JSON number.
131−number :: Scientific -> Builder
134+scientific :: Scientific -> Builder
132−number s
135+scientific s
133− | e < 0 = scientificBuilder s
136+ | e < 0 || e > 1024 = scientificBuilder s
134 | otherwise = B.integerDec (coefficient s * 10 ^ e)
137 | otherwise = B.integerDec (coefficient s * 10 ^ e)
135 where
138 where
136 e = base10Exponent s
139 e = base10Exponent s
@@ -142,27 +145,27 @@ emptyObject_ :: Builder
142emptyObject_ = BP.primBounded (ascii2 ('{','}')) ()
145emptyObject_ = BP.primBounded (ascii2 ('{','}')) ()
143
146
144ascii2 :: (Char, Char) -> BP.BoundedPrim a
147ascii2 :: (Char, Char) -> BP.BoundedPrim a
145−ascii2 cs = BP.liftFixedToBounded $ (const cs) BP.>$< BP.char7 >*< BP.char7
148+ascii2 cs = BP.liftFixedToBounded $ const cs BP.>$< BP.char7 >*< BP.char7
146{-# INLINE ascii2 #-}
149{-# INLINE ascii2 #-}
147
150
148ascii4 :: (Char, (Char, (Char, Char))) -> BP.BoundedPrim a
151ascii4 :: (Char, (Char, (Char, Char))) -> BP.BoundedPrim a
149−ascii4 cs = BP.liftFixedToBounded $ (const cs) >$<
152+ascii4 cs = BP.liftFixedToBounded $ const cs >$<
150 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
153 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
151{-# INLINE ascii4 #-}
154{-# INLINE ascii4 #-}
152
155
153ascii5 :: (Char, (Char, (Char, (Char, Char)))) -> BP.BoundedPrim a
156ascii5 :: (Char, (Char, (Char, (Char, Char)))) -> BP.BoundedPrim a
154−ascii5 cs = BP.liftFixedToBounded $ (const cs) >$<
157+ascii5 cs = BP.liftFixedToBounded $ const cs >$<
155 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
158 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
156{-# INLINE ascii5 #-}
159{-# INLINE ascii5 #-}
157
160
158ascii6 :: (Char, (Char, (Char, (Char, (Char, Char))))) -> BP.BoundedPrim a
161ascii6 :: (Char, (Char, (Char, (Char, (Char, Char))))) -> BP.BoundedPrim a
159−ascii6 cs = BP.liftFixedToBounded $ (const cs) >$<
162+ascii6 cs = BP.liftFixedToBounded $ const cs >$<
160 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
163 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
161{-# INLINE ascii6 #-}
164{-# INLINE ascii6 #-}
162
165
163ascii8 :: (Char, (Char, (Char, (Char, (Char, (Char, (Char, Char)))))))
166ascii8 :: (Char, (Char, (Char, (Char, (Char, (Char, (Char, Char)))))))
164 -> BP.BoundedPrim a
167 -> BP.BoundedPrim a
165−ascii8 cs = BP.liftFixedToBounded $ (const cs) >$<
168+ascii8 cs = BP.liftFixedToBounded $ const cs >$<
166 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*<
169 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*<
167 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
170 BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
168{-# INLINE ascii8 #-}
171{-# INLINE ascii8 #-}
@@ -175,12 +178,13 @@ day dd = encodeYear yr <>
175 !(T dh dl) = twoDigits d
178 !(T dh dl) = twoDigits d
176 encodeYear y
179 encodeYear y
177 | y >= 1000 = B.integerDec y
180 | y >= 1000 = B.integerDec y
178− | 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 =
179− let (ab,c) = fromIntegral y `quotRem` 10
185+ let (ab,c) = fromIntegral y `quotRem` 10
180− (a,b) = ab `quotRem` 10
186+ (a,b) = ab `quotRem` 10
181− in BP.primBounded (ascii4 ('0',(digit a,(digit b,digit c)))) ()
187+ in ('0',(digit a,(digit b,digit c)))
182− | otherwise =
183− error "Data.Aeson.Encode.Builder.day: years BCE not supported"
184{-# INLINE day #-}
188{-# INLINE day #-}
185
189
186timeOfDay :: TimeOfDay -> Builder
190timeOfDay :: TimeOfDay -> Builder
@@ -197,7 +201,7 @@ timeOfDay64 (TOD h m s)
197 !(T mh ml) = twoDigits m
201 !(T mh ml) = twoDigits m
198 !(T sh sl) = twoDigits (fromIntegral real)
202 !(T sh sl) = twoDigits (fromIntegral real)
199 (real,frac) = s `quotRem` pico
203 (real,frac) = s `quotRem` pico
200− showFrac = (\x -> ('.', x)) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
204+ showFrac = ('.',) >$< (BP.liftFixedToBounded BP.char7 >*< trunc12)
201 trunc12 = (`quotRem` micro) >$<
205 trunc12 = (`quotRem` micro) >$<
202 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
206 BP.condB (\(_,y) -> y == 0) (fst >$< trunc6) (digits6 >*< trunc6)
203 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
207 digits6 = ((`quotRem` milli) . fromIntegral) >$< (digits3 >*< digits3)
@@ -215,7 +219,6 @@ timeOfDay64 (TOD h m s)
215 pico = 1000000000000 -- number of picoseconds in 1 second
219 pico = 1000000000000 -- number of picoseconds in 1 second
216 micro = 1000000 -- number of microseconds in 1 second
220 micro = 1000000 -- number of microseconds in 1 second
217 milli = 1000 -- number of milliseconds in 1 second
221 milli = 1000 -- number of milliseconds in 1 second
218−{-# INLINE timeOfDay64 #-}
219
222
220timeZone :: TimeZone -> Builder
223timeZone :: TimeZone -> Builder
221timeZone (TimeZone off _ _)
224timeZone (TimeZone off _ _)
Data/Aeson/Encoding/Internal.hs+258−53
@@ -1,65 +1,115 @@
1−{-# LANGUAGE CPP, GeneralizedNewtypeDeriving, DeriveDataTypeable, RankNTypes #-}
1+{-# LANGUAGE DeriveDataTypeable #-}
2+{-# LANGUAGE EmptyDataDecls #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE RankNTypes #-}
6+
2module Data.Aeson.Encoding.Internal
7module Data.Aeson.Encoding.Internal
3 (
8 (
4 -- * Encoding
9 -- * Encoding
5− Encoding (..) -- TODO: export fromEncoding for now
10+ Encoding' (..)
6− , fromEncoding
11+ , Encoding
12+ , encodingToLazyByteString
7 , unsafeToEncoding
13 , unsafeToEncoding
8− , Series (..) -- TODO: don't export constructor
14+ , retagEncoding
15+ , Series (..)
9 , pairs
16 , pairs
17+ , pair
18+ , pairStr
19+ , pair'
10 -- * Predicates
20 -- * Predicates
11 , nullEncoding
21 , nullEncoding
12 -- * Encoding constructors
22 -- * Encoding constructors
13 , emptyArray_
23 , emptyArray_
14 , emptyObject_
24 , emptyObject_
15− , wrapArray
16 , wrapObject
25 , wrapObject
26+ , wrapArray
27+ , null_
28+ , bool
17 , text
29 , text
30+ , lazyText
31+ , string
18 , list
32 , list
19 , dict
33 , dict
20 , tuple
34 , tuple
21 , (>*<)
35 , (>*<)
22− -- ** chars
36+ , InArray
37+ , empty
38+ , (><)
39+ , econcat
40+ -- ** Decimal numbers
41+ , int8, int16, int32, int64, int
42+ , word8, word16, word32, word64, word
43+ , integer, float, double, scientific
44+ -- ** Decimal numbers as Text
45+ , int8Text, int16Text, int32Text, int64Text, intText
46+ , word8Text, word16Text, word32Text, word64Text, wordText
47+ , integerText, floatText, doubleText, scientificText
48+ -- ** Time
49+ , day
50+ , localTime
51+ , utcTime
52+ , timeOfDay
53+ , zonedTime
54+ -- ** value
55+ , value
56+ -- ** JSON tokens
23 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
57 , comma, colon, openBracket, closeBracket, openCurly, closeCurly
24 ) where
58 ) where
25
59
26−#if !MIN_VERSION_base(4,8,0)
60+import Prelude.Compat
27−import Data.Monoid (Monoid(..))
28−#endif
29
61
30−import Data.Text (Text)
62+import Data.Aeson.Types.Internal (Value)
31import Data.ByteString.Builder (Builder, char7, toLazyByteString)
63import Data.ByteString.Builder (Builder, char7, toLazyByteString)
64+import Data.Int
65+import Data.Scientific (Scientific)
32−import Data.Semigroup (Semigroup((<>)))
66+import Data.Semigroup (Semigroup ((<>)))
33−import Data.ByteString.Builder.Prim (primBounded)
67+import Data.Text (Text)
68+import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
34import Data.Typeable (Typeable)
69import Data.Typeable (Typeable)
35−
70+import Data.Word
71+import qualified Data.Aeson.Encoding.Builder as EB
72+import qualified Data.ByteString.Builder as B
36import qualified Data.ByteString.Lazy as BSL
73import qualified Data.ByteString.Lazy as BSL
37−import qualified Data.Aeson.Encode.Builder as B
74+import qualified Data.Text.Lazy as LT
38
75
39-- | An encoding of a JSON value.
76-- | An encoding of a JSON value.
77+--
78+-- @tag@ represents which kind of JSON the Encoding is encoding to,
79+-- we reuse 'Text' and 'Value' as tags here.
40−newtype Encoding = Encoding {
80+newtype Encoding' tag = Encoding {
41 fromEncoding :: Builder
81 fromEncoding :: Builder
42 -- ^ Acquire the underlying bytestring builder.
82 -- ^ Acquire the underlying bytestring builder.
43− } deriving (Semigroup,Monoid,Typeable)
83+ } deriving (Typeable)
84+
85+-- | Often used synonym for 'Encoding''.
86+type Encoding = Encoding' Value
44
87
45-- | Make Encoding from Builder.
88-- | Make Encoding from Builder.
46--
89--
47-- Use with care! You have to make sure that the passed Builder
90-- Use with care! You have to make sure that the passed Builder
48-- is a valid JSON Encoding!
91-- is a valid JSON Encoding!
49−unsafeToEncoding :: Builder -> Encoding
92+unsafeToEncoding :: Builder -> Encoding' a
50unsafeToEncoding = Encoding
93unsafeToEncoding = Encoding
51
94
95+encodingToLazyByteString :: Encoding' a -> BSL.ByteString
96+encodingToLazyByteString = toLazyByteString . fromEncoding
97+{-# INLINE encodingToLazyByteString #-}
98+
99+retagEncoding :: Encoding' a -> Encoding' b
100+retagEncoding = Encoding . fromEncoding
101+
52-------------------------------------------------------------------------------
102-------------------------------------------------------------------------------
53-- Encoding instances
103-- Encoding instances
54-------------------------------------------------------------------------------
104-------------------------------------------------------------------------------
55
105
56−instance Show Encoding where
106+instance Show (Encoding' a) where
57 show (Encoding e) = show (toLazyByteString e)
107 show (Encoding e) = show (toLazyByteString e)
58
108
59−instance Eq Encoding where
109+instance Eq (Encoding' a) where
60 Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
110 Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
61
111
62−instance Ord Encoding where
112+instance Ord (Encoding' a) where
63 compare (Encoding a) (Encoding b) =
113 compare (Encoding a) (Encoding b) =
64 compare (toLazyByteString a) (toLazyByteString b)
114 compare (toLazyByteString a) (toLazyByteString b)
65
115
@@ -71,73 +121,97 @@ instance Ord Encoding where
71--
121--
72-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
122-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
73data Series = Empty
123data Series = Empty
74− | Value Encoding
124+ | Value (Encoding' Series)
75 deriving (Typeable)
125 deriving (Typeable)
76
126
127+pair :: 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
136+pair' name val = Value $ retagEncoding $ retagEncoding name >< colon >< val
137+
77instance Semigroup Series where
138instance Semigroup Series where
78− Empty <> a = a
139+ Empty <> a = a
79− Value a <> b =
140+ a <> Empty = a
80− Value $
141+ Value a <> Value b = Value (a >< comma >< b)
81− a <> case b of
82− Empty -> mempty
83− Value c -> Encoding (char7 ',') <> c
84
142
85instance Monoid Series where
143instance Monoid Series where
86 mempty = Empty
144 mempty = Empty
87 mappend = (<>)
145 mappend = (<>)
88
146
89−nullEncoding :: Encoding -> Bool
147+nullEncoding :: Encoding' a -> Bool
90nullEncoding = BSL.null . toLazyByteString . fromEncoding
148nullEncoding = BSL.null . toLazyByteString . fromEncoding
91
149
92emptyArray_ :: Encoding
150emptyArray_ :: Encoding
93−emptyArray_ = Encoding B.emptyArray_
151+emptyArray_ = Encoding EB.emptyArray_
94
152
95emptyObject_ :: Encoding
153emptyObject_ :: Encoding
96−emptyObject_ = Encoding B.emptyObject_
154+emptyObject_ = Encoding EB.emptyObject_
97
155
98−wrapArray :: Encoding -> Encoding
156+wrapArray :: Encoding' a -> Encoding
99−wrapArray e = openBracket <> e <> closeBracket
157+wrapArray e = retagEncoding $ openBracket >< e >< closeBracket
100
158
101−wrapObject :: Encoding -> Encoding
159+wrapObject :: Encoding' a -> Encoding
102−wrapObject e = openCurly <> e <> closeCurly
160+wrapObject e = retagEncoding $ openCurly >< e >< closeCurly
161+
162+null_ :: Encoding
163+null_ = Encoding EB.null_
164+
165+bool :: Bool -> Encoding
166+bool True = Encoding "true"
167+bool False = Encoding "false"
103
168
104-- | Encode a series of key/value pairs, separated by commas.
169-- | Encode a series of key/value pairs, separated by commas.
105pairs :: Series -> Encoding
170pairs :: Series -> Encoding
106−pairs = brackets '{' '}'
171+pairs (Value v) = openCurly >< retagEncoding v >< closeCurly
172+pairs Empty = emptyObject_
107{-# INLINE pairs #-}
173{-# INLINE pairs #-}
108
174
109−brackets :: Char -> Char -> Series -> Encoding
110−brackets begin end (Value v) = Encoding $
111− char7 begin <> fromEncoding v <> char7 end
112−brackets begin end Empty = Encoding (primBounded (B.ascii2 (begin,end)) ())
113−
114list :: (a -> Encoding) -> [a] -> Encoding
175list :: (a -> Encoding) -> [a] -> Encoding
115list _ [] = emptyArray_
176list _ [] = emptyArray_
116−list to' (x:xs) = Encoding $
177+list to' (x:xs) = openBracket >< to' x >< commas xs >< closeBracket
117− char7 '[' <> fromEncoding (to' x) <> commas xs <> char7 ']'
118 where
178 where
119− commas = foldr (\v vs -> char7 ',' <> fromEncoding (to' v) <> vs) mempty
179+ commas = foldr (\v vs -> comma >< to' v >< vs) empty
120{-# INLINE list #-}
180{-# INLINE list #-}
121
181
122-- | Encode as JSON object
182-- | Encode as JSON object
123dict
183dict
124− :: (k -> Encoding) -- ^ key encoding
184+ :: (k -> Encoding' Text) -- ^ key encoding
125 -> (v -> Encoding) -- ^ value encoding
185 -> (v -> Encoding) -- ^ value encoding
126 -> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
186 -> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
127 -> m -- ^ container
187 -> m -- ^ container
128 -> Encoding
188 -> Encoding
129−dict encodeKey encodeVal foldrWithKey = brackets '{' '}' . foldrWithKey go mempty
189+dict encodeKey encodeVal foldrWithKey = pairs . foldrWithKey go mempty
130 where
190 where
131 go k v c = Value (encodeKV k v) <> c
191 go k v c = Value (encodeKV k v) <> c
132− encodeKV k v = encodeKey k <> Encoding (char7 ':') <> encodeVal v
192+ encodeKV k v = retagEncoding (encodeKey k) >< colon >< retagEncoding (encodeVal v)
133{-# INLINE dict #-}
193{-# INLINE dict #-}
134
194
195+-- | Type tag for tuples contents, see 'tuple'.
196+data InArray
197+
135infixr 6 >*<
198infixr 6 >*<
136-- | See 'tuple'.
199-- | See 'tuple'.
137−(>*<) :: Encoding -> Encoding -> Encoding
200+(>*<) :: Encoding' a -> Encoding' b -> Encoding' InArray
138−Encoding a >*< Encoding b = Encoding $ a <> char7 ',' <> b
201+a >*< b = retagEncoding a >< comma >< retagEncoding b
139{-# INLINE (>*<) #-}
202{-# INLINE (>*<) #-}
140
203
204+empty :: Encoding' a
205+empty = Encoding mempty
206+
207+econcat :: [Encoding' a] -> Encoding' a
208+econcat = foldr (><) empty
209+
210+infixr 6 ><
211+(><) :: Encoding' a -> Encoding' a -> Encoding' a
212+Encoding a >< Encoding b = Encoding (a <> b)
213+{-# INLINE (><) #-}
214+
141-- | Encode as a tuple.
215-- | Encode as a tuple.
142--
216--
143-- @
217-- @
@@ -145,18 +219,26 @@ Encoding a >*< Encoding b = Encoding $ a <> char7 ',' <> b
145-- toEncoding a >*<
219-- toEncoding a >*<
146-- toEncoding b >*<
220-- toEncoding b >*<
147-- toEncoding c
221-- toEncoding c
148−tuple :: Encoding -> Encoding
222+tuple :: Encoding' InArray -> Encoding
149−tuple b = Encoding (char7 '[' <> fromEncoding b <> char7 ']')
223+tuple b = retagEncoding $ openBracket >< b >< closeBracket
150{-# INLINE tuple #-}
224{-# INLINE tuple #-}
151
225
152−text :: Text -> Encoding
226+text :: Text -> Encoding' a
153−text = Encoding . B.text
227+text = Encoding . EB.text
228+
229+lazyText :: LT.Text -> Encoding' a
230+lazyText t = Encoding $
231+ B.char7 '"' <>
232+ LT.foldrChunks (\x xs -> EB.unquoted x <> xs) (B.char7 '"') t
233+
234+string :: String -> Encoding' a
235+string = Encoding . EB.string
154
236
155-------------------------------------------------------------------------------
237-------------------------------------------------------------------------------
156-- chars
238-- chars
157-------------------------------------------------------------------------------
239-------------------------------------------------------------------------------
158
240
159−comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding
241+comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding' a
160comma = Encoding $ char7 ','
242comma = Encoding $ char7 ','
161colon = Encoding $ char7 ':'
243colon = Encoding $ char7 ':'
162openBracket = Encoding $ char7 '['
244openBracket = Encoding $ char7 '['
@@ -164,3 +246,126 @@ closeBracket = Encoding $ char7 ']'
164openCurly = Encoding $ char7 '{'
246openCurly = Encoding $ char7 '{'
165closeCurly = Encoding $ char7 '}'
247closeCurly = Encoding $ char7 '}'
166
248
249+-------------------------------------------------------------------------------
250+-- Decimal numbers
251+-------------------------------------------------------------------------------
252+
253+int8 :: Int8 -> Encoding
254+int8 = Encoding . B.int8Dec
255+
256+int16 :: Int16 -> Encoding
257+int16 = Encoding . B.int16Dec
258+
259+int32 :: Int32 -> Encoding
260+int32 = Encoding . B.int32Dec
261+
262+int64 :: Int64 -> Encoding
263+int64 = Encoding . B.int64Dec
264+
265+int :: Int -> Encoding
266+int = Encoding . B.intDec
267+
268+word8 :: Word8 -> Encoding
269+word8 = Encoding . B.word8Dec
270+
271+word16 :: Word16 -> Encoding
272+word16 = Encoding . B.word16Dec
273+
274+word32 :: Word32 -> Encoding
275+word32 = Encoding . B.word32Dec
276+
277+word64 :: Word64 -> Encoding
278+word64 = Encoding . B.word64Dec
279+
280+word :: Word -> Encoding
281+word = Encoding . B.wordDec
282+
283+integer :: Integer -> Encoding
284+integer = Encoding . B.integerDec
285+
286+float :: Float -> Encoding
287+float = realFloatToEncoding $ Encoding . B.floatDec
288+
289+double :: Double -> Encoding
290+double = realFloatToEncoding $ Encoding . B.doubleDec
291+
292+scientific :: Scientific -> Encoding
293+scientific = Encoding . EB.scientific
294+
295+realFloatToEncoding :: RealFloat a => (a -> Encoding) -> a -> Encoding
296+realFloatToEncoding e d
297+ | isNaN d || isInfinite d = null_
298+ | otherwise = e d
299+{-# INLINE realFloatToEncoding #-}
300+
301+-------------------------------------------------------------------------------
302+-- Decimal numbers as Text
303+-------------------------------------------------------------------------------
304+
305+int8Text :: Int8 -> Encoding' a
306+int8Text = Encoding . EB.quote . B.int8Dec
307+
308+int16Text :: Int16 -> Encoding' a
309+int16Text = Encoding . EB.quote . B.int16Dec
310+
311+int32Text :: Int32 -> Encoding' a
312+int32Text = Encoding . EB.quote . B.int32Dec
313+
314+int64Text :: Int64 -> Encoding' a
315+int64Text = Encoding . EB.quote . B.int64Dec
316+
317+intText :: Int -> Encoding' a
318+intText = Encoding . EB.quote . B.intDec
319+
320+word8Text :: Word8 -> Encoding' a
321+word8Text = Encoding . EB.quote . B.word8Dec
322+
323+word16Text :: Word16 -> Encoding' a
324+word16Text = Encoding . EB.quote . B.word16Dec
325+
326+word32Text :: Word32 -> Encoding' a
327+word32Text = Encoding . EB.quote . B.word32Dec
328+
329+word64Text :: Word64 -> Encoding' a
330+word64Text = Encoding . EB.quote . B.word64Dec
331+
332+wordText :: Word -> Encoding' a
333+wordText = Encoding . EB.quote . B.wordDec
334+
335+integerText :: Integer -> Encoding' a
336+integerText = Encoding . EB.quote . B.integerDec
337+
338+floatText :: Float -> Encoding' a
339+floatText = Encoding . EB.quote . B.floatDec
340+
341+doubleText :: Double -> Encoding' a
342+doubleText = Encoding . EB.quote . B.doubleDec
343+
344+scientificText :: Scientific -> Encoding' a
345+scientificText = Encoding . EB.quote . EB.scientific
346+
347+-------------------------------------------------------------------------------
348+-- time
349+-------------------------------------------------------------------------------
350+
351+day :: Day -> Encoding' a
352+day = Encoding . EB.quote . EB.day
353+
354+localTime :: LocalTime -> Encoding' a
355+localTime = Encoding . EB.quote . EB.localTime
356+
357+utcTime :: UTCTime -> Encoding' a
358+utcTime = Encoding . EB.quote . EB.utcTime
359+
360+timeOfDay :: TimeOfDay -> Encoding' a
361+timeOfDay = Encoding . EB.quote . EB.timeOfDay
362+
363+zonedTime :: ZonedTime -> Encoding' a
364+zonedTime = Encoding . EB.quote . EB.zonedTime
365+
366+-------------------------------------------------------------------------------
367+-- Value
368+-------------------------------------------------------------------------------
369+
370+value :: Value -> Encoding
371+value = Encoding . EB.encodeToBuilder
Data/Aeson/Internal.hs+2
@@ -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,5 +23,6 @@ module Data.Aeson.Internal
22 , iparse
23 , iparse
23 ) where
24 ) where
24
25
26+
25import Data.Aeson.Types.Internal
27import Data.Aeson.Types.Internal
26import Data.Aeson.Types.FromJSON (ifromJSON)
28import Data.Aeson.Types.FromJSON (ifromJSON)
Data/Aeson/Functions.hs → Data/Aeson/Internal/Functions.hsrenamed+6−9
@@ -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
@@ -7,13 +8,15 @@
7-- Stability: experimental
8-- Stability: experimental
8-- Portability: portable
9-- Portability: portable
9
10
10−module Data.Aeson.Functions
11+module Data.Aeson.Internal.Functions
12+ (
11− ( mapHashKeyVal
13+ mapHashKeyVal
12− , hashMapKey
13 , mapKeyVal
14 , mapKeyVal
14 , mapKey
15 , mapKey
15 ) where
16 ) where
16
17
18+import Prelude.Compat
19+
17import Data.Hashable (Hashable)
20import Data.Hashable (Hashable)
18import qualified Data.HashMap.Strict as H
21import qualified Data.HashMap.Strict as H
19import qualified Data.Map as M
22import qualified Data.Map as M
@@ -24,12 +27,6 @@ mapHashKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
24mapHashKeyVal fk kv = M.foldrWithKey (\k v -> H.insert (fk k) (kv v)) H.empty
27mapHashKeyVal fk kv = M.foldrWithKey (\k v -> H.insert (fk k) (kv v)) H.empty
25{-# INLINE mapHashKeyVal #-}
28{-# INLINE mapHashKeyVal #-}
26
29
27−-- | Transform a 'M.Map' into a 'H.HashMap' while transforming the keys.
28−hashMapKey :: (Ord k2) => (k1 -> k2)
29− -> H.HashMap k1 v -> M.Map k2 v
30−hashMapKey kv = H.foldrWithKey (M.insert . kv) M.empty
31−{-# INLINE hashMapKey #-}
32−
33-- | Transform the keys and values of a 'H.HashMap'.
30-- | Transform the keys and values of a 'H.HashMap'.
34mapKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
31mapKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
35 -> H.HashMap k1 v1 -> H.HashMap k2 v2
32 -> H.HashMap k1 v1 -> H.HashMap k2 v2
Data/Aeson/Internal/Time.hs+1−38
@@ -17,41 +17,4 @@ module Data.Aeson.Internal.Time
17 , toTimeOfDay64
17 , toTimeOfDay64
18 ) where
18 ) where
19
19
20−import Data.Fixed (Pico)
20+import Data.Attoparsec.Time.Internal
21−import Data.Int (Int64)
22−import Data.Time
23−import Unsafe.Coerce (unsafeCoerce)
24−
25−#if MIN_VERSION_base(4,7,0)
26−
27−import Data.Fixed (Fixed(MkFixed))
28−
29−toPico :: Integer -> Pico
30−toPico = MkFixed
31−
32−fromPico :: Pico -> Integer
33−fromPico (MkFixed i) = i
34−
35−#else
36−
37−toPico :: Integer -> Pico
38−toPico = unsafeCoerce
39−
40−fromPico :: Pico -> Integer
41−fromPico = unsafeCoerce
42−
43−#endif
44−
45−-- | Like TimeOfDay, but using a fixed-width integer for seconds.
46−data TimeOfDay64 = TOD {-# UNPACK #-} !Int
47− {-# UNPACK #-} !Int
48− {-# UNPACK #-} !Int64
49−
50−diffTimeOfDay64 :: DiffTime -> TimeOfDay64
51−diffTimeOfDay64 t = TOD (fromIntegral h) (fromIntegral m) s
52− where (h,mp) = fromIntegral pico `quotRem` 3600000000000000
53− (m,s) = mp `quotRem` 60000000000000
54− pico = unsafeCoerce t :: Integer
55−
56−toTimeOfDay64 :: TimeOfDay -> TimeOfDay64
57−toTimeOfDay64 (TimeOfDay h m s) = TOD h m (fromIntegral (fromPico s))
Data/Aeson/Parser.hs+14−5
@@ -1,5 +1,4 @@
1−{-# LANGUAGE BangPatterns, 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
59+
49−import Data.Aeson.Parser.Internal (decodeStrictWith, decodeWith,
60+import Data.Aeson.Parser.Internal
50− eitherDecodeStrictWith, eitherDecodeWith,
51− json, json', jstring, value, value')
52
61
53-- $lazy
62-- $lazy
54--
63--
Data/Aeson/Parser/Internal.hs+325−198
@@ -1,8 +1,14 @@
1−{-# LANGUAGE BangPatterns, CPP, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
4+{-# LANGUAGE OverloadedStrings #-}
2#if MIN_VERSION_ghc_prim(0,3,1)
5#if MIN_VERSION_ghc_prim(0,3,1)
3{-# LANGUAGE MagicHash #-}
6{-# LANGUAGE MagicHash #-}
4#endif
7#endif
5−
8+#if __GLASGOW_HASKELL__ <= 710 && __GLASGOW_HASKELL__ >= 706
9+-- Work around a compiler bug
10+{-# OPTIONS_GHC -fsimpl-tick-factor=200 #-}
11+#endif
6-- |
12-- |
7-- Module: Data.Aeson.Parser.Internal
13-- Module: Data.Aeson.Parser.Internal
8-- Copyright: (c) 2011-2016 Bryan O'Sullivan
14-- Copyright: (c) 2011-2016 Bryan O'Sullivan
@@ -19,52 +25,56 @@ module Data.Aeson.Parser.Internal
19 (
25 (
20 -- * Lazy parsers
26 -- * Lazy parsers
21 json, jsonEOF
27 json, jsonEOF
28+ , jsonWith
29+ , jsonLast
30+ , jsonAccum
31+ , jsonNoDup
22 , value
32 , value
23 , jstring
33 , jstring
34+ , jstring_
35+ , scientific
24 -- * Strict parsers
36 -- * Strict parsers
25 , json', jsonEOF'
37 , json', jsonEOF'
38+ , jsonWith'
39+ , jsonLast'
40+ , jsonAccum'
41+ , jsonNoDup'
26 , value'
42 , value'
27 -- * Helpers
43 -- * Helpers
28 , decodeWith
44 , decodeWith
29 , decodeStrictWith
45 , decodeStrictWith
30 , eitherDecodeWith
46 , eitherDecodeWith
31 , eitherDecodeStrictWith
47 , eitherDecodeStrictWith
48+ -- ** Handling objects with duplicate keys
49+ , fromListAccum
50+ , parseListNoDup
32 ) where
51 ) where
33
52
53+import Prelude.Compat
54+
55+import Control.Applicative ((<|>))
34−import Control.Monad.IO.Class (liftIO)
56+import Control.Monad (void, when)
35−import Data.Aeson.Types.Internal (IResult(..), JSONPath, Result(..), Value(..))
57+import Data.Aeson.Types.Internal (IResult(..), JSONPath, Object, Result(..), Value(..))
36−import Data.Attoparsec.ByteString.Char8 (Parser, char, endOfInput, scientific,
58+import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string)
37− skipSpace, string)
38−import Data.Bits ((.|.), shiftL)
59+import Data.Function (fix)
39−import Data.ByteString.Internal (ByteString(..))
60+import Data.Functor.Compat (($>))
40−import Data.Char (chr)
61+import Data.Scientific (Scientific)
41import Data.Text (Text)
62import Data.Text (Text)
42−import Data.Text.Encoding (decodeUtf8')
63+import Data.Vector (Vector)
43−import Data.Text.Internal.Encoding.Utf8 (ord2, ord3, ord4)
44−import Data.Text.Internal.Unsafe.Char (ord)
45−import Data.Vector as Vector (Vector, empty, fromList, reverse)
64+import qualified Data.Vector as Vector (empty, fromList, fromListN, reverse)
46−import Data.Word (Word8)
47−import Foreign.ForeignPtr (withForeignPtr)
48−import Foreign.Ptr (Ptr, plusPtr)
49−import Foreign.Ptr (minusPtr)
50−import Foreign.Storable (poke)
51−import System.IO.Unsafe (unsafePerformIO)
52import qualified Data.Attoparsec.ByteString as A
65import qualified Data.Attoparsec.ByteString as A
53import qualified Data.Attoparsec.Lazy as L
66import qualified Data.Attoparsec.Lazy as L
54−import qualified Data.Attoparsec.Zepto as Z
55import qualified Data.ByteString as B
67import qualified Data.ByteString as B
56−import qualified Data.ByteString.Internal as B
57−import qualified Data.ByteString.Lazy as L
58import qualified Data.ByteString.Unsafe as B
68import qualified Data.ByteString.Unsafe as B
69+import qualified Data.ByteString.Lazy as L
59import qualified Data.HashMap.Strict as H
70import qualified Data.HashMap.Strict as H
60−
71+import qualified Data.Scientific as Sci
61−#if !MIN_VERSION_base(4,8,0)
72+import Data.Aeson.Parser.Unescape (unescapeText)
62−import Control.Applicative ((*>), (<$>), (<*), pure)
63−#endif
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))
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
189
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 -> B.memcpy 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− B.memcpy 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+21−104
@@ -1,14 +1,4 @@
1−{-# LANGUAGE BangPatterns, CPP, ScopedTypeVariables #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2−
3−-- |
4−-- Module: Data.Aeson.Parser.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−-- Parsers for parsing dates and times.
12
2
13module Data.Aeson.Parser.Time
3module Data.Aeson.Parser.Time
14 (
4 (
@@ -21,122 +11,51 @@ module Data.Aeson.Parser.Time
21 , zonedTime
11 , zonedTime
22 ) where
12 ) where
23
13
24−import Control.Monad (when, void)
14+import Prelude.Compat
25−import Data.Aeson.Internal.Time (toPico)
15+
26−import Data.Attoparsec.Text as A
16+import Data.Attoparsec.Text (Parser)
27−import Data.Bits ((.&.))
28−import Data.Char (isDigit, ord)
29−import Data.Fixed (Pico)
30−import Data.Int (Int64)
31−import Data.Maybe (fromMaybe)
32import Data.Text (Text)
17import Data.Text (Text)
33−import Data.Time.Calendar (Day, fromGregorianValid)
18+import Data.Time.Calendar (Day)
34import Data.Time.Clock (UTCTime(..))
19import Data.Time.Clock (UTCTime(..))
35import qualified Data.Aeson.Types.Internal as Aeson
20import qualified Data.Aeson.Types.Internal as Aeson
36−import qualified Data.Text as T
21+import qualified Data.Attoparsec.Text as A
22+import qualified Data.Attoparsec.Time as T
37import qualified Data.Time.LocalTime as Local
23import qualified Data.Time.LocalTime as Local
38
24
39−#if !MIN_VERSION_base(4,8,0)
40−import Control.Applicative ((<$>), (<*>), (<*), (*>))
41−#endif
42−
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+1336−873
@@ -1,5 +1,9 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
1−{-# LANGUAGE CPP, FlexibleInstances, NamedFieldPuns,
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE NamedFieldPuns #-}
5+{-# LANGUAGE NoImplicitPrelude #-}
2− NoImplicitPrelude, UndecidableInstances #-}
6+{-# LANGUAGE UndecidableInstances #-}
3#if __GLASGOW_HASKELL__ >= 800
7#if __GLASGOW_HASKELL__ >= 800
4-- a) THQ works on cross-compilers and unregisterised GHCs
8-- a) THQ works on cross-compilers and unregisterised GHCs
5-- 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)
@@ -9,6 +13,7 @@
9{-# LANGUAGE TemplateHaskell #-}
13{-# LANGUAGE TemplateHaskell #-}
10#endif
14#endif
11
15
16+#include "incoherent-compat.h"
12#include "overlapping-compat.h"
17#include "overlapping-compat.h"
13
18
14{-|
19{-|
@@ -80,73 +85,72 @@ $('deriveJSON' 'defaultOptions' ''(,,,))
80@
85@
81
86
82-}
87-}
83−
84module Data.Aeson.TH
88module Data.Aeson.TH
89+ (
85− ( -- * Encoding configuration
90+ -- * Encoding configuration
91+ Options(..)
92+ , SumEncoding(..)
93+ , defaultOptions
86− Options(..), SumEncoding(..), defaultOptions, defaultTaggedObject
94+ , defaultTaggedObject
87
95
88 -- * FromJSON and ToJSON derivation
96 -- * FromJSON and ToJSON derivation
89 , deriveJSON
97 , deriveJSON
98+ , deriveJSON1
99+ , deriveJSON2
90
100
91 , deriveToJSON
101 , deriveToJSON
102+ , deriveToJSON1
103+ , deriveToJSON2
92 , deriveFromJSON
104 , deriveFromJSON
105+ , deriveFromJSON1
106+ , deriveFromJSON2
93
107
94 , mkToJSON
108 , mkToJSON
109+ , mkLiftToJSON
110+ , mkLiftToJSON2
95 , mkToEncoding
111 , mkToEncoding
112+ , mkLiftToEncoding
113+ , mkLiftToEncoding2
96 , mkParseJSON
114 , mkParseJSON
115+ , mkLiftParseJSON
116+ , mkLiftParseJSON2
97 ) where
117 ) where
98
118
119+import Prelude.Compat
120+
99−import Control.Applicative ( pure, (<$>), (<*>) )
121+import Control.Applicative ((<|>))
100−import Data.Aeson ( toJSON, Object, (.=), (.:), (.:?)
122+import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..))
101− , ToJSON, toEncoding, toJSON
123+import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject)
102− , FromJSON, parseJSON
103− )
104−import Data.Aeson.Types ( Value(..), Parser
105− , Options(..)
106− , SumEncoding(..)
107− , defaultOptions
108− , defaultTaggedObject
109− )
110import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
124import Data.Aeson.Types.Internal ((<?>), JSONPathElement(Key))
125+import Data.Aeson.Types.FromJSON (parseOptionalFieldWith)
126+import Data.Aeson.Types.ToJSON (fromPairs, pair)
111−import Control.Monad ( liftM2, return, mapM, fail )
127+import Control.Monad (liftM2, unless, when)
112−import Data.Bool ( Bool(False, True), otherwise, (&&), not )
113−import Data.Either ( Either(Left, Right) )
114−import Data.Eq ( (==) )
128+import Data.Foldable (foldr')
115−import Data.Function ( ($), (.), flip )
116−import Data.Functor ( fmap )
117−import Data.Int ( Int )
118−import Data.List ( (++), all, any, find, foldl, foldl'
119− , genericLength , intercalate , intersperse, length, map
120− , partition, zip
121− )
122−import Data.Map ( Map )
123−import Data.Maybe ( Maybe(Nothing, Just), catMaybes )
124−import Data.Monoid ( (<>), mconcat )
125−import Language.Haskell.TH
126−import Language.Haskell.TH.Syntax ( VarStrictType )
127−import Prelude ( String, (-), Integer, error, foldr1, fromIntegral
128− , splitAt, zipWith
129− )
130−#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
129+#if MIN_VERSION_template_haskell(2,8,0) && !MIN_VERSION_template_haskell(2,10,0)
131−import Data.Foldable ( foldr' )
130+import Data.List (nub)
132−import qualified Data.Map as M ( singleton )
133−import Data.List ( nub )
134−import Language.Haskell.TH.Syntax ( mkNameG_tc )
135−import Prelude ( concatMap, uncurry )
136#endif
131#endif
132+import Data.List (foldl', genericLength, intercalate, partition, union)
133+import Data.List.NonEmpty ((<|), NonEmpty((:|)))
134+import Data.Map (Map)
135+import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136+import qualified Data.Monoid as Monoid
137+import Data.Set (Set)
138+import Language.Haskell.TH hiding (Arity)
139+import Language.Haskell.TH.Datatype
137−#if MIN_VERSION_template_haskell(2,11,0)
140+#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
138−import Prelude ( head )
141+import Language.Haskell.TH.Syntax (mkNameG_tc)
139#endif
142#endif
140−import Text.Printf ( printf )
143+import Text.Printf (printf)
141−import Text.Show ( show )
142−import qualified Data.Aeson as A
143import qualified Data.Aeson.Encoding.Internal as E
144import qualified Data.Aeson.Encoding.Internal as E
145+import qualified Data.Foldable as F (all)
144−import qualified Data.HashMap.Strict as H ( lookup, toList )
146+import qualified Data.HashMap.Strict as H (difference, fromList, keys, lookup, toList)
147+import qualified Data.List.NonEmpty as NE (length, reverse)
145−import qualified Data.Map as M ( fromList, findWithDefault )
148+import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
149+import qualified Data.Semigroup as Semigroup (Option(..))
150+import qualified Data.Set as Set (empty, insert, member)
146−import qualified Data.Text as T ( Text, pack, unpack )
151+import qualified Data.Text as T (Text, pack, unpack)
147−import qualified Data.Vector as V ( unsafeIndex, null, length, create, fromList )
152+import qualified Data.Vector as V (unsafeIndex, null, length, create, empty)
148−import qualified Data.Vector.Mutable as VM ( unsafeNew, unsafeWrite )
153+import qualified Data.Vector.Mutable as VM (unsafeNew, unsafeWrite)
149−
150
154
151--------------------------------------------------------------------------------
155--------------------------------------------------------------------------------
152-- Convenience
156-- Convenience
@@ -163,11 +167,33 @@ deriveJSON :: Options
163 -- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
167 -- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
164 -- instances.
168 -- instances.
165 -> Q [Dec]
169 -> Q [Dec]
166−deriveJSON opts name =
170+deriveJSON = deriveJSONBoth deriveToJSON deriveFromJSON
167− liftM2 (++)
168− (deriveToJSON opts name)
169− (deriveFromJSON opts name)
170
171
172+-- | Generates both 'ToJSON1' and 'FromJSON1' instance declarations for the given
173+-- data type or data family instance constructor.
174+--
175+-- This is a convienience function which is equivalent to calling both
176+-- 'deriveToJSON1' and 'deriveFromJSON1'.
177+deriveJSON1 :: Options
178+ -- ^ Encoding options.
179+ -> Name
180+ -- ^ Name of the type for which to generate 'ToJSON1' and 'FromJSON1'
181+ -- instances.
182+ -> Q [Dec]
183+deriveJSON1 = deriveJSONBoth deriveToJSON1 deriveFromJSON1
184+
185+-- | Generates both 'ToJSON2' and 'FromJSON2' instance declarations for the given
186+-- data type or data family instance constructor.
187+--
188+-- This is a convienience function which is equivalent to calling both
189+-- 'deriveToJSON2' and 'deriveFromJSON2'.
190+deriveJSON2 :: Options
191+ -- ^ Encoding options.
192+ -> Name
193+ -- ^ Name of the type for which to generate 'ToJSON2' and 'FromJSON2'
194+ -- instances.
195+ -> Q [Dec]
196+deriveJSON2 = deriveJSONBoth deriveToJSON2 deriveFromJSON2
171
197
172--------------------------------------------------------------------------------
198--------------------------------------------------------------------------------
173-- ToJSON
199-- ToJSON
@@ -190,414 +216,361 @@ deriveToJSON :: Options
190 -- ^ Name of the type for which to generate a 'ToJSON' instance
216 -- ^ Name of the type for which to generate a 'ToJSON' instance
191 -- declaration.
217 -- declaration.
192 -> Q [Dec]
218 -> Q [Dec]
193−deriveToJSON opts name =
219+deriveToJSON = deriveToJSONCommon toJSONClass
194− withType name $ \name' tvbs cons mbTys -> fmap (:[]) $ fromCons name' tvbs cons mbTys
220+
195− where
221+-- | Generates a 'ToJSON1' instance declaration for the given data type or
196− fromCons :: Name -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q Dec
222+-- data family instance constructor.
197− fromCons name' tvbs cons mbTys = do
223+deriveToJSON1 :: Options
198− (instanceCxt, instanceType) <- buildTypeInstance name' ''ToJSON tvbs mbTys
224+ -- ^ Encoding options.
199− instanceD (return instanceCxt)
225+ -> Name
200− (return instanceType)
226+ -- ^ Name of the type for which to generate a 'ToJSON1' instance
201− [ funD 'toJSON
227+ -- declaration.
202− [ clause []
228+ -> Q [Dec]
203− (normalB $ consToValue opts cons)
229+deriveToJSON1 = deriveToJSONCommon toJSON1Class
204− []
230+
205− ]
231+-- | Generates a 'ToJSON2' instance declaration for the given data type or
206− , funD 'toEncoding
232+-- data family instance constructor.
207− [ clause []
233+deriveToJSON2 :: Options
208− (normalB $ consToEncoding opts cons)
234+ -- ^ Encoding options.
209− []
235+ -> Name
210− ]
236+ -- ^ Name of the type for which to generate a 'ToJSON2' instance
237+ -- declaration.
238+ -> Q [Dec]
239+deriveToJSON2 = deriveToJSONCommon toJSON2Class
240+
241+deriveToJSONCommon :: JSONClass
242+ -- ^ The ToJSON variant being derived.
243+ -> Options
244+ -- ^ Encoding options.
245+ -> Name
246+ -- ^ Name of the type for which to generate an instance.
247+ -> Q [Dec]
248+deriveToJSONCommon = deriveJSONClass [ (ToJSON, \jc _ -> consToValue Value jc)
249+ , (ToEncoding, \jc _ -> consToValue Encoding jc)
211− ]
250+ ]
212
251
213-- | Generates a lambda expression which encodes the given data type or
252-- | Generates a lambda expression which encodes the given data type or
214-- data family instance constructor as a 'Value'.
253-- data family instance constructor as a 'Value'.
215mkToJSON :: Options -- ^ Encoding options.
254mkToJSON :: Options -- ^ Encoding options.
216 -> Name -- ^ Name of the type to encode.
255 -> Name -- ^ Name of the type to encode.
217 -> Q Exp
256 -> Q Exp
218−mkToJSON opts name = withType name (\_ _ cons _ -> consToValue opts cons)
257+mkToJSON = mkToJSONCommon toJSONClass
258+
259+-- | Generates a lambda expression which encodes the given data type or
260+-- data family instance constructor as a 'Value' by using the given encoding
261+-- function on occurrences of the last type parameter.
262+mkLiftToJSON :: Options -- ^ Encoding options.
263+ -> Name -- ^ Name of the type to encode.
264+ -> Q Exp
265+mkLiftToJSON = mkToJSONCommon toJSON1Class
266+
267+-- | Generates a lambda expression which encodes the given data type or
268+-- data family instance constructor as a 'Value' by using the given encoding
269+-- functions on occurrences of the last two type parameters.
270+mkLiftToJSON2 :: Options -- ^ Encoding options.
271+ -> Name -- ^ Name of the type to encode.
272+ -> Q Exp
273+mkLiftToJSON2 = mkToJSONCommon toJSON2Class
274+
275+mkToJSONCommon :: JSONClass -- ^ Which class's method is being derived.
276+ -> Options -- ^ Encoding options.
277+ -> Name -- ^ Name of the encoded type.
278+ -> Q Exp
279+mkToJSONCommon = mkFunCommon (\jc _ -> consToValue Value jc)
219
280
220-- | Generates a lambda expression which encodes the given data type or
281-- | Generates a lambda expression which encodes the given data type or
221-- data family instance constructor as a JSON string.
282-- data family instance constructor as a JSON string.
222mkToEncoding :: Options -- ^ Encoding options.
283mkToEncoding :: Options -- ^ Encoding options.
223 -> Name -- ^ Name of the type to encode.
284 -> Name -- ^ Name of the type to encode.
224 -> Q Exp
285 -> Q Exp
225−mkToEncoding opts name = withType name (\_ _ cons _ -> consToEncoding opts cons)
286+mkToEncoding = mkToEncodingCommon toJSONClass
226
287
227−-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
288+-- | Generates a lambda expression which encodes the given data type or
228−-- code to generate a 'Value' of a number of constructors. All constructors
289+-- data family instance constructor as a JSON string by using the given encoding
229−-- must be from the same type.
290+-- function on occurrences of the last type parameter.
230−consToValue :: Options
231− -- ^ Encoding options.
291+mkLiftToEncoding :: Options -- ^ Encoding options.
232− -> [Con]
292+ -> Name -- ^ Name of the type to encode.
233− -- ^ Constructors for which to generate JSON generating code.
234− -> Q Exp
293+ -> Q Exp
294+mkLiftToEncoding = mkToEncodingCommon toJSON1Class
235
295
236−consToValue _ [] = error $ "Data.Aeson.TH.consToValue: "
296+-- | Generates a lambda expression which encodes the given data type or
237− ++ "Not a single constructor given!"
297+-- data family instance constructor as a JSON string by using the given encoding
298+-- functions on occurrences of the last two type parameters.
299+mkLiftToEncoding2 :: Options -- ^ Encoding options.
300+ -> Name -- ^ Name of the type to encode.
301+ -> Q Exp
302+mkLiftToEncoding2 = mkToEncodingCommon toJSON2Class
303+
304+mkToEncodingCommon :: JSONClass -- ^ Which class's method is being derived.
305+ -> Options -- ^ Encoding options.
306+ -> Name -- ^ Name of the encoded type.
307+ -> Q Exp
308+mkToEncodingCommon = mkFunCommon (\jc _ -> consToValue Encoding jc)
238
309
239−-- A single constructor is directly encoded. The constructor itself may be
310+-- | Helper function used by both 'deriveToJSON' and 'mkToJSON'. Generates
240−-- forgotten.
311+-- code to generate a 'Value' or 'Encoding' of a number of constructors. All
241−consToValue opts [con] = do
312+-- constructors must be from the same type.
242− value <- newName "value"
313+consToValue :: ToJSONFun
243− lam1E (varP value) $ caseE (varE value) [argsToValue opts False con]
314+ -- ^ The method ('toJSON' or 'toEncoding') being derived.
315+ -> JSONClass
316+ -- ^ The ToJSON variant being derived.
317+ -> Options
318+ -- ^ Encoding options.
319+ -> [Type]
320+ -- ^ The types from the data type/data family instance declaration
321+ -> [ConstructorInfo]
322+ -- ^ Constructors for which to generate JSON generating code.
323+ -> Q Exp
324+
325+consToValue _ _ _ _ [] = error $ "Data.Aeson.TH.consToValue: "
326+ ++ "Not a single constructor given!"
244
327
245−consToValue opts cons = do
328+consToValue target jc opts instTys cons = do
246 value <- newName "value"
329 value <- newName "value"
330+ tjs <- newNameList "_tj" $ arityInt jc
331+ tjls <- newNameList "_tjl" $ arityInt jc
332+ let zippedTJs = zip tjs tjls
333+ interleavedTJs = interleave tjs tjls
334+ lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
335+ tvMap = M.fromList $ zip lastTyVars zippedTJs
336+ lamE (map varP $ interleavedTJs ++ [value]) $
247− lam1E (varP value) $ caseE (varE value) matches
337+ caseE (varE value) (matches tvMap)
248 where
338 where
249− matches
339+ matches tvMap = case cons of
340+ -- A single constructor is directly encoded. The constructor itself may be
341+ -- forgotten.
342+ [con] | not (tagSingleConstructors opts) -> [argsToValue target jc tvMap opts False con]
250− | allNullaryToStringTag opts && all isNullary cons =
343+ _ | allNullaryToStringTag opts && all isNullary cons ->
251− [ match (conP conName []) (normalB $ conStr opts conName) []
344+ [ match (conP conName []) (normalB $ conStr target opts conName) []
252 | con <- cons
345 | con <- cons
253− , let conName = getConName con
346+ , let conName = constructorName con
254 ]
347 ]
255− | otherwise = [argsToValue opts True con | con <- cons]
348+ | otherwise -> [argsToValue target jc tvMap opts True con | con <- cons]
256
349
350+-- | Name of the constructor as a quoted 'Value' or 'Encoding'.
257−conStr :: Options -> Name -> Q Exp
351+conStr :: ToJSONFun -> Options -> Name -> Q Exp
258−conStr opts = appE [|String|] . conTxt opts
352+conStr Value opts = appE [|String|] . conTxt opts
353+conStr Encoding opts = appE [|E.text|] . conTxt opts
259
354
355+-- | Name of the constructor as a quoted 'Text'.
260conTxt :: Options -> Name -> Q Exp
356conTxt :: Options -> Name -> Q Exp
261−conTxt opts = appE [|T.pack|] . conStringE opts
357+conTxt opts = appE [|T.pack|] . stringE . conString opts
262−
263−conStringE :: Options -> Name -> Q Exp
264−conStringE opts = stringE . constructorTagModifier opts . nameBase
265−
266−-- | Helper function used by both 'deriveToJSON' and 'mkToEncoding'. Generates
267−-- code to write out a value for a number of constructors. All constructors
268−-- must be from the same type.
269−consToEncoding :: Options
270− -- ^ Encoding options.
271− -> [Con]
272− -- ^ Constructors for which to generate JSON generating code.
273− -> Q Exp
274−
275−consToEncoding _ [] = error $ "Data.Aeson.TH.consToEncoding: "
276− ++ "Not a single constructor given!"
277−
278−-- A single constructor is directly encoded. The constructor itself may be
279−-- forgotten.
280−consToEncoding opts [con] = do
281− value <- newName "value"
282− lam1E (varP value) $ caseE (varE value) [argsToEncoding opts False con]
283
358
284−-- Encode just the name of the constructor of a sum type iff all the
359+-- | Name of the constructor.
285−-- constructors are nullary.
286−consToEncoding opts cons = do
287− value <- newName "value"
288− lam1E (varP value) $ caseE (varE value) matches
289− where
290− matches
291− | allNullaryToStringTag opts && all isNullary cons =
292− [ match (conP conName [])
293− (normalB $ encStr opts conName) []
294− | con <- cons
295− , let conName = getConName con
296− ]
297− | otherwise = [argsToEncoding opts True con | con <- cons]
298−
299−encStr :: Options -> Name -> Q Exp
360+conString :: Options -> Name -> String
300−encStr opts = appE [|E.text|] . conTxt opts
361+conString opts = constructorTagModifier opts . nameBase
301
362
302-- | If constructor is nullary.
363-- | If constructor is nullary.
303−isNullary :: Con -> Bool
364+isNullary :: ConstructorInfo -> Bool
304−isNullary (NormalC _ []) = True
365+isNullary ConstructorInfo { constructorVariant = NormalConstructor
366+ , constructorFields = tys } = null tys
305isNullary _ = False
367isNullary _ = False
306
368
307−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 =
308−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
309 | multiCons =
410 | multiCons =
310 case sumEncoding opts of
411 case sumEncoding opts of
311 TwoElemArray ->
412 TwoElemArray ->
312− [|Array|] `appE` ([|V.fromList|] `appE` listE [conStr opts conName, exp])
413+ array target [conStr target opts conName, value]
313 TaggedObject{tagFieldName, contentsFieldName} ->
414 TaggedObject{tagFieldName, contentsFieldName} ->
314− [|A.object|] `appE` listE
415+ -- TODO: Maybe throw an error in case
315− [ infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
416+ -- tagFieldName overwrites a field in pairs.
417+ let tag = pairE tagFieldName (conStr target opts conName)
316− , infixApp [|T.pack contentsFieldName|] [|(.=)|] exp
418+ content = pairs contentsFieldName
317− ]
419+ in fromPairsE $
420+ if nullary then tag else infixApp tag [|(Monoid.<>)|] content
318 ObjectWithSingleField ->
421 ObjectWithSingleField ->
319− [|A.object|] `appE` listE
422+ objectE [(conString opts conName, value)]
320− [ infixApp (conTxt opts conName) [|(.=)|] exp
423+ UntaggedValue | nullary -> conStr target opts conName
321− ]
424+ UntaggedValue -> value
322−
323− | otherwise = exp
425+ | otherwise = value
324−
325−nullarySumToValue :: Options -> Bool -> Name -> Q Exp
326−nullarySumToValue opts multiCons conName =
327− case sumEncoding opts of
328− TaggedObject{tagFieldName} ->
329− [|A.object|] `appE` listE
330− [ infixApp [|T.pack tagFieldName|] [|(.=)|] (conStr opts conName)
331− ]
332− _ -> sumToValue opts multiCons conName [e|toJSON ([] :: [()])|]
333
426
334-- | Generates code to generate the JSON encoding of a single constructor.
427-- | Generates code to generate the JSON encoding of a single constructor.
335−argsToValue :: Options -> Bool -> Con -> Q Match
428+argsToValue :: ToJSONFun -> JSONClass -> TyVarMap -> Options -> Bool -> ConstructorInfo -> Q Match
336−-- Nullary constructors. Generates code that explicitly matches against the
337−-- constructor even though it doesn't contain data. This is useful to prevent
338−-- type errors.
339−argsToValue opts multiCons (NormalC conName []) =
340− match (conP conName [])
341− (normalB (nullarySumToValue opts multiCons conName))
342− []
343
429
344-- Polyadic constructors with special case for unary constructors.
430-- Polyadic constructors with special case for unary constructors.
345−argsToValue opts multiCons (NormalC conName ts) = do
431+argsToValue target jc tvMap opts multiCons
432+ ConstructorInfo { constructorName = conName
433+ , constructorVariant = NormalConstructor
434+ , constructorFields = argTys } = do
435+ argTys' <- mapM resolveTypeSynonyms argTys
346− let len = length ts
436+ let len = length argTys'
347− args <- mapM newName ["arg" ++ show n | n <- [1..len]]
437+ args <- newNameList "arg" len
438+ let js = case [ dispatchToJSON target jc conName tvMap argTy
348− js <- case [[|toJSON|] `appE` varE arg | arg <- args] of
439+ `appE` varE arg
440+ | (arg, argTy) <- zip args argTys'
441+ ] of
349− -- Single argument is directly converted.
442+ -- Single argument is directly converted.
350− [e] -> return e
443+ [e] -> e
351− -- Multiple arguments are converted to a JSON array.
444+ -- Zero and multiple arguments are converted to a JSON array.
352− es -> do
445+ es -> array target es
353− mv <- newName "mv"
446+
354− let newMV = bindS (varP mv)
355− ([|VM.unsafeNew|] `appE`
356− litE (integerL $ fromIntegral len))
357− stmts = [ noBindS $
358− [|VM.unsafeWrite|] `appE`
359− (varE mv) `appE`
360− litE (integerL ix) `appE`
361− e
362− | (ix, e) <- zip [(0::Integer)..] es
363− ]
364− ret = noBindS $ [|return|] `appE` varE mv
365− return $ [|Array|] `appE`
366− (varE 'V.create `appE`
367− doE (newMV:stmts++[ret]))
368 match (conP conName $ map varP args)
447 match (conP conName $ map varP args)
369− (normalB $ sumToValue opts multiCons conName js)
448+ (normalB $ opaqueSumToValue target opts multiCons (null argTys') conName js)
370 []
449 []
371
450
372-- Records.
451-- Records.
452+argsToValue target jc tvMap opts multiCons
453+ info@ConstructorInfo { constructorName = conName
454+ , constructorVariant = RecordConstructor fields
455+ , constructorFields = argTys } =
373−argsToValue opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts, not multiCons, ts) of
456+ case (unwrapUnaryRecords opts, not multiCons, argTys) of
374− (True,True,[(_,st,ty)]) -> argsToValue opts multiCons (NormalC conName [(st,ty)])
457+ (True,True,[_]) -> argsToValue target jc tvMap opts multiCons
458+ (info{constructorVariant = NormalConstructor})
375− _ -> do
459+ _ -> do
376− args <- mapM newName ["arg" ++ show n | (_, n) <- zip ts [1 :: Integer ..]]
460+ argTys' <- mapM resolveTypeSynonyms argTys
377− let exp = [|A.object|] `appE` pairs
461+ args <- newNameList "arg" $ length argTys'
378−
379− pairs | omitNothingFields opts = infixApp maybeFields
462+ let pairs | omitNothingFields opts = infixApp maybeFields
380− [|(++)|]
463+ [|(Monoid.<>)|]
381− restFields
464+ restFields
382− | otherwise = listE $ map toPair argCons
465+ | otherwise = mconcatE (map pureToPair argCons)
383−
466+
384− argCons = zip args ts
467+ argCons = zip3 (map varE args) argTys' fields
385−
468+
386− maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
469+ maybeFields = mconcatE (map maybeToPair maybes)
387−
470+
388− restFields = listE $ map toPair rest
471+ restFields = mconcatE (map pureToPair rest)
389−
472+
390− (maybes, rest) = partition isMaybe argCons
473+ (maybes0, rest0) = partition isMaybe argCons
391−
474+ (options, rest) = partition isOption rest0
392− maybeToPair (arg, (field, _, _)) =
475+ maybes = maybes0 ++ map optionToMaybe options
393− infixApp (infixE (Just $ toFieldName field)
476+
394− [|(.=)|]
477+ maybeToPair = toPairLifted True
395− Nothing)
478+ pureToPair = toPairLifted False
396− [|(<$>)|]
479+
397− (varE arg)
480+ toPairLifted lifted (arg, argTy, field) =
398−
481+ let toValue = dispatchToJSON target jc conName tvMap argTy
399− toPair (arg, (field, _, _)) =
482+ fieldName = fieldLabel opts field
400− infixApp (toFieldName field)
483+ e arg' = pairE fieldName (toValue `appE` arg')
401− [|(.=)|]
484+ in if lifted
402− (varE arg)
485+ then do
403−
486+ x <- newName "x"
404− toFieldName field = [|T.pack|] `appE` fieldLabelExp opts field
487+ [|maybe mempty|] `appE` lam1E (varP x) (e (varE x)) `appE` arg
405−
488+ else e arg
489+
406− match (conP conName $ map varP args)
490+ match (conP conName $ map varP args)
407− ( normalB
491+ (normalB $ recordSumToValue target opts multiCons (null argTys) conName pairs)
408− $ if multiCons
409− then case sumEncoding opts of
410− TwoElemArray -> [|toJSON|] `appE` tupE [conStr opts conName, exp]
411− TaggedObject{tagFieldName} ->
412− [|A.object|] `appE`
413− -- TODO: Maybe throw an error in case
414− -- tagFieldName overwrites a field in pairs.
415− infixApp (infixApp [|T.pack tagFieldName|]
416− [|(.=)|]
417− (conStr opts conName))
418− [|(:)|]
419− pairs
420− ObjectWithSingleField ->
421− [|A.object|] `appE` listE
422− [ infixApp (conTxt opts conName) [|(.=)|] exp ]
423− else exp
424− ) []
492+ []
425
493
426-- Infix constructors.
494-- Infix constructors.
427−argsToValue opts multiCons (InfixC _ conName _) = do
495+argsToValue target jc tvMap opts multiCons
496+ ConstructorInfo { constructorName = conName
497+ , constructorVariant = InfixConstructor
498+ , constructorFields = argTys } = do
499+ [alTy, arTy] <- mapM resolveTypeSynonyms argTys
428 al <- newName "argL"
500 al <- newName "argL"
429 ar <- newName "argR"
501 ar <- newName "argR"
430 match (infixP (varP al) conName (varP ar))
502 match (infixP (varP al) conName (varP ar))
431 ( normalB
503 ( normalB
432− $ sumToValue opts multiCons conName
504+ $ opaqueSumToValue target opts multiCons False conName
505+ $ array target
506+ [ dispatchToJSON target jc conName tvMap aTy
433− $ [|toJSON|] `appE` listE [ [|toJSON|] `appE` varE a
507+ `appE` varE a
434− | a <- [al,ar]
508+ | (a, aTy) <- [(al,alTy), (ar,arTy)]
435− ]
509+ ]
436 )
510 )
437 []
511 []
438−-- Existentially quantified constructors.
439−argsToValue opts multiCons (ForallC _ _ con) =
440− argsToValue opts multiCons con
441
512
442−#if MIN_VERSION_template_haskell(2,11,0)
513+isMaybe :: (a, Type, b) -> Bool
443−-- GADTs.
514+isMaybe (_, AppT (ConT t) _, _) = t == ''Maybe
444−argsToValue opts multiCons (GadtC conNames ts _) =
515+isMaybe _ = False
445− argsToValue opts multiCons $ NormalC (head conNames) ts
446
516
447−argsToValue opts multiCons (RecGadtC conNames ts _) =
517+isOption :: (a, Type, b) -> Bool
448− argsToValue opts multiCons $ RecC (head conNames) ts
518+isOption (_, AppT (ConT t) _, _) = t == ''Semigroup.Option
449−#endif
519+isOption _ = False
450
520
451−isMaybe :: (a, (b, c, Type)) -> Bool
521+optionToMaybe :: (ExpQ, b, c) -> (ExpQ, b, c)
452−isMaybe (_, (_, _, AppT (ConT t) _)) = t == ''Maybe
522+optionToMaybe (a, b, c) = ([|Semigroup.getOption|] `appE` a, b, c)
453−isMaybe _ = False
454
523
455(<^>) :: ExpQ -> ExpQ -> ExpQ
524(<^>) :: ExpQ -> ExpQ -> ExpQ
456−(<^>) a b = infixApp a [|(<>)|] b
525+(<^>) a b = infixApp a [|(E.><)|] b
457infixr 6 <^>
526infixr 6 <^>
458
527
459−(<:>) :: ExpQ -> ExpQ -> ExpQ
460−(<:>) a b = a <^> [|E.colon|] <^> b
461−infixr 5 <:>
462−
463(<%>) :: ExpQ -> ExpQ -> ExpQ
528(<%>) :: ExpQ -> ExpQ -> ExpQ
464(<%>) a b = a <^> [|E.comma|] <^> b
529(<%>) a b = a <^> [|E.comma|] <^> b
465infixr 4 <%>
530infixr 4 <%>
466
531
467−array :: ExpQ -> ExpQ
532+-- | Wrap a list of quoted 'Value's in a quoted 'Array' (of type 'Value').
468−array exp = [|E.wrapArray|] `appE` exp
533+array :: ToJSONFun -> [ExpQ] -> ExpQ
469−
534+array Encoding [] = [|E.emptyArray_|]
470−object :: ExpQ -> ExpQ
535+array Value [] = [|Array V.empty|]
471−object exp = [|E.wrapObject|] `appE` exp
536+array Encoding es = [|E.wrapArray|] `appE` foldr1 (<%>) es
472−
537+array Value es = do
473−sumToEncoding :: Options -> Bool -> Name -> Q Exp -> Q Exp
538+ mv <- newName "mv"
474−sumToEncoding opts multiCons conName exp
539+ let newMV = bindS (varP mv)
475− | multiCons =
540+ ([|VM.unsafeNew|] `appE`
476− let fexp = exp in
541+ litE (integerL $ fromIntegral (length es)))
477− case sumEncoding opts of
542+ stmts = [ noBindS $
478− TwoElemArray ->
543+ [|VM.unsafeWrite|] `appE`
479− array (encStr opts conName <%> fexp)
544+ varE mv `appE`
480− TaggedObject{tagFieldName, contentsFieldName} ->
545+ litE (integerL ix) `appE`
481− object $
546+ e
482− ([|E.text (T.pack tagFieldName)|] <:> encStr opts conName) <%>
547+ | (ix, e) <- zip [(0::Integer)..] es
483− ([|E.text (T.pack contentsFieldName)|] <:> fexp)
548+ ]
484− ObjectWithSingleField ->
549+ ret = noBindS $ [|return|] `appE` varE mv
485− object (encStr opts conName <:> fexp)
550+ [|Array|] `appE`
486−
551+ (varE 'V.create `appE`
487− | otherwise = exp
552+ doE (newMV:stmts++[ret]))
488−
489−nullarySumToEncoding :: Options -> Bool -> Name -> Q Exp
490−nullarySumToEncoding opts multiCons conName =
491− case sumEncoding opts of
492− TaggedObject{tagFieldName} ->
493− object $
494− ([|E.text (T.pack tagFieldName)|] <:> encStr opts conName)
495− _ -> sumToEncoding opts multiCons conName [e|toEncoding ([] :: [()])|]
496−
497−-- | Generates code to generate the JSON encoding of a single constructor.
498−argsToEncoding :: Options -> Bool -> Con -> Q Match
499−-- Nullary constructors. Generates code that explicitly matches against the
500−-- constructor even though it doesn't contain data. This is useful to prevent
501−-- type errors.
502−argsToEncoding opts multiCons (NormalC conName []) =
503− match (conP conName [])
504− (normalB (nullarySumToEncoding opts multiCons conName))
505− []
506−
507−-- Polyadic constructors with special case for unary constructors.
508−argsToEncoding opts multiCons (NormalC conName ts) = do
509− let len = length ts
510− args <- mapM newName ["arg" ++ show n | n <- [1..len]]
511− js <- case args of
512− -- Single argument is directly converted.
513− [e] -> return ([|toEncoding|] `appE` varE e)
514− -- Multiple arguments are converted to a JSON array.
515− es ->
516− return (array (foldr1 (<%>) [[|toEncoding|] `appE` varE x | x <- es]))
517− match (conP conName $ map varP args)
518− (normalB $ sumToEncoding opts multiCons conName js)
519− []
520−
521−-- Records.
522−argsToEncoding opts multiCons (RecC conName ts) = case (unwrapUnaryRecords opts, not multiCons, ts) of
523− (True,True,[(_,st,ty)]) -> argsToEncoding opts multiCons (NormalC conName [(st,ty)])
524− _ -> do
525− args <- mapM newName ["arg" ++ show n | (_, n) <- zip ts [1 :: Integer ..]]
526−
527− let exp = object objBody
528−
529− objBody = [|mconcat|] `appE`
530− ([|intersperse E.comma|] `appE` pairs)
531− pairs | omitNothingFields opts = infixApp maybeFields
532− [|(<>)|]
533− restFields
534− | otherwise = listE (map toPair argCons)
535−
536− argCons = zip args ts
537−
538− maybeFields = [|catMaybes|] `appE` listE (map maybeToPair maybes)
539−
540− restFields = listE (map toPair rest)
541−
542− (maybes, rest) = partition isMaybe argCons
543−
544− maybeToPair (arg, (field, _, _)) =
545− infixApp
546− (infixApp
547− (infixE
548− (Just $ toFieldName field <^> [|E.colon|])
549− [|(<>)|]
550− Nothing)
551− [|(.)|]
552− [|toEncoding|])
553− [|(<$>)|]
554− (varE arg)
555−
556− toPair (arg, (field, _, _)) =
557− toFieldName field <:> [|toEncoding|] `appE` varE arg
558−
559− toFieldName field = [|E.text|] `appE`
560− ([|T.pack|] `appE` fieldLabelExp opts field)
561
553
562− match (conP conName $ map varP args)
554+-- | Wrap an associative list of keys and quoted values in a quoted 'Object'.
563− ( normalB
555+objectE :: [(String, ExpQ)] -> ExpQ
564− $ if multiCons
556+objectE = fromPairsE . mconcatE . fmap (uncurry pairE)
565− then case sumEncoding opts of
566− TwoElemArray -> array $
567− encStr opts conName <%> exp
568− TaggedObject{tagFieldName} -> object $
569− ([|E.text (T.pack tagFieldName)|] <:>
570− encStr opts conName) <%>
571− objBody
572− ObjectWithSingleField -> object $
573− encStr opts conName <:> exp
574− else exp
575− ) []
576
557
577−-- Infix constructors.
558+-- | 'mconcat' a list of fixed length.
578−argsToEncoding opts multiCons (InfixC _ conName _) = do
559+--
579− al <- newName "argL"
560+-- > mconcatE [ [|x|], [|y|], [|z|] ] = [| x <> (y <> z) |]
580− ar <- newName "argR"
561+mconcatE :: [ExpQ] -> ExpQ
581− match (infixP (varP al) conName (varP ar))
562+mconcatE [] = [|Monoid.mempty|]
582− ( normalB
563+mconcatE [x] = x
583− $ sumToEncoding opts multiCons conName
564+mconcatE (x : xs) = infixApp x [|(Monoid.<>)|] (mconcatE xs)
584− $ [|toEncoding|] `appE` listE [ [|toJSON|] `appE` varE a
585− | a <- [al,ar]
586− ]
587− )
588− []
589−-- Existentially quantified constructors.
590−argsToEncoding opts multiCons (ForallC _ _ con) =
591− argsToEncoding opts multiCons con
592
565
593−#if MIN_VERSION_template_haskell(2,11,0)
566+fromPairsE :: ExpQ -> ExpQ
594−-- GADTs.
567+fromPairsE = ([|fromPairs|] `appE`)
595−argsToEncoding opts multiCons (GadtC conNames ts _) =
596− argsToEncoding opts multiCons $ NormalC (head conNames) ts
597
568
598−argsToEncoding opts multiCons (RecGadtC conNames ts _) =
569+-- | Create (an encoding of) a key-value pair.
599− argsToEncoding opts multiCons $ RecC (head conNames) ts
570+--
600−#endif
571+-- > pairE "k" [|v|] = [|pair "k" v|]
572+pairE :: String -> ExpQ -> ExpQ
573+pairE k v = [|pair k|] `appE` v
601
574
602--------------------------------------------------------------------------------
575--------------------------------------------------------------------------------
603-- FromJSON
576-- FromJSON
@@ -611,55 +584,112 @@ deriveFromJSON :: Options
611 -- ^ Name of the type for which to generate a 'FromJSON' instance
584 -- ^ Name of the type for which to generate a 'FromJSON' instance
612 -- declaration.
585 -- declaration.
613 -> Q [Dec]
586 -> Q [Dec]
614−deriveFromJSON opts name =
587+deriveFromJSON = deriveFromJSONCommon fromJSONClass
615− withType name $ \name' tvbs cons mbTys -> fmap (:[]) $ fromCons name' tvbs cons mbTys
588+
616− where
589+-- | Generates a 'FromJSON1' instance declaration for the given data type or
617− fromCons :: Name -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q Dec
590+-- data family instance constructor.
618− fromCons name' tvbs cons mbTys = do
591+deriveFromJSON1 :: Options
619− (instanceCxt, instanceType) <- buildTypeInstance name' ''FromJSON tvbs mbTys
592+ -- ^ Encoding options.
620− instanceD (return instanceCxt)
593+ -> Name
621− (return instanceType)
594+ -- ^ Name of the type for which to generate a 'FromJSON1' instance
622− [ funD 'parseJSON
595+ -- declaration.
623− [ clause []
596+ -> Q [Dec]
624− (normalB $ consFromJSON name' opts cons)
597+deriveFromJSON1 = deriveFromJSONCommon fromJSON1Class
625− []
598+
626− ]
599+-- | Generates a 'FromJSON2' instance declaration for the given data type or
627− ]
600+-- data family instance constructor.
601+deriveFromJSON2 :: Options
602+ -- ^ Encoding options.
603+ -> Name
604+ -- ^ Name of the type for which to generate a 'FromJSON3' instance
605+ -- declaration.
606+ -> Q [Dec]
607+deriveFromJSON2 = deriveFromJSONCommon fromJSON2Class
608+
609+deriveFromJSONCommon :: JSONClass
610+ -- ^ The FromJSON variant being derived.
611+ -> Options
612+ -- ^ Encoding options.
613+ -> Name
614+ -- ^ Name of the type for which to generate an instance.
615+ -- declaration.
616+ -> Q [Dec]
617+deriveFromJSONCommon = deriveJSONClass [(ParseJSON, consFromJSON)]
628
618
629-- | Generates a lambda expression which parses the JSON encoding of the given
619-- | Generates a lambda expression which parses the JSON encoding of the given
630-- data type or data family instance constructor.
620-- data type or data family instance constructor.
631mkParseJSON :: Options -- ^ Encoding options.
621mkParseJSON :: Options -- ^ Encoding options.
632 -> Name -- ^ Name of the encoded type.
622 -> Name -- ^ Name of the encoded type.
633 -> Q Exp
623 -> Q Exp
634−mkParseJSON opts name =
624+mkParseJSON = mkParseJSONCommon fromJSONClass
635− withType name (\name' _ cons _ -> consFromJSON name' opts cons)
625+
626+-- | Generates a lambda expression which parses the JSON encoding of the given
627+-- data type or data family instance constructor by using the given parsing
628+-- function on occurrences of the last type parameter.
629+mkLiftParseJSON :: Options -- ^ Encoding options.
630+ -> Name -- ^ Name of the encoded type.
631+ -> Q Exp
632+mkLiftParseJSON = mkParseJSONCommon fromJSON1Class
633+
634+-- | Generates a lambda expression which parses the JSON encoding of the given
635+-- data type or data family instance constructor by using the given parsing
636+-- functions on occurrences of the last two type parameters.
637+mkLiftParseJSON2 :: Options -- ^ Encoding options.
638+ -> Name -- ^ Name of the encoded type.
639+ -> Q Exp
640+mkLiftParseJSON2 = mkParseJSONCommon fromJSON2Class
641+
642+mkParseJSONCommon :: JSONClass -- ^ Which class's method is being derived.
643+ -> Options -- ^ Encoding options.
644+ -> Name -- ^ Name of the encoded type.
645+ -> Q Exp
646+mkParseJSONCommon = mkFunCommon consFromJSON
636
647
637-- | Helper function used by both 'deriveFromJSON' and 'mkParseJSON'. Generates
648-- | Helper function used by both 'deriveFromJSON' and 'mkParseJSON'. Generates
638-- code to parse the JSON encoding of a number of constructors. All constructors
649-- code to parse the JSON encoding of a number of constructors. All constructors
639-- must be from the same type.
650-- must be from the same type.
640−consFromJSON :: Name
651+consFromJSON :: JSONClass
652+ -- ^ The FromJSON variant being derived.
653+ -> Name
641 -- ^ Name of the type to which the constructors belong.
654 -- ^ Name of the type to which the constructors belong.
642 -> Options
655 -> Options
643 -- ^ Encoding options
656 -- ^ Encoding options
644− -> [Con]
657+ -> [Type]
658+ -- ^ The types from the data type/data family instance declaration
659+ -> [ConstructorInfo]
645 -- ^ Constructors for which to generate JSON parsing code.
660 -- ^ Constructors for which to generate JSON parsing code.
646 -> Q Exp
661 -> Q Exp
647
662
648−consFromJSON _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
663+consFromJSON _ _ _ _ [] = error $ "Data.Aeson.TH.consFromJSON: "
649− ++ "Not a single constructor given!"
664+ ++ "Not a single constructor given!"
650
665
651−consFromJSON tName opts [con] = do
666+consFromJSON jc tName opts instTys cons = do
652 value <- newName "value"
667 value <- newName "value"
653− lam1E (varP value) (parseArgs tName opts con (Right value))
668+ pjs <- newNameList "_pj" $ arityInt jc
654−
669+ pjls <- newNameList "_pjl" $ arityInt jc
655−consFromJSON tName opts cons = do
670+ let zippedPJs = zip pjs pjls
656− value <- newName "value"
671+ interleavedPJs = interleave pjs pjls
657− lam1E (varP value) $ caseE (varE value) $
672+ lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys
658− if allNullaryToStringTag opts && all isNullary cons
673+ tvMap = M.fromList $ zip lastTyVars zippedPJs
659− then allNullaryMatches
674+ lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap
660− else mixedMatches
661
675
662 where
676 where
677+ checkExi tvMap con = checkExistentialContext jc tvMap
678+ (constructorContext con)
679+ (constructorName con)
680+
681+ lamExpr value tvMap = case cons of
682+ [con]
683+ | not (tagSingleConstructors opts)
684+ -> checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right value)
685+ _ | sumEncoding opts == UntaggedValue
686+ -> parseUntaggedValue tvMap cons value
687+ | otherwise
688+ -> caseE (varE value) $
689+ if allNullaryToStringTag opts && all isNullary cons
690+ then allNullaryMatches
691+ else mixedMatches tvMap
692+
663 allNullaryMatches =
693 allNullaryMatches =
664 [ do txt <- newName "txt"
694 [ do txt <- newName "txt"
665 match (conP 'String [varP txt])
695 match (conP 'String [varP txt])
@@ -667,18 +697,17 @@ consFromJSON tName opts cons = do
667 [ liftM2 (,) (normalG $
697 [ liftM2 (,) (normalG $
668 infixApp (varE txt)
698 infixApp (varE txt)
669 [|(==)|]
699 [|(==)|]
670− ([|T.pack|] `appE`
671− conStringE opts conName)
700+ (conTxt opts conName)
672 )
701 )
673 ([|pure|] `appE` conE conName)
702 ([|pure|] `appE` conE conName)
674 | con <- cons
703 | con <- cons
675− , let conName = getConName con
704+ , let conName = constructorName con
676 ]
705 ]
677 ++
706 ++
678 [ liftM2 (,)
707 [ liftM2 (,)
679 (normalG [|otherwise|])
708 (normalG [|otherwise|])
680 ( [|noMatchFail|]
709 ( [|noMatchFail|]
681− `appE` (litE $ stringL $ show tName)
710+ `appE` litE (stringL $ show tName)
682 `appE` ([|T.unpack|] `appE` varE txt)
711 `appE` ([|T.unpack|] `appE` varE txt)
683 )
712 )
684 ]
713 ]
@@ -687,30 +716,31 @@ consFromJSON tName opts cons = do
687 , do other <- newName "other"
716 , do other <- newName "other"
688 match (varP other)
717 match (varP other)
689 (normalB $ [|noStringFail|]
718 (normalB $ [|noStringFail|]
690− `appE` (litE $ stringL $ show tName)
719+ `appE` litE (stringL $ show tName)
691 `appE` ([|valueConName|] `appE` varE other)
720 `appE` ([|valueConName|] `appE` varE other)
692 )
721 )
693 []
722 []
694 ]
723 ]
695
724
696− mixedMatches =
725+ mixedMatches tvMap =
697 case sumEncoding opts of
726 case sumEncoding opts of
698 TaggedObject {tagFieldName, contentsFieldName} ->
727 TaggedObject {tagFieldName, contentsFieldName} ->
699− parseObject $ parseTaggedObject tagFieldName contentsFieldName
728+ parseObject $ parseTaggedObject tvMap tagFieldName contentsFieldName
729+ UntaggedValue -> error "UntaggedValue: Should be handled already"
700 ObjectWithSingleField ->
730 ObjectWithSingleField ->
701− parseObject $ parseObjectWithSingleField
731+ parseObject $ parseObjectWithSingleField tvMap
702 TwoElemArray ->
732 TwoElemArray ->
703 [ do arr <- newName "array"
733 [ do arr <- newName "array"
704 match (conP 'Array [varP arr])
734 match (conP 'Array [varP arr])
705− (guardedB $
735+ (guardedB
706 [ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
736 [ liftM2 (,) (normalG $ infixApp ([|V.length|] `appE` varE arr)
707 [|(==)|]
737 [|(==)|]
708 (litE $ integerL 2))
738 (litE $ integerL 2))
709− (parse2ElemArray arr)
739+ (parse2ElemArray tvMap arr)
710 , liftM2 (,) (normalG [|otherwise|])
740 , liftM2 (,) (normalG [|otherwise|])
711− (([|not2ElemArray|]
741+ ([|not2ElemArray|]
712− `appE` (litE $ stringL $ show tName)
742+ `appE` litE (stringL $ show tName)
713− `appE` ([|V.length|] `appE` varE arr)))
743+ `appE` ([|V.length|] `appE` varE arr))
714 ]
744 ]
715 )
745 )
716 []
746 []
@@ -718,7 +748,7 @@ consFromJSON tName opts cons = do
718 match (varP other)
748 match (varP other)
719 ( normalB
749 ( normalB
720 $ [|noArrayFail|]
750 $ [|noArrayFail|]
721− `appE` (litE $ stringL $ show tName)
751+ `appE` litE (stringL $ show tName)
722 `appE` ([|valueConName|] `appE` varE other)
752 `appE` ([|valueConName|] `appE` varE other)
723 )
753 )
724 []
754 []
@@ -731,22 +761,47 @@ consFromJSON tName opts cons = do
731 match (varP other)
761 match (varP other)
732 ( normalB
762 ( normalB
733 $ [|noObjectFail|]
763 $ [|noObjectFail|]
734− `appE` (litE $ stringL $ show tName)
764+ `appE` litE (stringL $ show tName)
735 `appE` ([|valueConName|] `appE` varE other)
765 `appE` ([|valueConName|] `appE` varE other)
736 )
766 )
737 []
767 []
738 ]
768 ]
739
769
740− parseTaggedObject typFieldName valFieldName obj = do
770+ parseTaggedObject tvMap typFieldName valFieldName obj = do
741 conKey <- newName "conKey"
771 conKey <- newName "conKey"
742 doE [ bindS (varP conKey)
772 doE [ bindS (varP conKey)
743 (infixApp (varE obj)
773 (infixApp (varE obj)
744 [|(.:)|]
774 [|(.:)|]
745 ([|T.pack|] `appE` stringE typFieldName))
775 ([|T.pack|] `appE` stringE typFieldName))
746− , noBindS $ parseContents conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
776+ , noBindS $ parseContents tvMap conKey (Left (valFieldName, obj)) 'conNotFoundFailTaggedObject
747 ]
777 ]
748
778
779+ parseUntaggedValue tvMap cons' conVal =
780+ foldr1 (\e e' -> infixApp e [|(<|>)|] e')
781+ (map (\x -> parseValue tvMap x conVal) cons')
782+
783+ parseValue _tvMap
784+ ConstructorInfo { constructorName = conName
785+ , constructorVariant = NormalConstructor
786+ , constructorFields = [] }
787+ conVal = do
788+ str <- newName "str"
789+ caseE (varE conVal)
790+ [ match (conP 'String [varP str])
791+ (guardedB
792+ [ liftM2 (,) (normalG $ infixApp (varE str) [|(==)|] (conTxt opts conName)
793+ )
794+ ([|pure|] `appE` conE conName)
795+ ]
796+ )
797+ []
798+ , matchFailed tName conName "String"
799+ ]
800+ parseValue tvMap con conVal =
801+ checkExi tvMap con $ parseArgs jc tvMap tName opts con (Right conVal)
802+
803+
749− parse2ElemArray arr = do
804+ parse2ElemArray tvMap arr = do
750 conKey <- newName "conKey"
805 conKey <- newName "conKey"
751 conVal <- newName "conVal"
806 conVal <- newName "conVal"
752 let letIx n ix =
807 let letIx n ix =
@@ -761,7 +816,8 @@ consFromJSON tName opts cons = do
761 (caseE (varE conKey)
816 (caseE (varE conKey)
762 [ do txt <- newName "txt"
817 [ do txt <- newName "txt"
763 match (conP 'String [varP txt])
818 match (conP 'String [varP txt])
764− (normalB $ parseContents txt
819+ (normalB $ parseContents tvMap
820+ txt
765 (Right conVal)
821 (Right conVal)
766 'conNotFoundFail2ElemArray
822 'conNotFoundFail2ElemArray
767 )
823 )
@@ -770,30 +826,30 @@ consFromJSON tName opts cons = do
770 match (varP other)
826 match (varP other)
771 ( normalB
827 ( normalB
772 $ [|firstElemNoStringFail|]
828 $ [|firstElemNoStringFail|]
773− `appE` (litE $ stringL $ show tName)
829+ `appE` litE (stringL $ show tName)
774 `appE` ([|valueConName|] `appE` varE other)
830 `appE` ([|valueConName|] `appE` varE other)
775 )
831 )
776 []
832 []
777 ]
833 ]
778 )
834 )
779
835
780− parseObjectWithSingleField obj = do
836+ parseObjectWithSingleField tvMap obj = do
781 conKey <- newName "conKey"
837 conKey <- newName "conKey"
782 conVal <- newName "conVal"
838 conVal <- newName "conVal"
783 caseE ([e|H.toList|] `appE` varE obj)
839 caseE ([e|H.toList|] `appE` varE obj)
784 [ match (listP [tupP [varP conKey, varP conVal]])
840 [ match (listP [tupP [varP conKey, varP conVal]])
785− (normalB $ parseContents conKey (Right conVal) 'conNotFoundFailObjectSingleField)
841+ (normalB $ parseContents tvMap conKey (Right conVal) 'conNotFoundFailObjectSingleField)
786 []
842 []
787 , do other <- newName "other"
843 , do other <- newName "other"
788 match (varP other)
844 match (varP other)
789 (normalB $ [|wrongPairCountFail|]
845 (normalB $ [|wrongPairCountFail|]
790− `appE` (litE $ stringL $ show tName)
846+ `appE` litE (stringL $ show tName)
791 `appE` ([|show . length|] `appE` varE other)
847 `appE` ([|show . length|] `appE` varE other)
792 )
848 )
793 []
849 []
794 ]
850 ]
795
851
796− parseContents conKey contents errorFun =
852+ parseContents tvMap conKey contents errorFun =
797 caseE (varE conKey)
853 caseE (varE conKey)
798 [ match wildP
854 [ match wildP
799 ( guardedB $
855 ( guardedB $
@@ -801,7 +857,8 @@ consFromJSON tName opts cons = do
801 [|(==)|]
857 [|(==)|]
802 ([|T.pack|] `appE`
858 ([|T.pack|] `appE`
803 conNameExp opts con)
859 conNameExp opts con)
860+ e <- checkExi tvMap con $
804− e <- parseArgs tName opts con contents
861+ parseArgs jc tvMap tName opts con contents
805 return (g, e)
862 return (g, e)
806 | con <- cons
863 | con <- cons
807 ]
864 ]
@@ -809,12 +866,12 @@ consFromJSON tName opts cons = do
809 [ liftM2 (,)
866 [ liftM2 (,)
810 (normalG [e|otherwise|])
867 (normalG [e|otherwise|])
811 ( varE errorFun
868 ( varE errorFun
812− `appE` (litE $ stringL $ show tName)
869+ `appE` litE (stringL $ show tName)
813 `appE` listE (map ( litE
870 `appE` listE (map ( litE
814 . stringL
871 . stringL
815 . constructorTagModifier opts
872 . constructorTagModifier opts
816 . nameBase
873 . nameBase
817− . getConName
874+ . constructorName
818 ) cons
875 ) cons
819 )
876 )
820 `appE` ([|T.unpack|] `appE` varE conKey)
877 `appE` ([|T.unpack|] `appE` varE conKey)
@@ -828,13 +885,13 @@ parseNullaryMatches :: Name -> Name -> [Q Match]
828parseNullaryMatches tName conName =
885parseNullaryMatches tName conName =
829 [ do arr <- newName "arr"
886 [ do arr <- newName "arr"
830 match (conP 'Array [varP arr])
887 match (conP 'Array [varP arr])
831− (guardedB $
888+ (guardedB
832 [ liftM2 (,) (normalG $ [|V.null|] `appE` varE arr)
889 [ liftM2 (,) (normalG $ [|V.null|] `appE` varE arr)
833 ([|pure|] `appE` conE conName)
890 ([|pure|] `appE` conE conName)
834 , liftM2 (,) (normalG [|otherwise|])
891 , liftM2 (,) (normalG [|otherwise|])
835 (parseTypeMismatch tName conName
892 (parseTypeMismatch tName conName
836 (litE $ stringL "an empty Array")
893 (litE $ stringL "an empty Array")
837− (infixApp (litE $ stringL $ "Array of length ")
894+ (infixApp (litE $ stringL "Array of length ")
838 [|(++)|]
895 [|(++)|]
839 ([|show . V.length|] `appE` varE arr)
896 ([|show . V.length|] `appE` varE arr)
840 )
897 )
@@ -845,30 +902,60 @@ parseNullaryMatches tName conName =
845 , matchFailed tName conName "Array"
902 , matchFailed tName conName "Array"
846 ]
903 ]
847
904
848−parseUnaryMatches :: Name -> [Q Match]
905+parseUnaryMatches :: JSONClass -> TyVarMap -> Type -> Name -> [Q Match]
849−parseUnaryMatches conName =
906+parseUnaryMatches jc tvMap argTy conName =
850 [ do arg <- newName "arg"
907 [ do arg <- newName "arg"
851 match (varP arg)
908 match (varP arg)
852 ( normalB $ infixApp (conE conName)
909 ( normalB $ infixApp (conE conName)
853 [|(<$>)|]
910 [|(<$>)|]
911+ (dispatchParseJSON jc conName tvMap argTy
854− ([|parseJSON|] `appE` varE arg)
912+ `appE` varE arg)
855 )
913 )
856 []
914 []
857 ]
915 ]
858
916
859−parseRecord :: Options -> Name -> Name -> [VarStrictType] -> Name -> ExpQ
917+parseRecord :: JSONClass
918+ -> TyVarMap
919+ -> [Type]
920+ -> Options
921+ -> Name
922+ -> Name
923+ -> [Name]
924+ -> Name
925+ -> Bool
926+ -> ExpQ
860−parseRecord 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) $
861 foldl' (\a b -> infixApp a [|(<*>)|] b)
931 foldl' (\a b -> infixApp a [|(<*>)|] b)
862 (infixApp (conE conName) [|(<$>)|] x)
932 (infixApp (conE conName) [|(<$>)|] x)
863 xs
933 xs
864 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+ ]
865 x:xs = [ [|lookupField|]
951 x:xs = [ [|lookupField|]
952+ `appE` dispatchParseJSON jc conName tvMap argTy
866− `appE` (litE $ stringL $ show tName)
953+ `appE` litE (stringL $ show tName)
867− `appE` (litE $ stringL $ constructorTagModifier opts $ nameBase conName)
954+ `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName)
868− `appE` (varE obj)
955+ `appE` varE obj
869− `appE` ( [|T.pack|] `appE` fieldLabelExp opts field
956+ `appE` ( [|T.pack|] `appE` stringE (fieldLabel opts field)
870 )
957 )
871− | (field, _, _) <- ts
958+ | (field, argTy) <- zip fields argTys
872 ]
959 ]
873
960
874getValField :: Name -> String -> [MatchQ] -> Q Exp
961getValField :: Name -> String -> [MatchQ] -> Q Exp
@@ -877,84 +964,112 @@ getValField obj valFieldName matches = do
877 doE [ bindS (varP val) $ infixApp (varE obj)
964 doE [ bindS (varP val) $ infixApp (varE obj)
878 [|(.:)|]
965 [|(.:)|]
879 ([|T.pack|] `appE`
966 ([|T.pack|] `appE`
880− (litE $ stringL valFieldName))
967+ litE (stringL valFieldName))
881 , noBindS $ caseE (varE val) matches
968 , noBindS $ caseE (varE val) matches
882 ]
969 ]
883
970
971+matchCases :: Either (String, Name) Name -> [MatchQ] -> Q Exp
972+matchCases (Left (valFieldName, obj)) = getValField obj valFieldName
973+matchCases (Right valName) = caseE (varE valName)
974+
884-- | Generates code to parse the JSON encoding of a single constructor.
975-- | Generates code to parse the JSON encoding of a single constructor.
976+parseArgs :: JSONClass -- ^ The FromJSON variant being derived.
977+ -> TyVarMap -- ^ Maps the last type variables to their decoding
978+ -- function arguments.
885−parseArgs :: Name -- ^ Name of the type to which the constructor belongs.
979+ -> Name -- ^ Name of the type to which the constructor belongs.
886 -> Options -- ^ Encoding options.
980 -> Options -- ^ Encoding options.
887− -> Con -- ^ Constructor for which to generate JSON parsing code.
981+ -> ConstructorInfo -- ^ Constructor for which to generate JSON parsing code.
888 -> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
982 -> Either (String, Name) Name -- ^ Left (valFieldName, objName) or
889 -- Right valName
983 -- Right valName
890 -> Q Exp
984 -> Q Exp
891-- Nullary constructors.
985-- Nullary constructors.
892−parseArgs _ _ (NormalC conName []) (Left _) =
986+parseArgs _ _ _ _
987+ ConstructorInfo { constructorName = conName
988+ , constructorVariant = NormalConstructor
989+ , constructorFields = [] }
990+ (Left _) =
893− [|pure|] `appE` conE conName
991+ [|pure|] `appE` conE conName
894−parseArgs tName _ (NormalC conName []) (Right valName) =
992+parseArgs _ _ tName _
993+ ConstructorInfo { constructorName = conName
994+ , constructorVariant = NormalConstructor
995+ , constructorFields = [] }
996+ (Right valName) =
895− caseE (varE valName) $ parseNullaryMatches tName conName
997+ caseE (varE valName) $ parseNullaryMatches tName conName
896
998
897-- Unary constructors.
999-- Unary constructors.
898−parseArgs _ _ (NormalC conName [_]) (Left (valFieldName, obj)) =
1000+parseArgs jc tvMap _ _
899− getValField obj valFieldName $ parseUnaryMatches conName
1001+ ConstructorInfo { constructorName = conName
900−parseArgs _ _ (NormalC conName [_]) (Right valName) =
1002+ , constructorVariant = NormalConstructor
1003+ , constructorFields = [argTy] }
1004+ contents = do
1005+ argTy' <- resolveTypeSynonyms argTy
901− caseE (varE valName) $ parseUnaryMatches conName
1006+ matchCases contents $ parseUnaryMatches jc tvMap argTy' conName
902
1007
903-- Polyadic constructors.
1008-- Polyadic constructors.
904−parseArgs tName _ (NormalC conName ts) (Left (valFieldName, obj)) =
1009+parseArgs jc tvMap tName _
905− getValField obj valFieldName $ parseProduct tName conName $ genericLength ts
1010+ ConstructorInfo { constructorName = conName
906−parseArgs tName _ (NormalC conName ts) (Right valName) =
1011+ , constructorVariant = NormalConstructor
907− caseE (varE valName) $ parseProduct tName conName $ genericLength ts
1012+ , constructorFields = argTys }
1013+ contents = do
1014+ argTys' <- mapM resolveTypeSynonyms argTys
1015+ let len = genericLength argTys'
1016+ matchCases contents $ parseProduct jc tvMap argTys' tName conName len
908
1017
909-- Records.
1018-- Records.
910−parseArgs tName opts (RecC conName ts) (Left (_, obj)) =
1019+parseArgs jc tvMap tName opts
1020+ ConstructorInfo { constructorName = conName
1021+ , constructorVariant = RecordConstructor fields
1022+ , constructorFields = argTys }
1023+ (Left (_, obj)) = do
1024+ argTys' <- mapM resolveTypeSynonyms argTys
911− parseRecord 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) =
912−parseArgs tName opts (RecC conName ts) (Right valName) = case (unwrapUnaryRecords opts,ts) of
1031+ case (unwrapUnaryRecords opts,argTys) of
913− (True,[(_,st,ty)])-> parseArgs tName opts (NormalC conName [(st,ty)]) (Right valName)
1032+ (True,[_])-> parseArgs jc tvMap tName opts
1033+ (info{constructorVariant = NormalConstructor})
1034+ (Right valName)
914− _ -> do
1035+ _ -> do
915− obj <- newName "recObj"
1036+ obj <- newName "recObj"
1037+ argTys' <- mapM resolveTypeSynonyms argTys
916− caseE (varE valName)
1038+ caseE (varE valName)
917− [ match (conP 'Object [varP obj]) (normalB $ parseRecord opts tName conName ts obj) []
1039+ [ match (conP 'Object [varP obj]) (normalB $
1040+ parseRecord jc tvMap argTys' opts tName conName fields obj False) []
918− , matchFailed tName conName "Object"
1041+ , matchFailed tName conName "Object"
919− ]
1042+ ]
920
1043
921-- Infix constructors. Apart from syntax these are the same as
1044-- Infix constructors. Apart from syntax these are the same as
922-- polyadic constructors.
1045-- polyadic constructors.
923−parseArgs tName _ (InfixC _ conName _) (Left (valFieldName, obj)) =
1046+parseArgs jc tvMap tName _
924− getValField obj valFieldName $ parseProduct tName conName 2
1047+ ConstructorInfo { constructorName = conName
925−parseArgs tName _ (InfixC _ conName _) (Right valName) =
1048+ , constructorVariant = InfixConstructor
926− caseE (varE valName) $ parseProduct tName conName 2
1049+ , constructorFields = argTys }
927−
1050+ contents = do
928−-- Existentially quantified constructors. We ignore the quantifiers
1051+ argTys' <- mapM resolveTypeSynonyms argTys
929−-- and proceed with the contained constructor.
1052+ matchCases contents $ parseProduct jc tvMap argTys' tName conName 2
930−parseArgs tName opts (ForallC _ _ con) contents =
931− parseArgs tName opts con contents
932−
933−#if MIN_VERSION_template_haskell(2,11,0)
934−-- GADTs. We ignore the refined return type and proceed as if it were a
935−-- NormalC or RecC.
936−parseArgs tName opts (GadtC conNames ts _) contents =
937− parseArgs tName opts (NormalC (head conNames) ts) contents
938−
939−parseArgs tName opts (RecGadtC conNames ts _) contents =
940− parseArgs tName opts (RecC (head conNames) ts) contents
941−#endif
942
1053
943-- | Generates code to parse the JSON encoding of an n-ary
1054-- | Generates code to parse the JSON encoding of an n-ary
944-- constructor.
1055-- constructor.
1056+parseProduct :: JSONClass -- ^ The FromJSON variant being derived.
1057+ -> TyVarMap -- ^ Maps the last type variables to their decoding
1058+ -- function arguments.
1059+ -> [Type] -- ^ The argument types of the constructor.
945−parseProduct :: Name -- ^ Name of the type to which the constructor belongs.
1060+ -> Name -- ^ Name of the type to which the constructor belongs.
946 -> Name -- ^ 'Con'structor name.
1061 -> Name -- ^ 'Con'structor name.
947 -> Integer -- ^ 'Con'structor arity.
1062 -> Integer -- ^ 'Con'structor arity.
948 -> [Q Match]
1063 -> [Q Match]
949−parseProduct tName conName numArgs =
1064+parseProduct jc tvMap argTys tName conName numArgs =
950 [ do arr <- newName "arr"
1065 [ do arr <- newName "arr"
951 -- List of: "parseJSON (arr `V.unsafeIndex` <IX>)"
1066 -- List of: "parseJSON (arr `V.unsafeIndex` <IX>)"
952− let x:xs = [ [|parseJSON|]
1067+ let x:xs = [ dispatchParseJSON jc conName tvMap argTy
953 `appE`
1068 `appE`
954 infixApp (varE arr)
1069 infixApp (varE arr)
955 [|V.unsafeIndex|]
1070 [|V.unsafeIndex|]
956 (litE $ integerL ix)
1071 (litE $ integerL ix)
957− | ix <- [0 .. numArgs - 1]
1072+ | (argTy, ix) <- zip argTys [0 .. numArgs - 1]
958 ]
1073 ]
959 match (conP 'Array [varP arr])
1074 match (conP 'Array [varP arr])
960 (normalB $ condE ( infixApp ([|V.length|] `appE` varE arr)
1075 (normalB $ condE ( infixApp ([|V.length|] `appE` varE arr)
@@ -967,7 +1082,7 @@ parseProduct tName conName numArgs =
967 )
1082 )
968 ( parseTypeMismatch tName conName
1083 ( parseTypeMismatch tName conName
969 (litE $ stringL $ "Array of length " ++ show numArgs)
1084 (litE $ stringL $ "Array of length " ++ show numArgs)
970− ( infixApp (litE $ stringL $ "Array of length ")
1085+ ( infixApp (litE $ stringL "Array of length ")
971 [|(++)|]
1086 [|(++)|]
972 ([|show . V.length|] `appE` varE arr)
1087 ([|show . V.length|] `appE` varE arr)
973 )
1088 )
@@ -977,7 +1092,6 @@ parseProduct tName conName numArgs =
977 , matchFailed tName conName "Array"
1092 , matchFailed tName conName "Array"
978 ]
1093 ]
979
1094
980−
981--------------------------------------------------------------------------------
1095--------------------------------------------------------------------------------
982-- Parsing errors
1096-- Parsing errors
983--------------------------------------------------------------------------------
1097--------------------------------------------------------------------------------
@@ -1002,17 +1116,27 @@ parseTypeMismatch tName conName expected actual =
1002 , actual
1116 , actual
1003 ]
1117 ]
1004
1118
1005−class (FromJSON a) => LookupField a where
1119+class LookupField a where
1120+ lookupField :: (Value -> Parser a) -> String -> String
1006− lookupField :: String -> String -> Object -> T.Text -> Parser a
1121+ -> Object -> T.Text -> Parser a
1122+
1123+instance OVERLAPPABLE_ LookupField a where
1124+ lookupField = lookupFieldWith
1125+
1126+instance INCOHERENT_ LookupField (Maybe a) where
1127+ lookupField pj _ _ = parseOptionalFieldWith pj
1007
1128
1008−instance OVERLAPPABLE_ (FromJSON a) => LookupField a where
1129+instance INCOHERENT_ LookupField (Semigroup.Option a) where
1009− lookupField tName rec obj key =
1130+ lookupField pj tName rec obj key =
1010− case H.lookup key obj of
1131+ fmap Semigroup.Option
1011− Nothing -> unknownFieldFail tName rec (T.unpack key)
1132+ (lookupField (fmap Semigroup.getOption . pj) tName rec obj key)
1012− Just v -> parseJSON v <?> Key key
1013
1133
1014−instance (FromJSON a) => LookupField (Maybe a) where
1134+lookupFieldWith :: (Value -> Parser a) -> String -> String
1015− lookupField _ _ = (.:?)
1135+ -> Object -> T.Text -> Parser a
1136+lookupFieldWith pj tName rec obj key =
1137+ case H.lookup key obj of
1138+ Nothing -> unknownFieldFail tName rec (T.unpack key)
1139+ Just v -> pj v <?> Key key
1016
1140
1017unknownFieldFail :: String -> String -> String -> Parser fail
1141unknownFieldFail :: String -> String -> String -> Parser fail
1018unknownFieldFail tName rec key =
1142unknownFieldFail tName rec key =
@@ -1063,284 +1187,295 @@ parseTypeMismatch' conName tName expected actual =
1063 fail $ printf "When parsing the constructor %s of type %s expected %s but got %s."
1187 fail $ printf "When parsing the constructor %s of type %s expected %s but got %s."
1064 conName tName expected actual
1188 conName tName expected actual
1065
1189
1066−
1067--------------------------------------------------------------------------------
1190--------------------------------------------------------------------------------
1068−-- Utility functions
1191+-- Shared ToJSON and FromJSON code
1069--------------------------------------------------------------------------------
1192--------------------------------------------------------------------------------
1070
1193
1071−-- | Boilerplate for top level splices.
1194+-- | Functionality common to 'deriveJSON', 'deriveJSON1', and 'deriveJSON2'.
1072−--
1195+deriveJSONBoth :: (Options -> Name -> Q [Dec])
1073−-- The given 'Name' must meet one of two criteria:
1196+ -- ^ Function which derives a flavor of 'ToJSON'.
1074−--
1197+ -> (Options -> Name -> Q [Dec])
1075−-- 1. It must be the name of a type constructor of a plain data type or newtype.
1198+ -- ^ Function which derives a flavor of 'FromJSON'.
1076−-- 2. It must be the name of a data family instance or newtype instance constructor.
1199+ -> Options
1077−
1200+ -- ^ Encoding options.
1078−-- Any other value will result in an exception.
1201+ -> Name
1079−withType :: Name
1202+ -- ^ Name of the type for which to generate 'ToJSON' and 'FromJSON'
1080− -> (Name -> [TyVarBndr] -> [Con] -> Maybe [Type] -> Q a)
1203+ -- instances.
1081− -- ^ Function that generates the actual code. Will be applied
1204+ -> Q [Dec]
1082− -- to the datatype/data family 'Name', type variable binders and
1205+deriveJSONBoth dtj dfj opts name =
1083− -- constructors extracted from the given 'Name'. If the 'Name' is
1206+ liftM2 (++) (dtj opts name) (dfj opts name)
1084− -- from a data family instance constructor, it will also have its
1207+
1085− -- instantiated types; otherwise, it will be 'Nothing'.
1208+-- | Functionality common to @deriveToJSON(1)(2)@ and @deriveFromJSON(1)(2)@.
1086− -> Q a
1209+deriveJSONClass :: [(JSONFun, JSONClass -> Name -> Options -> [Type]
1087− -- ^ Resulting value in the 'Q'uasi monad.
1210+ -> [ConstructorInfo] -> Q Exp)]
1088−withType name f = do
1211+ -- ^ The class methods and the functions which derive them.
1212+ -> JSONClass
1213+ -- ^ The class for which to generate an instance.
1214+ -> Options
1215+ -- ^ Encoding options.
1216+ -> Name
1217+ -- ^ Name of the type for which to generate a class instance
1218+ -- declaration.
1219+ -> Q [Dec]
1220+deriveJSONClass consFuns jc opts name = do
1089− info <- reify name
1221+ info <- reifyDatatype name
1090− case info of
1222+ case info of
1091− TyConI dec ->
1223+ DatatypeInfo { datatypeContext = ctxt
1092− case dec of
1224+ , datatypeName = parentName
1093−#if MIN_VERSION_template_haskell(2,11,0)
1225+#if MIN_VERSION_th_abstraction(0,3,0)
1094− DataD _ _ tvbs _ cons _ -> f name tvbs cons Nothing
1226+ , datatypeInstTypes = instTys
1095− NewtypeD _ _ tvbs _ con _ -> f name tvbs [con] Nothing
1096−#else
1097− DataD _ _ tvbs cons _ -> f name tvbs cons Nothing
1098− NewtypeD _ _ tvbs con _ -> f name tvbs [con] Nothing
1099−#endif
1100− other -> error $ ns ++ "Unsupported type: " ++ show other
1101−#if MIN_VERSION_template_haskell(2,11,0)
1102− DataConI _ _ parentName -> do
1103−#else
1104− DataConI _ _ parentName _ -> do
1105−#endif
1106− parentInfo <- reify parentName
1107− case parentInfo of
1108−#if MIN_VERSION_template_haskell(2,11,0)
1109− FamilyI (DataFamilyD _ tvbs _) decs ->
1110−#else
1111− FamilyI (FamilyD DataFam _ tvbs _) decs ->
1112−#endif
1113− let instDec = flip find decs $ \dec -> case dec of
1114−#if MIN_VERSION_template_haskell(2,11,0)
1115− DataInstD _ _ _ _ cons _ -> any ((name ==) . getConName) cons
1116− NewtypeInstD _ _ _ _ con _ -> name == getConName con
1117−#else
1118− DataInstD _ _ _ cons _ -> any ((name ==) . getConName) cons
1119− NewtypeInstD _ _ _ con _ -> name == getConName con
1120−#endif
1121− _ -> error $ ns ++ "Must be a data or newtype instance."
1122− in case instDec of
1123−#if MIN_VERSION_template_haskell(2,11,0)
1124− Just (DataInstD _ _ instTys _ cons _) -> f parentName tvbs cons $ Just instTys
1125− Just (NewtypeInstD _ _ instTys _ con _) -> f parentName tvbs [con] $ Just instTys
1126−#else
1127− Just (DataInstD _ _ instTys cons _) -> f parentName tvbs cons $ Just instTys
1128− Just (NewtypeInstD _ _ instTys con _) -> f parentName tvbs [con] $ Just instTys
1129−#endif
1130− _ -> error $ ns ++
1131− "Could not find data or newtype instance constructor."
1132− _ -> error $ ns ++ "Data constructor " ++ show name ++
1133− " is not from a data family instance constructor."
1134−#if MIN_VERSION_template_haskell(2,11,0)
1135− FamilyI DataFamilyD{} _ ->
1136#else
1227#else
1137− FamilyI (FamilyD DataFam _ _ _) _ ->
1228+ , datatypeVars = instTys
1138#endif
1229#endif
1139− error $ ns ++
1230+ , datatypeVariant = variant
1140− "Cannot use a data family name. Use a data family instance constructor instead."
1231+ , datatypeCons = cons
1141− _ -> error $ ns ++ "I need the name of a plain data type constructor, "
1232+ } -> do
1142− ++ "or a data family instance constructor."
1233+ (instanceCxt, instanceType)
1234+ <- buildTypeInstance parentName jc ctxt instTys variant
1235+ (:[]) <$> instanceD (return instanceCxt)
1236+ (return instanceType)
1237+ (methodDecs parentName instTys cons)
1143 where
1238 where
1144− ns :: String
1239+ methodDecs :: Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
1145− ns = "Data.Aeson.TH.withType: "
1240+ methodDecs parentName instTys cons = flip map consFuns $ \(jf, jfMaker) ->
1241+ funD (jsonFunValName jf (arity jc))
1242+ [ clause []
1243+ (normalB $ jfMaker jc parentName opts instTys cons)
1244+ []
1245+ ]
1146
1246
1147−-- | Infer the context and instance head needed for a FromJSON or ToJSON instance.
1247+mkFunCommon :: (JSONClass -> Name -> Options -> [Type] -> [ConstructorInfo] -> Q Exp)
1148−buildTypeInstance :: Name
1248+ -- ^ The function which derives the expression.
1149− -- ^ The type constructor or data family name
1249+ -> JSONClass
1150− -> Name
1250+ -- ^ Which class's method is being derived.
1151− -- ^ The typeclass name ('ToJSON' or 'FromJSON')
1251+ -> Options
1152− -> [TyVarBndr]
1252+ -- ^ Encoding options.
1153− -- ^ The type variables from the data type/data family declaration
1253+ -> Name
1154− -> Maybe [Type]
1254+ -- ^ Name of the encoded type.
1155− -- ^ 'Just' the types used to instantiate a data family instance,
1255+ -> Q Exp
1156− -- or 'Nothing' if it's a plain data type
1256+mkFunCommon consFun jc opts name = do
1157− -> Q (Cxt, Type)
1257+ info <- reifyDatatype name
1158− -- ^ The resulting 'Cxt' and 'Type' to use in a class instance
1258+ case info of
1159−-- Plain data type/newtype case
1259+ DatatypeInfo { datatypeContext = ctxt
1160−buildTypeInstance tyConName constraint tvbs Nothing =
1260+ , datatypeName = parentName
1161− let varTys :: [Type]
1261+#if MIN_VERSION_th_abstraction(0,3,0)
1162− varTys = map tvbToType tvbs
1262+ , datatypeInstTypes = instTys
1163− in buildTypeInstanceFromTys tyConName constraint varTys False
1164−-- Data family instance case
1165−--
1166−-- The CPP is present to work around a couple of annoying old GHC bugs.
1167−-- See Note [Polykinded data families in Template Haskell]
1168−buildTypeInstance dataFamName constraint tvbs (Just instTysAndKinds) = do
1169−#if !(MIN_VERSION_template_haskell(2,8,0)) || MIN_VERSION_template_haskell(2,10,0)
1170− let instTys :: [Type]
1171− instTys = zipWith stealKindForType tvbs instTysAndKinds
1172#else
1263#else
1173− let kindVarNames :: [Name]
1174− kindVarNames = nub $ concatMap (tyVarNamesOfType . tvbKind) tvbs
1175−
1176− -- Gets all of the type/kind variable names mentioned somewhere in a Type.
1177− tyVarNamesOfType :: Type -> [Name]
1178− tyVarNamesOfType = go
1179− where
1180− go :: Type -> [Name]
1181− go (AppT t1 t2) = go t1 ++ go t2
1182− go (SigT t k) = go t ++ go k
1183− go (VarT n) = [n]
1184− go _ = []
1185−
1186− numKindVars :: Int
1187− numKindVars = length kindVarNames
1188−
1189− givenKinds, givenKinds' :: [Kind]
1190− givenTys :: [Type]
1191− (givenKinds, givenTys) = splitAt numKindVars instTysAndKinds
1192− givenKinds' = map sanitizeStars givenKinds
1193−
1194− -- A GHC 7.6-specific bug requires us to replace all occurrences of
1195− -- (ConT GHC.Prim.*) with StarT, or else Template Haskell will reject it.
1196− -- Luckily, (ConT GHC.Prim.*) only seems to occur in this one spot.
1197− sanitizeStars :: Kind -> Kind
1198− sanitizeStars = go
1199− where
1200− go :: Kind -> Kind
1201− go (AppT t1 t2) = AppT (go t1) (go t2)
1202− go (SigT t k) = SigT (go t) (go k)
1203− go (ConT n) | n == starKindName = StarT
1204− go t = t
1205−
1206− -- It's quite awkward to import * from GHC.Prim, so we'll just
1207− -- hack our way around it.
1208− starKindName :: Name
1209− starKindName = mkNameG_tc "ghc-prim" "GHC.Prim" "*"
1210−
1211− -- Generate a list of fresh names with a common prefix, and numbered suffixes.
1212− newNameList :: String -> Int -> Q [Name]
1213− newNameList prefix n = mapM (newName . (prefix ++) . show) [1..n]
1214−
1215− -- If we run this code with GHC 7.8, we might have to generate extra type
1216− -- variables to compensate for any type variables that Template Haskell
1217− -- eta-reduced away.
1218− -- See Note [Polykinded data families in Template Haskell]
1219− xTypeNames <- newNameList "tExtra" (length tvbs - length givenTys)
1220−
1221− let xTys :: [Type]
1222− xTys = map VarT xTypeNames
1223− -- ^ Because these type variables were eta-reduced away, we can only
1224− -- determine their kind by using stealKindForType. Therefore, we mark
1225− -- them as VarT to ensure they will be given an explicit kind annotation
1226− -- (and so the kind inference machinery has the right information).
1227−
1228− substNameWithKind :: Name -> Kind -> Type -> Type
1229− substNameWithKind n k = substType (M.singleton n k)
1230−
1231− substNamesWithKinds :: [(Name, Kind)] -> Type -> Type
1232− substNamesWithKinds nks t = foldr' (uncurry substNameWithKind) t nks
1233−
1234− -- The types from the data family instance might not have explicit kind
1235− -- annotations, which the kind machinery needs to work correctly. To
1236− -- compensate, we use stealKindForType to explicitly annotate any
1237− -- types without kind annotations.
1238− instTys :: [Type]
1264+ , datatypeVars = instTys
1239− instTys = map (substNamesWithKinds (zip kindVarNames givenKinds'))
1240− -- Note that due to a GHC 7.8-specific bug
1241− -- (see Note [Polykinded data families in Template Haskell]),
1242− -- there may be more kind variable names than there are kinds
1243− -- to substitute. But this is OK! If a kind is eta-reduced, it
1244− -- means that is was not instantiated to something more specific,
1245− -- so we need not substitute it. Using stealKindForType will
1246− -- grab the correct kind.
1247− $ zipWith stealKindForType tvbs (givenTys ++ xTys)
1248#endif
1265#endif
1249− buildTypeInstanceFromTys dataFamName constraint instTys True
1266+ , datatypeVariant = variant
1250−
1267+ , datatypeCons = cons
1251−-- For the given Types, generate an instance context and head.
1268+ } -> do
1252−buildTypeInstanceFromTys :: Name
1269+ -- We force buildTypeInstance here since it performs some checks for whether
1253− -- ^ The type constructor or data family name
1270+ -- or not the provided datatype's kind matches the derived method's
1254− -> Name
1271+ -- typeclass, and produces errors if it can't.
1255− -- ^ The typeclass name ('ToJSON' or 'FromJSON')
1272+ !_ <- buildTypeInstance parentName jc ctxt instTys variant
1256− -> [Type]
1273+ consFun jc parentName opts instTys cons
1257− -- ^ The types to instantiate the instance with
1274+
1258− -> Bool
1275+dispatchFunByType :: JSONClass
1259− -- ^ True if it's a data family, False otherwise
1276+ -> JSONFun
1260− -> Q (Cxt, Type)
1277+ -> Name
1261−buildTypeInstanceFromTys tyConName constraint varTysOrig isDataFamily = do
1278+ -> TyVarMap
1262− -- Make sure to expand through type/kind synonyms! Otherwise, we won't
1279+ -> Bool -- True if we are using the function argument that works
1263− -- be able to infer constraints as accurately.
1280+ -- on lists (e.g., [a] -> Value). False is we are using
1264− varTysExp <- mapM expandSyn varTysOrig
1281+ -- the function argument that works on single values
1265−
1282+ -- (e.g., a -> Value).
1266− let preds :: [Maybe Pred]
1283+ -> Type
1267− -- Derive instance constraints for type variables of kind *
1284+ -> Q Exp
1268− preds = map (deriveConstraint constraint) varTysExp
1285+dispatchFunByType _ jf _ tvMap list (VarT tyName) =
1269−
1286+ varE $ case M.lookup tyName tvMap of
1270− varTys :: [Type]
1287+ Just (tfjExp, tfjlExp) -> if list then tfjlExp else tfjExp
1271− -- See Note [Kind signatures in derived instances] for an explanation
1288+ Nothing -> jsonFunValOrListName list jf Arity0
1272− -- of the isDataFamily check.
1289+dispatchFunByType jc jf conName tvMap list (SigT ty _) =
1273− varTys =
1290+ dispatchFunByType jc jf conName tvMap list ty
1274− if isDataFamily
1291+dispatchFunByType jc jf conName tvMap list (ForallT _ _ ty) =
1275− then varTysOrig
1292+ dispatchFunByType jc jf conName tvMap list ty
1276− else map unSigT varTysOrig
1293+dispatchFunByType jc jf conName tvMap list ty = do
1294+ let tyCon :: Type
1295+ tyArgs :: [Type]
1296+ tyCon :| tyArgs = unapplyTy ty
1297+
1298+ numLastArgs :: Int
1299+ numLastArgs = min (arityInt jc) (length tyArgs)
1300+
1301+ lhsArgs, rhsArgs :: [Type]
1302+ (lhsArgs, rhsArgs) = splitAt (length tyArgs - numLastArgs) tyArgs
1303+
1304+ tyVarNames :: [Name]
1305+ tyVarNames = M.keys tvMap
1306+
1307+ itf <- isTyFamily tyCon
1308+ if any (`mentionsName` tyVarNames) lhsArgs
1309+ || itf && any (`mentionsName` tyVarNames) tyArgs
1310+ then outOfPlaceTyVarError jc conName
1311+ else if any (`mentionsName` tyVarNames) rhsArgs
1312+ then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs)
1313+ : zipWith (dispatchFunByType jc jf conName tvMap)
1314+ (cycle [False,True])
1315+ (interleave rhsArgs rhsArgs)
1316+ else varE $ jsonFunValOrListName list jf Arity0
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+
1323+dispatchParseJSON
1324+ :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp
1325+dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False
1277
1326
1278− instanceCxt :: Cxt
1327+--------------------------------------------------------------------------------
1279− instanceCxt = catMaybes preds
1328+-- Utility functions
1329+--------------------------------------------------------------------------------
1280
1330
1281− instanceType :: Type
1331+-- For the given Types, generate an instance context and head.
1282− instanceType = AppT (ConT constraint)
1332+buildTypeInstance :: Name
1283− $ applyTyCon tyConName varTys
1333+ -- ^ The type constructor or data family name
1334+ -> JSONClass
1335+ -- ^ The typeclass to derive
1336+ -> Cxt
1337+ -- ^ The datatype context
1338+ -> [Type]
1339+ -- ^ The types to instantiate the instance with
1340+ -> DatatypeVariant
1341+ -- ^ Are we dealing with a data family instance or not
1342+ -> Q (Cxt, Type)
1343+buildTypeInstance tyConName jc dataCxt varTysOrig variant = do
1344+ -- Make sure to expand through type/kind synonyms! Otherwise, the
1345+ -- eta-reduction check might get tripped up over type variables in a
1346+ -- synonym that are actually dropped.
1347+ -- (See GHC Trac #11416 for a scenario where this actually happened.)
1348+ varTysExp <- mapM resolveTypeSynonyms varTysOrig
1284
1349
1285− return (instanceCxt, instanceType)
1350+ let remainingLength :: Int
1351+ remainingLength = length varTysOrig - arityInt jc
1286
1352
1287−-- | Attempt to derive a constraint on a Type. If it's of kind *,
1353+ droppedTysExp :: [Type]
1288−-- we give it Just a ToJSON/FromJSON constraint. Otherwise, return Nothing.
1354+ droppedTysExp = drop remainingLength varTysExp
1289−deriveConstraint :: Name -> Type -> Maybe Pred
1290−deriveConstraint constraint t
1291− | isTyVar t && hasKindStar t = Just $ applyCon constraint $ varTToName t
1292− | otherwise = Nothing
1293
1355
1294−{-
1356+ droppedStarKindStati :: [StarKindStatus]
1295−Note [Polykinded data families in Template Haskell]
1357+ droppedStarKindStati = map canRealizeKindStar droppedTysExp
1296−~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1297
1358
1298−In order to come up with the correct instance context and head for an instance, e.g.,
1359+ -- Check there are enough types to drop and that all of them are either of
1360+ -- kind * or kind k (for some kind variable k). If not, throw an error.
1361+ when (remainingLength < 0 || elem NotKindStar droppedStarKindStati) $
1362+ derivingKindError jc tyConName
1299
1363
1300− instance C a => C (Data a) where ...
1364+ let droppedKindVarNames :: [Name]
1365+ droppedKindVarNames = catKindVarNames droppedStarKindStati
1301
1366
1302−We need to know the exact types and kinds used to instantiate the instance. For
1367+ -- Substitute kind * for any dropped kind variables
1303−plain old datatypes, this is simple: every type must be a type variable, and
1368+ varTysExpSubst :: [Type]
1304−Template Haskell reliably tells us the type variables and their kinds.
1369+ varTysExpSubst = map (substNamesWithKindStar droppedKindVarNames) varTysExp
1305
1370
1306−Doing the same for data families proves to be much harder for three reasons:
1371+ remainingTysExpSubst, droppedTysExpSubst :: [Type]
1372+ (remainingTysExpSubst, droppedTysExpSubst) =
1373+ splitAt remainingLength varTysExpSubst
1307
1374
1308−1. On any version of Template Haskell, it may not tell you what an instantiated
1375+ -- All of the type variables mentioned in the dropped types
1309− type's kind is. For instance, in the following data family instance:
1376+ -- (post-synonym expansion)
1377+ droppedTyVarNames :: [Name]
1378+ droppedTyVarNames = freeVariables droppedTysExpSubst
1310
1379
1311− data family Fam (f :: * -> *) (a :: *)
1380+ -- If any of the dropped types were polykinded, ensure that they are of kind *
1312− data instance Fam f a
1381+ -- after substituting * for the dropped kind variables. If not, throw an error.
1382+ unless (all hasKindStar droppedTysExpSubst) $
1383+ derivingKindError jc tyConName
1313
1384
1314− Then if we use TH's reify function, it would tell us the TyVarBndrs of the
1385+ let preds :: [Maybe Pred]
1315− data family declaration are:
1386+ kvNames :: [[Name]]
1387+ kvNames' :: [Name]
1388+ -- Derive instance constraints (and any kind variables which are specialized
1389+ -- to * in those constraints)
1390+ (preds, kvNames) = unzip $ map (deriveConstraint jc) remainingTysExpSubst
1391+ kvNames' = concat kvNames
1392+
1393+ -- Substitute the kind variables specialized in the constraints with *
1394+ remainingTysExpSubst' :: [Type]
1395+ remainingTysExpSubst' =
1396+ map (substNamesWithKindStar kvNames') remainingTysExpSubst
1397+
1398+ -- We now substitute all of the specialized-to-* kind variable names with
1399+ -- *, but in the original types, not the synonym-expanded types. The reason
1400+ -- we do this is a superficial one: we want the derived instance to resemble
1401+ -- the datatype written in source code as closely as possible. For example,
1402+ -- for the following data family instance:
1403+ --
1404+ -- data family Fam a
1405+ -- newtype instance Fam String = Fam String
1406+ --
1407+ -- We'd want to generate the instance:
1408+ --
1409+ -- instance C (Fam String)
1410+ --
1411+ -- Not:
1412+ --
1413+ -- instance C (Fam [Char])
1414+ remainingTysOrigSubst :: [Type]
1415+ remainingTysOrigSubst =
1416+ map (substNamesWithKindStar (droppedKindVarNames `union` kvNames'))
1417+ $ take remainingLength varTysOrig
1418+
1419+ isDataFamily :: Bool
1420+ isDataFamily = case variant of
1421+ Datatype -> False
1422+ Newtype -> False
1423+ DataInstance -> True
1424+ NewtypeInstance -> True
1425+
1426+ remainingTysOrigSubst' :: [Type]
1427+ -- See Note [Kind signatures in derived instances] for an explanation
1428+ -- of the isDataFamily check.
1429+ remainingTysOrigSubst' =
1430+ if isDataFamily
1431+ then remainingTysOrigSubst
1432+ else map unSigT remainingTysOrigSubst
1316
1433
1317− [KindedTV f (AppT (AppT ArrowT StarT) StarT),KindedTV a StarT]
1434+ instanceCxt :: Cxt
1435+ instanceCxt = catMaybes preds
1318
1436
1319− and the instantiated types of the data family instance are:
1437+ instanceType :: Type
1438+ instanceType = AppT (ConT $ jsonClassName jc)
1439+ $ applyTyCon tyConName remainingTysOrigSubst'
1440+
1441+ -- If the datatype context mentions any of the dropped type variables,
1442+ -- we can't derive an instance, so throw an error.
1443+ when (any (`predMentionsName` droppedTyVarNames) dataCxt) $
1444+ datatypeContextError tyConName instanceType
1445+ -- Also ensure the dropped types can be safely eta-reduced. Otherwise,
1446+ -- throw an error.
1447+ unless (canEtaReduce remainingTysExpSubst' droppedTysExpSubst) $
1448+ etaReductionError instanceType
1449+ return (instanceCxt, instanceType)
1320
1450
1321− [VarT f1,VarT a1]
1451+-- | Attempt to derive a constraint on a Type. If successful, return
1452+-- Just the constraint and any kind variable names constrained to *.
1453+-- Otherwise, return Nothing and the empty list.
1454+--
1455+-- See Note [Type inference in derived instances] for the heuristics used to
1456+-- come up with constraints.
1457+deriveConstraint :: JSONClass -> Type -> (Maybe Pred, [Name])
1458+deriveConstraint jc t
1459+ | not (isTyVar t) = (Nothing, [])
1460+ | hasKindStar t = (Just (applyCon (jcConstraint Arity0) tName), [])
1461+ | otherwise = case hasKindVarChain 1 t of
1462+ Just ns | jcArity >= Arity1
1463+ -> (Just (applyCon (jcConstraint Arity1) tName), ns)
1464+ _ -> case hasKindVarChain 2 t of
1465+ Just ns | jcArity == Arity2
1466+ -> (Just (applyCon (jcConstraint Arity2) tName), ns)
1467+ _ -> (Nothing, [])
1468+ where
1469+ tName :: Name
1470+ tName = varTToName t
1322
1471
1323− We can't just pass [VarT f1,VarT a1] to buildTypeInstanceFromTys, since we
1472+ jcArity :: Arity
1324− have no way of knowing their kinds. Luckily, the TyVarBndrs tell us what the
1473+ jcArity = arity jc
1325− kind is in case an instantiated type isn't a SigT, so we use the stealKindForType
1326− function to ensure all of the instantiated types are SigTs before passing them
1327− to buildTypeInstanceFromTys.
1328−2. On GHC 7.6 and 7.8, a bug is present in which Template Haskell lists all of
1329− the specified kinds of a data family instance efore any of the instantiated
1330− types. Fortunately, this is easy to deal with: you simply count the number of
1331− distinct kind variables in the data family declaration, take that many elements
1332− from the front of the Types list of the data family instance, substitute the
1333− kind variables with their respective instantiated kinds (which you took earlier),
1334− and proceed as normal.
1335−3. On GHC 7.8, an even uglier bug is present (GHC Trac #9692) in which Template
1336− Haskell might not even list all of the Types of a data family instance, since
1337− they are eta-reduced away! And yes, kinds can be eta-reduced too.
1338
1474
1339− The simplest workaround is to count how many instantiated types are missing from
1475+ jcConstraint :: Arity -> Name
1340− the list and generate extra type variables to use in their place. Luckily, we
1476+ jcConstraint = jsonClassName . JSONClass (direction jc)
1341− needn't worry much if its kind was eta-reduced away, since using stealKindForType
1342− will get it back.
1343
1477
1478+{-
1344Note [Kind signatures in derived instances]
1479Note [Kind signatures in derived instances]
1345~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1480~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1346
1481
@@ -1372,53 +1507,145 @@ knowing which instance we are talking about. To avoid this scenario, we always
1372include explicit kind signatures in data family instances. There is a chance that
1507include explicit kind signatures in data family instances. There is a chance that
1373the inferred kind signatures will be incorrect, but if so, we can always fall back
1508the inferred kind signatures will be incorrect, but if so, we can always fall back
1374on the mk- functions.
1509on the mk- functions.
1510+
1511+Note [Type inference in derived instances]
1512+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1513+
1514+Type inference is can be tricky to get right, and we want to avoid recreating the
1515+entirety of GHC's type inferencer in Template Haskell. For this reason, we will
1516+probably never come up with derived instance contexts that are as accurate as
1517+GHC's. But that doesn't mean we can't do anything! There are a couple of simple
1518+things we can do to make instance contexts that work for 80% of use cases:
1519+
1520+1. If one of the last type parameters is polykinded, then its kind will be
1521+ specialized to * in the derived instance. We note what kind variable the type
1522+ parameter had and substitute it with * in the other types as well. For example,
1523+ imagine you had
1524+
1525+ data Data (a :: k) (b :: k)
1526+
1527+ Then you'd want to derived instance to be:
1528+
1529+ instance C (Data (a :: *))
1530+
1531+ Not:
1532+
1533+ instance C (Data (a :: k))
1534+
1535+2. We naïvely come up with instance constraints using the following criteria:
1536+
1537+ (i) If there's a type parameter n of kind *, generate a ToJSON n/FromJSON n
1538+ constraint.
1539+ (ii) If there's a type parameter n of kind k1 -> k2 (where k1/k2 are * or kind
1540+ variables), then generate a ToJSON1 n/FromJSON1 n constraint, and if
1541+ k1/k2 are kind variables, then substitute k1/k2 with * elsewhere in the
1542+ types. We must consider the case where they are kind variables because
1543+ you might have a scenario like this:
1544+
1545+ newtype Compose (f :: k2 -> *) (g :: k1 -> k2) (a :: k1)
1546+ = Compose (f (g a))
1547+
1548+ Which would have a derived ToJSON1 instance of:
1549+
1550+ instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Compose f g) where ...
1551+ (iii) If there's a type parameter n of kind k1 -> k2 -> k3 (where k1/k2/k3 are
1552+ * or kind variables), then generate a ToJSON2 n/FromJSON2 n constraint
1553+ and perform kind substitution as in the other cases.
1375-}
1554-}
1376
1555
1377−-- | If a VarT is missing an explicit kind signature, steal it from a TyVarBndr.
1556+checkExistentialContext :: JSONClass -> TyVarMap -> Cxt -> Name
1378−stealKindForType :: TyVarBndr -> Type -> Type
1557+ -> Q a -> Q a
1379−stealKindForType tvb t@VarT{} = SigT t (tvbKind tvb)
1558+checkExistentialContext jc tvMap ctxt conName q =
1380−stealKindForType _ t = t
1559+ if (any (`predMentionsName` M.keys tvMap) ctxt
1560+ || M.size tvMap < arityInt jc)
1561+ && not (allowExQuant jc)
1562+ then existentialContextError conName
1563+ else q
1381
1564
1382−-- | Extracts the kind from a type variable binder.
1565+{-
1383−tvbKind :: TyVarBndr -> Kind
1566+Note [Matching functions with GADT type variables]
1384−#if MIN_VERSION_template_haskell(2,8,0)
1567+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1385−tvbKind (PlainTV _ ) = StarT
1568+
1386−#else
1569+When deriving ToJSON2, there is a tricky corner case to consider:
1387−tvbKind (PlainTV _ ) = StarK
1570+
1388−#endif
1571+ data Both a b where
1389−tvbKind (KindedTV _ k) = k
1572+ BothCon :: x -> x -> Both x x
1573+
1574+Which encoding functions should be applied to which arguments of BothCon?
1575+We have a choice, since both the function of type (a -> Value) and of type
1576+(b -> Value) can be applied to either argument. In such a scenario, the
1577+second encoding function takes precedence over the first encoding function, so the
1578+derived ToJSON2 instance would be something like:
1579+
1580+ instance ToJSON2 Both where
1581+ liftToJSON2 tj1 tj2 p (BothCon x1 x2) = Array $ create $ do
1582+ mv <- unsafeNew 2
1583+ unsafeWrite mv 0 (tj1 x1)
1584+ unsafeWrite mv 1 (tj2 x2)
1585+ return mv
1586+
1587+This is not an arbitrary choice, as this definition ensures that
1588+liftToJSON2 toJSON = liftToJSON for a derived ToJSON1 instance for
1589+Both.
1590+-}
1390
1591
1391−tvbToType :: TyVarBndr -> Type
1592+-- A mapping of type variable Names to their encoding/decoding function Names.
1392−tvbToType (PlainTV n) = VarT n
1593+-- For example, in a ToJSON2 declaration, a TyVarMap might look like
1393−tvbToType (KindedTV n k) = SigT (VarT n) k
1594+--
1595+-- { a ~> (tj1, tjl1)
1596+-- , b ~> (tj2, tjl2) }
1597+--
1598+-- where a and b are the last two type variables of the datatype, tj1 and tjl1 are
1599+-- the function arguments of types (a -> Value) and ([a] -> Value), and tj2 and tjl2
1600+-- are the function arguments of types (b -> Value) and ([b] -> Value).
1601+type TyVarMap = Map Name (Name, Name)
1394
1602
1395-- | Returns True if a Type has kind *.
1603-- | Returns True if a Type has kind *.
1396hasKindStar :: Type -> Bool
1604hasKindStar :: Type -> Bool
1397hasKindStar VarT{} = True
1605hasKindStar VarT{} = True
1398−#if MIN_VERSION_template_haskell(2,8,0)
1399hasKindStar (SigT _ StarT) = True
1606hasKindStar (SigT _ StarT) = True
1400−#else
1401−hasKindStar (SigT _ StarK) = True
1402−#endif
1403hasKindStar _ = False
1607hasKindStar _ = False
1404
1608
1609+-- Returns True is a kind is equal to *, or if it is a kind variable.
1610+isStarOrVar :: Kind -> Bool
1611+isStarOrVar StarT = True
1612+isStarOrVar VarT{} = True
1613+isStarOrVar _ = False
1614+
1615+-- Generate a list of fresh names with a common prefix, and numbered suffixes.
1616+newNameList :: String -> Int -> Q [Name]
1617+newNameList prefix len = mapM newName [prefix ++ show n | n <- [1..len]]
1618+
1619+-- | @hasKindVarChain n kind@ Checks if @kind@ is of the form
1620+-- k_0 -> k_1 -> ... -> k_(n-1), where k0, k1, ..., and k_(n-1) can be * or
1621+-- kind variables.
1622+hasKindVarChain :: Int -> Type -> Maybe [Name]
1623+hasKindVarChain kindArrows t =
1624+ let uk = uncurryKind (tyKind t)
1625+ in if (NE.length uk - 1 == kindArrows) && F.all isStarOrVar uk
1626+ then Just (concatMap freeVariables uk)
1627+ else Nothing
1628+
1629+-- | If a Type is a SigT, returns its kind signature. Otherwise, return *.
1630+tyKind :: Type -> Kind
1631+tyKind (SigT _ k) = k
1632+tyKind _ = starK
1633+
1634+-- | Extract Just the Name from a type variable. If the argument Type is not a
1635+-- type variable, return Nothing.
1636+varTToNameMaybe :: Type -> Maybe Name
1637+varTToNameMaybe (VarT n) = Just n
1638+varTToNameMaybe (SigT t _) = varTToNameMaybe t
1639+varTToNameMaybe _ = Nothing
1640+
1405-- | Extract the Name from a type variable. If the argument Type is not a
1641-- | Extract the Name from a type variable. If the argument Type is not a
1406-- type variable, throw an error.
1642-- type variable, throw an error.
1407varTToName :: Type -> Name
1643varTToName :: Type -> Name
1408−varTToName (VarT n) = n
1409−varTToName (SigT t _) = varTToName t
1410−varTToName _ = error "Not a type variable!"
1644+varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe
1411−
1645+
1412−-- | Extracts the name from a constructor.
1646+interleave :: [a] -> [a] -> [a]
1413−getConName :: Con -> Name
1647+interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s
1414−getConName (NormalC name _) = name
1648+interleave _ _ = []
1415−getConName (RecC name _) = name
1416−getConName (InfixC _ name _) = name
1417−getConName (ForallC _ _ con) = getConName con
1418−#if MIN_VERSION_template_haskell(2,11,0)
1419−getConName (GadtC names _ _) = head names
1420−getConName (RecGadtC names _ _) = head names
1421−#endif
1422
1649
1423-- | Fully applies a type constructor to its type variables.
1650-- | Fully applies a type constructor to its type variables.
1424applyTyCon :: Name -> [Type] -> Type
1651applyTyCon :: Name -> [Type] -> Type
@@ -1430,24 +1657,122 @@ isTyVar (VarT _) = True
1430isTyVar (SigT t _) = isTyVar t
1657isTyVar (SigT t _) = isTyVar t
1431isTyVar _ = False
1658isTyVar _ = False
1432
1659
1660+-- | Is the given type a type family constructor (and not a data family constructor)?
1661+isTyFamily :: Type -> Q Bool
1662+isTyFamily (ConT n) = do
1663+ info <- reify n
1664+ return $ case info of
1665+#if MIN_VERSION_template_haskell(2,11,0)
1666+ FamilyI OpenTypeFamilyD{} _ -> True
1667+#else
1668+ FamilyI (FamilyD TypeFam _ _ _) _ -> True
1669+#endif
1670+ FamilyI ClosedTypeFamilyD{} _ -> True
1671+ _ -> False
1672+isTyFamily _ = return False
1673+
1433-- | Peel off a kind signature from a Type (if it has one).
1674-- | Peel off a kind signature from a Type (if it has one).
1434unSigT :: Type -> Type
1675unSigT :: Type -> Type
1435unSigT (SigT t _) = t
1676unSigT (SigT t _) = t
1436unSigT t = t
1677unSigT t = t
1437
1678
1679+-- | Are all of the items in a list (which have an ordering) distinct?
1680+--
1681+-- This uses Set (as opposed to nub) for better asymptotic time complexity.
1682+allDistinct :: Ord a => [a] -> Bool
1683+allDistinct = allDistinct' Set.empty
1684+ where
1685+ allDistinct' :: Ord a => Set a -> [a] -> Bool
1686+ allDistinct' uniqs (x:xs)
1687+ | x `Set.member` uniqs = False
1688+ | otherwise = allDistinct' (Set.insert x uniqs) xs
1689+ allDistinct' _ _ = True
1690+
1691+-- | Does the given type mention any of the Names in the list?
1692+mentionsName :: Type -> [Name] -> Bool
1693+mentionsName = go
1694+ where
1695+ go :: Type -> [Name] -> Bool
1696+ go (AppT t1 t2) names = go t1 names || go t2 names
1697+ go (SigT t _k) names = go t names
1698+ || go _k names
1699+ go (VarT n) names = n `elem` names
1700+ go _ _ = False
1701+
1702+-- | Does an instance predicate mention any of the Names in the list?
1703+predMentionsName :: Pred -> [Name] -> Bool
1704+#if MIN_VERSION_template_haskell(2,10,0)
1705+predMentionsName = mentionsName
1706+#else
1707+predMentionsName (ClassP n tys) names = n `elem` names || any (`mentionsName` names) tys
1708+predMentionsName (EqualP t1 t2) names = mentionsName t1 names || mentionsName t2 names
1709+#endif
1710+
1711+-- | Split an applied type into its individual components. For example, this:
1712+--
1713+-- @
1714+-- Either Int Char
1715+-- @
1716+--
1717+-- would split to this:
1718+--
1719+-- @
1720+-- [Either, Int, Char]
1721+-- @
1722+unapplyTy :: Type -> NonEmpty Type
1723+unapplyTy = NE.reverse . go
1724+ where
1725+ go :: Type -> NonEmpty Type
1726+ go (AppT t1 t2) = t2 <| go t1
1727+ go (SigT t _) = go t
1728+ go (ForallT _ _ t) = go t
1729+ go t = t :| []
1730+
1731+-- | Split a type signature by the arrows on its spine. For example, this:
1732+--
1733+-- @
1734+-- forall a b. (a ~ b) => (a -> b) -> Char -> ()
1735+-- @
1736+--
1737+-- would split to this:
1738+--
1739+-- @
1740+-- (a ~ b, [a -> b, Char, ()])
1741+-- @
1742+uncurryTy :: Type -> (Cxt, NonEmpty Type)
1743+uncurryTy (AppT (AppT ArrowT t1) t2) =
1744+ let (ctxt, tys) = uncurryTy t2
1745+ in (ctxt, t1 <| tys)
1746+uncurryTy (SigT t _) = uncurryTy t
1747+uncurryTy (ForallT _ ctxt t) =
1748+ let (ctxt', tys) = uncurryTy t
1749+ in (ctxt ++ ctxt', tys)
1750+uncurryTy t = ([], t :| [])
1751+
1752+-- | Like uncurryType, except on a kind level.
1753+uncurryKind :: Kind -> NonEmpty Kind
1754+uncurryKind = snd . uncurryTy
1755+
1756+createKindChain :: Int -> Kind
1757+createKindChain = go starK
1758+ where
1759+ go :: Kind -> Int -> Kind
1760+ go k 0 = k
1761+ go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1762+
1438-- | Makes a string literal expression from a constructor's name.
1763-- | Makes a string literal expression from a constructor's name.
1439−conNameExp :: Options -> Con -> Q Exp
1764+conNameExp :: Options -> ConstructorInfo -> Q Exp
1440conNameExp opts = litE
1765conNameExp opts = litE
1441 . stringL
1766 . stringL
1442 . constructorTagModifier opts
1767 . constructorTagModifier opts
1443 . nameBase
1768 . nameBase
1444− . getConName
1769+ . constructorName
1445
1770
1446−-- | Creates a string literal expression from a record field label.
1771+-- | Extracts a record field label.
1447−fieldLabelExp :: Options -- ^ Encoding options
1772+fieldLabel :: Options -- ^ Encoding options
1448− -> Name
1773+ -> Name
1449− -> Q Exp
1774+ -> String
1450−fieldLabelExp opts = litE . stringL . fieldLabelModifier opts . nameBase
1775+fieldLabel opts = fieldLabelModifier opts . nameBase
1451
1776
1452-- | The name of the outermost 'Value' constructor.
1777-- | The name of the outermost 'Value' constructor.
1453valueConName :: Value -> String
1778valueConName :: Value -> String
@@ -1466,63 +1791,201 @@ applyCon con t =
1466 ClassP con [VarT t]
1791 ClassP con [VarT t]
1467#endif
1792#endif
1468
1793
1794+-- | Checks to see if the last types in a data family instance can be safely eta-
1795+-- reduced (i.e., dropped), given the other types. This checks for three conditions:
1796+--
1797+-- (1) All of the dropped types are type variables
1798+-- (2) All of the dropped types are distinct
1799+-- (3) None of the remaining types mention any of the dropped types
1800+canEtaReduce :: [Type] -> [Type] -> Bool
1801+canEtaReduce remaining dropped =
1802+ all isTyVar dropped
1803+ && allDistinct droppedNames -- Make sure not to pass something of type [Type], since Type
1804+ -- didn't have an Ord instance until template-haskell-2.10.0.0
1805+ && not (any (`mentionsName` droppedNames) remaining)
1806+ where
1807+ droppedNames :: [Name]
1808+ droppedNames = map varTToName dropped
1809+
1469-------------------------------------------------------------------------------
1810-------------------------------------------------------------------------------
1470-- Expanding type synonyms
1811-- Expanding type synonyms
1471-------------------------------------------------------------------------------
1812-------------------------------------------------------------------------------
1472
1813
1473−-- | Expands all type synonyms in a type. Written by Dan Rosén in the
1814+applySubstitutionKind :: Map Name Kind -> Type -> Type
1474−-- @genifunctors@ package (licensed under BSD3).
1815+applySubstitutionKind = applySubstitution
1475−expandSyn :: Type -> Q Type
1476−expandSyn (ForallT tvs ctx t) = fmap (ForallT tvs ctx) $ expandSyn t
1477−expandSyn t@AppT{} = expandSynApp t []
1478−expandSyn t@ConT{} = expandSynApp t []
1479−expandSyn (SigT t k) = do t' <- expandSyn t
1480− k' <- expandSynKind k
1481− return (SigT t' k')
1482−expandSyn t = return t
1483−
1484−expandSynKind :: Kind -> Q Kind
1485−#if MIN_VERSION_template_haskell(2,8,0)
1486−expandSynKind = expandSyn
1487−#else
1488−expandSynKind = return -- There are no kind synonyms to deal with
1489−#endif
1490
1816
1491−expandSynApp :: Type -> [Type] -> Q Type
1817+substNameWithKind :: Name -> Kind -> Type -> Type
1492−expandSynApp (AppT t1 t2) ts = do
1818+substNameWithKind n k = applySubstitutionKind (M.singleton n k)
1493− t2' <- expandSyn t2
1819+
1494− expandSynApp t1 (t2':ts)
1820+substNamesWithKindStar :: [Name] -> Type -> Type
1495−expandSynApp (ConT n) ts | nameBase n == "[]" = return $ foldl' AppT ListT ts
1821+substNamesWithKindStar ns t = foldr' (`substNameWithKind` starK) t ns
1496−expandSynApp t@(ConT n) ts = do
1822+
1497− info <- reify n
1823+-------------------------------------------------------------------------------
1498− case info of
1824+-- Error messages
1499− TyConI (TySynD _ tvs rhs) ->
1825+-------------------------------------------------------------------------------
1500− let (ts', ts'') = splitAt (length tvs) ts
1826+
1501− subs = mkSubst tvs ts'
1827+-- | Either the given data type doesn't have enough type variables, or one of
1502− rhs' = substType subs rhs
1828+-- the type variables to be eta-reduced cannot realize kind *.
1503− in expandSynApp rhs' ts''
1829+derivingKindError :: JSONClass -> Name -> Q a
1504− _ -> return $ foldl' AppT t ts
1830+derivingKindError jc tyConName = fail
1505−expandSynApp t ts = do
1831+ . showString "Cannot derive well-kinded instance of form ‘"
1506− t' <- expandSyn t
1832+ . showString className
1507− return $ foldl' AppT t' ts
1833+ . showChar ' '
1508−
1834+ . showParen True
1509−type TypeSubst = Map Name Type
1835+ ( showString (nameBase tyConName)
1510−
1836+ . showString " ..."
1511−mkSubst :: [TyVarBndr] -> [Type] -> TypeSubst
1837+ )
1512−mkSubst vs ts =
1838+ . showString "‘\n\tClass "
1513− let vs' = map un vs
1839+ . showString className
1514− un (PlainTV v) = v
1840+ . showString " expects an argument of kind "
1515− un (KindedTV v _) = v
1841+ . showString (pprint . createKindChain $ arityInt jc)
1516− in M.fromList $ zip vs' ts
1842+ $ ""
1517−
1843+ where
1518−substType :: TypeSubst -> Type -> Type
1844+ className :: String
1519−substType subs (ForallT v c t) = ForallT v c $ substType subs t
1845+ className = nameBase $ jsonClassName jc
1520−substType subs t@(VarT n) = M.findWithDefault t n subs
1846+
1521−substType subs (AppT t1 t2) = AppT (substType subs t1) (substType subs t2)
1847+-- | One of the last type variables cannot be eta-reduced (see the canEtaReduce
1522−substType subs (SigT t k) = SigT (substType subs t)
1848+-- function for the criteria it would have to meet).
1523−#if MIN_VERSION_template_haskell(2,8,0)
1849+etaReductionError :: Type -> Q a
1524− (substType subs k)
1850+etaReductionError instanceType = fail $
1525−#else
1851+ "Cannot eta-reduce to an instance of form \n\tinstance (...) => "
1526− k
1852+ ++ pprint instanceType
1527−#endif
1853+
1528−substType _ t = t
1854+-- | The data type has a DatatypeContext which mentions one of the eta-reduced
1855+-- type variables.
1856+datatypeContextError :: Name -> Type -> Q a
1857+datatypeContextError dataName instanceType = fail
1858+ . showString "Can't make a derived instance of ‘"
1859+ . showString (pprint instanceType)
1860+ . showString "‘:\n\tData type ‘"
1861+ . showString (nameBase dataName)
1862+ . showString "‘ must not have a class context involving the last type argument(s)"
1863+ $ ""
1864+
1865+-- | The data type mentions one of the n eta-reduced type variables in a place other
1866+-- than the last nth positions of a data type in a constructor's field.
1867+outOfPlaceTyVarError :: JSONClass -> Name -> a
1868+outOfPlaceTyVarError jc conName = error
1869+ . showString "Constructor ‘"
1870+ . showString (nameBase conName)
1871+ . showString "‘ must only use its last "
1872+ . shows n
1873+ . showString " type variable(s) within the last "
1874+ . shows n
1875+ . showString " argument(s) of a data type"
1876+ $ ""
1877+ where
1878+ n :: Int
1879+ n = arityInt jc
1880+
1881+-- | The data type has an existential constraint which mentions one of the
1882+-- eta-reduced type variables.
1883+existentialContextError :: Name -> a
1884+existentialContextError conName = error
1885+ . showString "Constructor ‘"
1886+ . showString (nameBase conName)
1887+ . showString "‘ must be truly polymorphic in the last argument(s) of the data type"
1888+ $ ""
1889+
1890+-------------------------------------------------------------------------------
1891+-- Class-specific constants
1892+-------------------------------------------------------------------------------
1893+
1894+-- | A representation of the arity of the ToJSON/FromJSON typeclass being derived.
1895+data Arity = Arity0 | Arity1 | Arity2
1896+ deriving (Enum, Eq, Ord)
1897+
1898+-- | Whether ToJSON(1)(2) or FromJSON(1)(2) is being derived.
1899+data Direction = To | From
1900+
1901+-- | A representation of which typeclass method is being spliced in.
1902+data JSONFun = ToJSON | ToEncoding | ParseJSON
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+
1911+-- | A representation of which typeclass is being derived.
1912+data JSONClass = JSONClass { direction :: Direction, arity :: Arity }
1913+
1914+toJSONClass, toJSON1Class, toJSON2Class,
1915+ fromJSONClass, fromJSON1Class, fromJSON2Class :: JSONClass
1916+toJSONClass = JSONClass To Arity0
1917+toJSON1Class = JSONClass To Arity1
1918+toJSON2Class = JSONClass To Arity2
1919+fromJSONClass = JSONClass From Arity0
1920+fromJSON1Class = JSONClass From Arity1
1921+fromJSON2Class = JSONClass From Arity2
1922+
1923+jsonClassName :: JSONClass -> Name
1924+jsonClassName (JSONClass To Arity0) = ''ToJSON
1925+jsonClassName (JSONClass To Arity1) = ''ToJSON1
1926+jsonClassName (JSONClass To Arity2) = ''ToJSON2
1927+jsonClassName (JSONClass From Arity0) = ''FromJSON
1928+jsonClassName (JSONClass From Arity1) = ''FromJSON1
1929+jsonClassName (JSONClass From Arity2) = ''FromJSON2
1930+
1931+jsonFunValName :: JSONFun -> Arity -> Name
1932+jsonFunValName ToJSON Arity0 = 'toJSON
1933+jsonFunValName ToJSON Arity1 = 'liftToJSON
1934+jsonFunValName ToJSON Arity2 = 'liftToJSON2
1935+jsonFunValName ToEncoding Arity0 = 'toEncoding
1936+jsonFunValName ToEncoding Arity1 = 'liftToEncoding
1937+jsonFunValName ToEncoding Arity2 = 'liftToEncoding2
1938+jsonFunValName ParseJSON Arity0 = 'parseJSON
1939+jsonFunValName ParseJSON Arity1 = 'liftParseJSON
1940+jsonFunValName ParseJSON Arity2 = 'liftParseJSON2
1941+
1942+jsonFunListName :: JSONFun -> Arity -> Name
1943+jsonFunListName ToJSON Arity0 = 'toJSONList
1944+jsonFunListName ToJSON Arity1 = 'liftToJSONList
1945+jsonFunListName ToJSON Arity2 = 'liftToJSONList2
1946+jsonFunListName ToEncoding Arity0 = 'toEncodingList
1947+jsonFunListName ToEncoding Arity1 = 'liftToEncodingList
1948+jsonFunListName ToEncoding Arity2 = 'liftToEncodingList2
1949+jsonFunListName ParseJSON Arity0 = 'parseJSONList
1950+jsonFunListName ParseJSON Arity1 = 'liftParseJSONList
1951+jsonFunListName ParseJSON Arity2 = 'liftParseJSONList2
1952+
1953+jsonFunValOrListName :: Bool -- e.g., toJSONList if True, toJSON if False
1954+ -> JSONFun -> Arity -> Name
1955+jsonFunValOrListName False = jsonFunValName
1956+jsonFunValOrListName True = jsonFunListName
1957+
1958+arityInt :: JSONClass -> Int
1959+arityInt = fromEnum . arity
1960+
1961+allowExQuant :: JSONClass -> Bool
1962+allowExQuant (JSONClass To _) = True
1963+allowExQuant _ = False
1964+
1965+-------------------------------------------------------------------------------
1966+-- StarKindStatus
1967+-------------------------------------------------------------------------------
1968+
1969+-- | Whether a type is not of kind *, is of kind *, or is a kind variable.
1970+data StarKindStatus = NotKindStar
1971+ | KindStar
1972+ | IsKindVar Name
1973+ deriving Eq
1974+
1975+-- | Does a Type have kind * or k (for some kind variable k)?
1976+canRealizeKindStar :: Type -> StarKindStatus
1977+canRealizeKindStar t = case t of
1978+ _ | hasKindStar t -> KindStar
1979+ SigT _ (VarT k) -> IsKindVar k
1980+ _ -> NotKindStar
1981+
1982+-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
1983+-- Otherwise, returns 'Nothing'.
1984+starKindStatusToName :: StarKindStatus -> Maybe Name
1985+starKindStatusToName (IsKindVar n) = Just n
1986+starKindStatusToName _ = Nothing
1987+
1988+-- | Concat together all of the StarKindStatuses that are IsKindVar and extract
1989+-- the kind variables' Names out.
1990+catKindVarNames :: [StarKindStatus] -> [Name]
1991+catKindVarNames = mapMaybe starKindStatusToName
Data/Aeson/Text.hsnew file+102
@@ -0,0 +1,102 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+-- |
5+-- Module: Data.Aeson.Text
6+-- Copyright: (c) 2012-2016 Bryan O'Sullivan
7+-- (c) 2011 MailRank, Inc.
8+-- License: BSD3
9+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
10+-- Stability: experimental
11+-- Portability: portable
12+--
13+-- Most frequently, you'll probably want to encode straight to UTF-8
14+-- (the standard JSON encoding) using 'encode'.
15+--
16+-- You can use the conversions to 'Builder's when embedding JSON messages as
17+-- parts of a protocol.
18+
19+module Data.Aeson.Text
20+ (
21+ encodeToLazyText
22+ , encodeToTextBuilder
23+ ) where
24+
25+import Prelude.Compat
26+
27+import Data.Aeson.Types (Value(..), ToJSON(..))
28+import Data.Aeson.Encoding (encodingToLazyByteString)
29+import Data.Semigroup ((<>))
30+import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
31+import Data.Text.Lazy.Builder
32+import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
33+import Numeric (showHex)
34+import qualified Data.HashMap.Strict as H
35+import qualified Data.Text as T
36+import qualified Data.Text.Lazy as LT
37+import qualified Data.Text.Lazy.Encoding as LT
38+import qualified Data.Vector as V
39+
40+-- | Encode a JSON 'Value' to a "Data.Text.Lazy"
41+--
42+-- /Note:/ uses 'toEncoding'
43+encodeToLazyText :: ToJSON a => a -> LT.Text
44+encodeToLazyText = LT.decodeUtf8 . encodingToLazyByteString . toEncoding
45+
46+-- | Encode a JSON 'Value' to a "Data.Text" 'Builder', which can be
47+-- embedded efficiently in a text-based protocol.
48+--
49+-- If you are going to immediately encode straight to a
50+-- 'L.ByteString', it is more efficient to use 'encode' (lazy ByteString)
51+-- or @'fromEncoding' . 'toEncoding'@ (ByteString.Builder) instead.
52+--
53+-- /Note:/ Uses 'toJSON'
54+encodeToTextBuilder :: ToJSON a => a -> Builder
55+encodeToTextBuilder =
56+ go . toJSON
57+ where
58+ go Null = {-# SCC "go/Null" #-} "null"
59+ go (Bool b) = {-# SCC "go/Bool" #-} if b then "true" else "false"
60+ go (Number s) = {-# SCC "go/Number" #-} fromScientific s
61+ go (String s) = {-# SCC "go/String" #-} string s
62+ go (Array v)
63+ | V.null v = {-# SCC "go/Array" #-} "[]"
64+ | otherwise = {-# SCC "go/Array" #-}
65+ singleton '[' <>
66+ go (V.unsafeHead v) <>
67+ V.foldr f (singleton ']') (V.unsafeTail v)
68+ where f a z = singleton ',' <> go a <> z
69+ go (Object m) = {-# SCC "go/Object" #-}
70+ case H.toList m of
71+ (x:xs) -> singleton '{' <> one x <> foldr f (singleton '}') xs
72+ _ -> "{}"
73+ where f a z = singleton ',' <> one a <> z
74+ one (k,v) = string k <> singleton ':' <> go v
75+
76+string :: T.Text -> Builder
77+string s = {-# SCC "string" #-} singleton '"' <> quote s <> singleton '"'
78+ where
79+ quote q = case T.uncons t of
80+ Nothing -> fromText h
81+ Just (!c,t') -> fromText h <> escape c <> quote t'
82+ where (h,t) = {-# SCC "break" #-} T.break isEscape q
83+ isEscape c = c == '\"' ||
84+ c == '\\' ||
85+ c < '\x20'
86+ escape '\"' = "\\\""
87+ escape '\\' = "\\\\"
88+ escape '\n' = "\\n"
89+ escape '\r' = "\\r"
90+ escape '\t' = "\\t"
91+
92+ escape c
93+ | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
94+ | otherwise = singleton c
95+ where h = showHex (fromEnum c) ""
96+
97+fromScientific :: Scientific -> Builder
98+fromScientific s = formatScientificBuilder format prec s
99+ where
100+ (format, prec)
101+ | base10Exponent s < 0 = (Generic, Nothing)
102+ | otherwise = (Fixed, Just 0)
Data/Aeson/Types.hs+60−7
@@ -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,14 +38,26 @@ 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(..)
42 , ToJSONKeyFunction(..)
47 , ToJSONKeyFunction(..)
48+ , toJSONKeyText
49+ , contramapToJSONKeyFunction
43 , FromJSONKey(..)
50 , FromJSONKey(..)
44 , FromJSONKeyFunction(..)
51 , FromJSONKeyFunction(..)
45 , fromJSONKeyCoerce
52 , fromJSONKeyCoerce
46 , coerceFromJSONKeyFunction
53 , coerceFromJSONKeyFunction
54+ , mapFromJSONKeyFunction
55+
56+ -- *** Generic keys
57+ , GToJSONKey()
58+ , genericToJSONKey
59+ , GFromJSONKey()
60+ , genericFromJSONKey
47
61
48 -- ** Liftings to unary and binary type constructors
62 -- ** Liftings to unary and binary type constructors
49 , FromJSON1(..)
63 , FromJSON1(..)
@@ -59,19 +73,26 @@ module Data.Aeson.Types
59
73
60 -- ** Generic JSON classes
74 -- ** Generic JSON classes
61 , GFromJSON(..)
75 , GFromJSON(..)
76+ , FromArgs(..)
62− , GToJSON(..)
77+ , GToJSON
63− , GToEncoding(..)
78+ , GToEncoding
79+ , ToArgs(..)
80+ , Zero
81+ , One
64 , genericToJSON
82 , genericToJSON
83+ , genericLiftToJSON
65 , genericToEncoding
84 , genericToEncoding
85+ , genericLiftToEncoding
66 , genericParseJSON
86 , genericParseJSON
87+ , genericLiftParseJSON
67
88
68 -- * Inspecting @'Value's@
89 -- * Inspecting @'Value's@
69 , withObject
90 , withObject
70 , withText
91 , withText
71 , withArray
92 , withArray
72− , withNumber
73 , withScientific
93 , withScientific
74 , withBool
94 , withBool
95+ , withEmbeddedJSON
75
96
76 , pairs
97 , pairs
77 , foldable
98 , foldable
@@ -80,30 +101,62 @@ module Data.Aeson.Types
80 , (.:!)
101 , (.:!)
81 , (.!=)
102 , (.!=)
82 , object
103 , object
104+ , parseField
105+ , parseFieldMaybe
106+ , parseFieldMaybe'
107+ , explicitParseField
108+ , explicitParseFieldMaybe
109+ , explicitParseFieldMaybe'
83
110
84 , listEncoding
111 , listEncoding
85 , listValue
112 , listValue
86 , listParser
113 , listParser
87
114
88 -- * Generic and TH encoding configuration
115 -- * Generic and TH encoding configuration
89− , 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
90 , SumEncoding(..)
130 , SumEncoding(..)
91 , camelTo
131 , camelTo
92 , camelTo2
132 , camelTo2
93 , defaultOptions
133 , defaultOptions
94 , 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
95 ) where
147 ) where
96
148
97−import Prelude ()
98import Prelude.Compat
149import Prelude.Compat
99
150
151+import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
100import Data.Aeson.Types.Class
152import Data.Aeson.Types.Class
101import Data.Aeson.Types.Internal
153import Data.Aeson.Types.Internal
102−import Data.Aeson.Encoding (Encoding, unsafeToEncoding, fromEncoding, Series, pairs)
103−
104import Data.Foldable (toList)
154import Data.Foldable (toList)
105
155
106-- | Encode a 'Foldable' as a JSON array.
156-- | Encode a 'Foldable' as a JSON array.
107foldable :: (Foldable t, ToJSON a) => t a -> Encoding
157foldable :: (Foldable t, ToJSON a) => t a -> Encoding
108foldable = toEncoding . toList
158foldable = toEncoding . toList
109{-# 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+42−7
@@ -1,5 +1,11 @@
1−{-# LANGUAGE CPP, GADTs, DefaultSignatures, FlexibleInstances, FlexibleContexts, DeriveFunctor,
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE NoImplicitPrelude #-}
2− ScopedTypeVariables, TypeSynonymInstances #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
3
9
4-- |
10-- |
5-- Module: Data.Aeson.Types.Class
11-- Module: Data.Aeson.Types.Class
@@ -30,20 +36,33 @@ module Data.Aeson.Types.Class
30 , toEncoding2
36 , toEncoding2
31 -- * Generic JSON classes
37 -- * Generic JSON classes
32 , GFromJSON(..)
38 , GFromJSON(..)
39+ , FromArgs(..)
33− , GToJSON(..)
40+ , GToJSON
34− , GToEncoding(..)
41+ , GToEncoding
42+ , ToArgs(..)
43+ , Zero
44+ , One
35 , genericToJSON
45 , genericToJSON
46+ , genericLiftToJSON
36 , genericToEncoding
47 , genericToEncoding
48+ , genericLiftToEncoding
37 , genericParseJSON
49 , genericParseJSON
50+ , genericLiftParseJSON
38 -- * Classes and types for map keys
51 -- * Classes and types for map keys
39 , ToJSONKey(..)
52 , ToJSONKey(..)
40 , ToJSONKeyFunction(..)
53 , ToJSONKeyFunction(..)
54+ , toJSONKeyText
41 , contramapToJSONKeyFunction
55 , contramapToJSONKeyFunction
42 , FromJSONKey(..)
56 , FromJSONKey(..)
43 , FromJSONKeyFunction(..)
57 , FromJSONKeyFunction(..)
44 , fromJSONKeyCoerce
58 , fromJSONKeyCoerce
45 , coerceFromJSONKeyFunction
59 , coerceFromJSONKeyFunction
46 , mapFromJSONKeyFunction
60 , mapFromJSONKeyFunction
61+ -- ** Generic keys
62+ , GToJSONKey()
63+ , genericToJSONKey
64+ , GFromJSONKey()
65+ , genericFromJSONKey
47 -- * Object key-value pairs
66 -- * Object key-value pairs
48 , KeyValue(..)
67 , KeyValue(..)
49
68
@@ -56,19 +75,35 @@ module Data.Aeson.Types.Class
56 , withObject
75 , withObject
57 , withText
76 , withText
58 , withArray
77 , withArray
59− , withNumber
60 , withScientific
78 , withScientific
61 , withBool
79 , withBool
80+ , withEmbeddedJSON
62
81
63 -- * Functions
82 -- * Functions
64 , fromJSON
83 , fromJSON
65 , ifromJSON
84 , ifromJSON
85+ , typeMismatch
86+ , unexpected
87+ , parseField
88+ , parseFieldMaybe
89+ , parseFieldMaybe'
90+ , explicitParseField
91+ , explicitParseFieldMaybe
92+ , explicitParseFieldMaybe'
93+ -- ** Operators
66 , (.:)
94 , (.:)
67 , (.:?)
95 , (.:?)
68 , (.:!)
96 , (.:!)
69 , (.!=)
97 , (.!=)
70− , typeMismatch
71 ) where
98 ) where
72
99
100+
73import Data.Aeson.Types.FromJSON
101import Data.Aeson.Types.FromJSON
74−import Data.Aeson.Types.ToJSON
102+import Data.Aeson.Types.Generic (One, Zero)
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+1531−517
@@ -1,29 +1,28 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE DefaultSignatures #-}
4−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleContexts #-}
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
24{-# OPTIONS_GHC -fno-warn-deprecations #-}
22{-# OPTIONS_GHC -fno-warn-deprecations #-}
25
23
26−module Data.Aeson.Types.FromJSON (
24+module Data.Aeson.Types.FromJSON
25+ (
27 -- * Core JSON classes
26 -- * Core JSON classes
28 FromJSON(..)
27 FromJSON(..)
29 -- * Liftings to unary and binary type constructors
28 -- * Liftings to unary and binary type constructors
@@ -33,7 +32,9 @@ module Data.Aeson.Types.FromJSON (
33 , parseJSON2
32 , parseJSON2
34 -- * Generic JSON classes
33 -- * Generic JSON classes
35 , GFromJSON(..)
34 , GFromJSON(..)
35+ , FromArgs(..)
36 , genericParseJSON
36 , genericParseJSON
37+ , genericLiftParseJSON
37 -- * Classes and types for map keys
38 -- * Classes and types for map keys
38 , FromJSONKey(..)
39 , FromJSONKey(..)
39 , FromJSONKeyFunction(..)
40 , FromJSONKeyFunction(..)
@@ -41,98 +42,121 @@ module Data.Aeson.Types.FromJSON (
41 , coerceFromJSONKeyFunction
42 , coerceFromJSONKeyFunction
42 , mapFromJSONKeyFunction
43 , mapFromJSONKeyFunction
43
44
45+ , GFromJSONKey()
46+ , genericFromJSONKey
47+
44 -- * List functions
48 -- * List functions
45 , listParser
49 , listParser
46
50
47− -- * Inspecting @'Value's@
51+ -- * Inspecting @'Value's@
48 , withObject
52 , withObject
49 , withText
53 , withText
50 , withArray
54 , withArray
51− , withNumber
52 , withScientific
55 , withScientific
53 , withBool
56 , withBool
57+ , withEmbeddedJSON
54
58
55 -- * Functions
59 -- * Functions
56 , fromJSON
60 , fromJSON
57 , ifromJSON
61 , ifromJSON
62+ , typeMismatch
63+ , unexpected
64+ , parseField
65+ , parseFieldMaybe
66+ , parseFieldMaybe'
67+ , explicitParseField
68+ , explicitParseFieldMaybe
69+ , explicitParseFieldMaybe'
70+ , parseIndexedJSON
71+ -- ** Operators
58 , (.:)
72 , (.:)
59 , (.:?)
73 , (.:?)
60 , (.:!)
74 , (.:!)
61 , (.!=)
75 , (.!=)
62− , typeMismatch
76+
77+ -- * Internal
78+ , parseOptionalFieldWith
63 ) where
79 ) where
64
80
65−import Prelude ()
81+import Prelude.Compat
66−import Prelude.Compat hiding (foldr)
67
82
83+import Control.Applicative ((<|>), Const(..), liftA2)
84+import Control.Monad (zipWithM)
68−import Data.Aeson.Functions (mapKey)
85+import Data.Aeson.Internal.Functions (mapKey)
86+import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
69import Data.Aeson.Types.Generic
87import Data.Aeson.Types.Generic
70import Data.Aeson.Types.Internal
88import Data.Aeson.Types.Internal
71−
72−import qualified Data.Aeson.Parser.Time as Time
73−
74−import Control.Applicative (Const (..), (<|>))
75−import Control.Monad ((<=<))
76−import Data.Attoparsec.Number (Number (..))
77−import Data.Bits (unsafeShiftR)
89+import Data.Bits (unsafeShiftR)
78−import Data.Fixed (Fixed, HasResolution)
90+import Data.Fixed (Fixed, HasResolution (resolution), Nano)
91+import Data.Functor.Compose (Compose(..))
79−import Data.Functor.Identity (Identity (..))
92+import Data.Functor.Identity (Identity(..))
93+import Data.Functor.Product (Product(..))
80−import Data.Hashable (Hashable (..))
94+import Data.Functor.Sum (Sum(..))
95+import Data.Hashable (Hashable(..))
81−import Data.Int (Int16, Int32, Int64, Int8)
96+import Data.Int (Int16, Int32, Int64, Int8)
82−import Data.List.NonEmpty (NonEmpty (..))
97+import Data.List.NonEmpty (NonEmpty(..))
83−import Data.Maybe (fromMaybe)
98+import Data.Maybe (fromMaybe)
84−import Data.Monoid (Dual (..), First (..), Last (..), (<>))
99+import Data.Semigroup ((<>))
85−import Data.Proxy (Proxy (..))
100+import Data.Proxy (Proxy(..))
86−import Data.Ratio (Ratio, (%))
101+import Data.Ratio ((%), Ratio)
87−import Data.Scientific (Scientific)
102+import Data.Scientific (Scientific, base10Exponent)
88−import Data.Tagged (Tagged (..))
103+import Data.Tagged (Tagged(..))
89−import Data.Text (Text, pack, unpack)
104+import Data.Text (Text, pack, unpack)
90−import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay,
105+import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
91− UTCTime, ZonedTime)
106+import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
92−import Data.Time.Format (parseTime)
107+import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
108+import Data.Time.Clock.System.Compat (SystemTime (..))
93−import Data.Time.Locale.Compat (defaultTimeLocale)
109+import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
94−import Data.Traversable as Tr (sequence)
110+import Data.Traversable as Tr (sequence)
95−import Data.Vector (Vector)
111+import Data.Vector (Vector)
96−import Data.Version (Version, parseVersion)
112+import Data.Version (Version, parseVersion)
113+import Data.Void (Void)
97−import Data.Word (Word16, Word32, Word64, Word8)
114+import Data.Word (Word16, Word32, Word64, Word8)
98−import Foreign.Storable (Storable)
115+import Foreign.Storable (Storable)
116+import Foreign.C.Types (CTime (..))
99import GHC.Generics
117import GHC.Generics
100−import Numeric.Natural (Natural)
118+import Numeric.Natural (Natural)
101import Text.ParserCombinators.ReadP (readP_to_S)
119import Text.ParserCombinators.ReadP (readP_to_S)
102−
120+import Unsafe.Coerce (unsafeCoerce)
121+import qualified Data.Aeson.Parser.Time as Time
122+import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123+import qualified Data.ByteString.Lazy as L
124+import qualified Data.DList as DList
103−import qualified Data.HashMap.Strict as H
125+import qualified Data.HashMap.Strict as H
104−import qualified Data.HashSet as HashSet
126+import qualified Data.HashSet as HashSet
105−import qualified Data.IntMap as IntMap
127+import qualified Data.IntMap as IntMap
106−import qualified Data.IntSet as IntSet
128+import qualified Data.IntSet as IntSet
107−import qualified Data.Map as M
129+import qualified Data.Map as M
130+import qualified Data.Monoid as Monoid
108−import qualified Data.Scientific as Scientific
131+import qualified Data.Scientific as Scientific
109−import qualified Data.Sequence as Seq
132+import qualified Data.Semigroup as Semigroup
133+import qualified Data.Sequence as Seq
110−import qualified Data.Set as Set
134+import qualified Data.Set as Set
111−import qualified Data.Text as T
135+import qualified Data.Text as T
136+import qualified Data.Text.Encoding as T
112−import qualified Data.Text.Lazy as LT
137+import qualified Data.Text.Lazy as LT
113−import qualified Data.Text.Read as TR
138+import qualified Data.Tree as Tree
114−import qualified Data.Tree as Tree
139+import qualified Data.UUID.Types as UUID
115−import qualified Data.Vector as V
140+import qualified Data.Vector as V
116−import qualified Data.Vector.Generic as VG
141+import qualified Data.Vector.Generic as VG
117import qualified Data.Vector.Primitive as VP
142import qualified Data.Vector.Primitive as VP
118−import qualified Data.Vector.Storable as VS
143+import qualified Data.Vector.Storable as VS
119−import qualified Data.Vector.Unboxed as VU
144+import qualified Data.Vector.Unboxed as VU
120
145
121−import Unsafe.Coerce (unsafeCoerce)
146+import qualified GHC.Exts as Exts
147+import qualified Data.Primitive.Array as PM
148+import qualified Data.Primitive.SmallArray as PM
149+import qualified Data.Primitive.Types as PM
122
150
123−-- Coercible derivations aren't as powerful on GHC 7.8, though supported.
151+#if MIN_VERSION_primitive(0,6,4)
124−#ifndef HAS_COERCIBLE
152+#if !MIN_VERSION_primitive(0,7,0)
125−#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 709)
153+import qualified Data.Primitive.UnliftedArray as PM
154+#endif
155+import qualified Data.Primitive.PrimArray as PM
126#endif
156#endif
127
157
128−#if HAS_COERCIBLE
158+
129import Data.Coerce (Coercible, coerce)
159import Data.Coerce (Coercible, coerce)
130−coerce' :: Coercible a b => a -> b
131−coerce' = coerce
132−#else
133−coerce' :: a -> b
134−coerce' = unsafeCoerce
135−#endif
136
160
137parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
161parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
138parseIndexedJSON p idx value = p value <?> Index idx
162parseIndexedJSON p idx value = p value <?> Index idx
@@ -141,7 +165,7 @@ parseIndexedJSON p idx value = p value <?> Index idx
141parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
165parseIndexedJSONPair :: (Value -> Parser a) -> (Value -> Parser b) -> Int -> Value -> Parser (a, b)
142parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
166parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
143 where
167 where
144− p = withArray "(k,v)" $ \ab ->
168+ p = withArray "(k, v)" $ \ab ->
145 let n = V.length ab
169 let n = V.length ab
146 in if n == 2
170 in if n == 2
147 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
171 then (,) <$> parseJSONElemAtIndex keyParser 0 ab
@@ -153,40 +177,98 @@ parseIndexedJSONPair keyParser valParser idx value = p value <?> Index idx
153parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
177parseJSONElemAtIndex :: (Value -> Parser a) -> Int -> V.Vector Value -> Parser a
154parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
178parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) <?> Index idx
155
179
156−scientificToNumber :: Scientific -> Number
157−scientificToNumber s
158− | e < 0 = D $ Scientific.toRealFloat s
159− | otherwise = I $ c * 10 ^ e
160− where
161− e = Scientific.base10Exponent s
162− c = Scientific.coefficient s
163−{-# INLINE scientificToNumber #-}
164−
165parseRealFloat :: RealFloat a => String -> Value -> Parser a
180parseRealFloat :: RealFloat a => String -> Value -> Parser a
166−parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
181+parseRealFloat _ (Number s) = pure $ Scientific.toRealFloat s
167−parseRealFloat _ Null = pure (0/0)
182+parseRealFloat _ Null = pure (0/0)
168−parseRealFloat expected v = typeMismatch expected v
183+parseRealFloat name v = prependContext name (unexpected v)
169{-# INLINE parseRealFloat #-}
184{-# INLINE parseRealFloat #-}
170
185
186+parseIntegralFromScientific :: forall a. Integral a => Scientific -> Parser a
187+parseIntegralFromScientific s =
188+ case Scientific.floatingOrInteger s :: Either Double a of
189+ Right x -> pure x
190+ Left _ -> fail $ "unexpected floating number " ++ show s
191+{-# INLINE parseIntegralFromScientific #-}
192+
171parseIntegral :: Integral a => String -> Value -> Parser a
193parseIntegral :: Integral a => String -> Value -> Parser a
172−parseIntegral expected = withScientific expected $ pure . truncate
194+parseIntegral name =
195+ prependContext name . withBoundedScientific' parseIntegralFromScientific
173{-# INLINE parseIntegral #-}
196{-# INLINE parseIntegral #-}
174
197
198+parseBoundedIntegralFromScientific :: (Bounded a, Integral a) => Scientific -> Parser a
199+parseBoundedIntegralFromScientific s = maybe
200+ (fail $ "value is either floating or will cause over or underflow " ++ show s)
201+ pure
202+ (Scientific.toBoundedInteger s)
203+{-# INLINE parseBoundedIntegralFromScientific #-}
204+
205+parseBoundedIntegral :: (Bounded a, Integral a) => String -> Value -> Parser a
206+parseBoundedIntegral name =
207+ prependContext name . withScientific' parseBoundedIntegralFromScientific
208+{-# INLINE parseBoundedIntegral #-}
209+
210+parseScientificText :: Text -> Parser Scientific
211+parseScientificText
212+ = either fail pure
213+ . A.parseOnly (A.scientific <* A.endOfInput)
214+ . T.encodeUtf8
215+
216+parseIntegralText :: Integral a => String -> Text -> Parser a
217+parseIntegralText name t =
218+ prependContext name $
219+ parseScientificText t
220+ >>= rejectLargeExponent
221+ >>= parseIntegralFromScientific
222+ where
223+ rejectLargeExponent :: Scientific -> Parser Scientific
224+ rejectLargeExponent s = withBoundedScientific' pure (Number s)
225+{-# INLINE parseIntegralText #-}
226+
227+parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a
228+parseBoundedIntegralText name t =
229+ prependContext name $
230+ parseScientificText t >>= parseBoundedIntegralFromScientific
231+
232+parseOptionalFieldWith :: (Value -> Parser (Maybe a))
233+ -> Object -> Text -> Parser (Maybe a)
234+parseOptionalFieldWith pj obj key =
235+ case H.lookup key obj of
236+ Nothing -> pure Nothing
237+ Just v -> pj v <?> Key key
238+{-# INLINE parseOptionalFieldWith #-}
239+
175-------------------------------------------------------------------------------
240-------------------------------------------------------------------------------
176-- Generics
241-- Generics
177-------------------------------------------------------------------------------
242-------------------------------------------------------------------------------
178
243
179−-- | Class of generic representation types ('Rep') that can be converted from JSON.
244+-- | Class of generic representation types that can be converted from JSON.
180−class GFromJSON f where
245+class GFromJSON arity f where
181 -- | This method (applied to 'defaultOptions') is used as the
246 -- | This method (applied to 'defaultOptions') is used as the
182− -- default generic implementation of 'parseJSON'.
247+ -- default generic implementation of 'parseJSON' (if the @arity@ is 'Zero')
248+ -- or 'liftParseJSON' (if the @arity@ is 'One').
183− gParseJSON :: Options -> Value -> Parser (f a)
249+ gParseJSON :: Options -> FromArgs arity a -> Value -> Parser (f a)
250+
251+-- | A 'FromArgs' value either stores nothing (for 'FromJSON') or it stores the
252+-- two function arguments that decode occurrences of the type parameter (for
253+-- 'FromJSON1').
254+data FromArgs arity a where
255+ NoFromArgs :: FromArgs Zero a
256+ From1Args :: (Value -> Parser a) -> (Value -> Parser [a]) -> FromArgs One a
184
257
185-- | A configurable generic JSON decoder. This function applied to
258-- | A configurable generic JSON decoder. This function applied to
186-- 'defaultOptions' is used as the default for 'parseJSON' when the
259-- 'defaultOptions' is used as the default for 'parseJSON' when the
187-- type is an instance of 'Generic'.
260-- type is an instance of 'Generic'.
188−genericParseJSON :: (Generic a, GFromJSON (Rep a)) => Options -> Value -> Parser a
261+genericParseJSON :: (Generic a, GFromJSON Zero (Rep a))
262+ => Options -> Value -> Parser a
189−genericParseJSON opts = fmap to . gParseJSON opts
263+genericParseJSON opts = fmap to . gParseJSON opts NoFromArgs
264+
265+-- | A configurable generic JSON decoder. This function applied to
266+-- 'defaultOptions' is used as the default for 'liftParseJSON' when the
267+-- type is an instance of 'Generic1'.
268+genericLiftParseJSON :: (Generic1 f, GFromJSON One (Rep1 f))
269+ => Options -> (Value -> Parser a) -> (Value -> Parser [a])
270+ -> Value -> Parser (f a)
271+genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl)
190
272
191-------------------------------------------------------------------------------
273-------------------------------------------------------------------------------
192-- Class
274-- Class
@@ -205,14 +287,21 @@ genericParseJSON opts = fmap to . gParseJSON opts
205--
287--
206-- The basic ways to signal a failed conversion are as follows:
288-- The basic ways to signal a failed conversion are as follows:
207--
289--
208−-- * 'empty' and 'mzero' work, but are terse and uninformative
290+-- * 'fail' yields a custom error message: it is the recommended way of
291+-- reporting a failure;
209--
292--
210−-- * 'fail' yields a custom error message
293+-- * 'Control.Applicative.empty' (or 'Control.Monad.mzero') is uninformative:
294+-- use it when the error is meant to be caught by some @('<|>')@;
211--
295--
212−-- * 'typeMismatch' produces an informative message for cases when the
296+-- * 'typeMismatch' can be used to report a failure when the encountered value
213−-- value encountered is not of the expected type
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.
214--
300--
301+-- 'prependFailure' (or 'modifyFailure') add more information to a parser's
302+-- error messages.
303+--
215−-- An example type and instance:
304+-- An example type and instance using 'typeMismatch' and 'prependFailure':
216--
305--
217-- @
306-- @
218-- \-- Allow ourselves to write 'Text' literals.
307-- \-- Allow ourselves to write 'Text' literals.
@@ -220,15 +309,30 @@ genericParseJSON opts = fmap to . gParseJSON opts
220--
309--
221-- data Coord = Coord { x :: Double, y :: Double }
310-- data Coord = Coord { x :: Double, y :: Double }
222--
311--
223−-- instance FromJSON Coord where
312+-- instance 'FromJSON' Coord where
224−-- parseJSON ('Object' v) = Coord '<$>'
313+-- 'parseJSON' ('Object' v) = Coord
225−-- v '.:' \"x\" '<*>'
314+-- '<$>' v '.:' \"x\"
226−-- v '.:' \"y\"
315+-- '<*>' v '.:' \"y\"
316+--
317+-- \-- We do not expect a non-'Object' value here.
318+-- \-- We could use 'Control.Applicative.empty' to fail, but 'typeMismatch'
319+-- \-- gives a much more informative error message.
320+-- 'parseJSON' invalid =
321+-- 'prependFailure' "parsing Coord failed, "
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:
227--
330--
228−-- \-- We do not expect a non-'Object' value here.
331+-- @
229−-- \-- We could use 'mzero' to fail, but 'typeMismatch'
332+-- instance 'FromJSON' Coord where
230−-- \-- gives a much more informative error message.
231−-- parseJSON invalid = 'typeMismatch' \"Coord\" invalid
333+-- 'parseJSON' = 'withObject' \"Coord\" $ \\v -> Coord
334+-- '<$>' v '.:' \"x\"
335+-- '<*>' v '.:' \"y\"
232-- @
336-- @
233--
337--
234-- Instead of manually writing your 'FromJSON' instance, there are two options
338-- Instead of manually writing your 'FromJSON' instance, there are two options
@@ -236,7 +340,7 @@ genericParseJSON opts = fmap to . gParseJSON opts
236--
340--
237-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
341-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
238-- 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
239−-- so will probably be more efficient than the following two options:
343+-- so it will probably be more efficient than the following option.
240--
344--
241-- * The compiler can provide a default generic implementation for
345-- * The compiler can provide a default generic implementation for
242-- 'parseJSON'.
346-- 'parseJSON'.
@@ -254,139 +358,255 @@ genericParseJSON opts = fmap to . gParseJSON opts
254--
358--
255-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
359-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
256--
360--
257−-- instance FromJSON Coord
361+-- instance 'FromJSON' Coord
258-- @
362-- @
259--
363--
260−-- 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
261−-- you can customize the generic decoding with only a tiny amount of
366+-- options, you can customize the generic decoding by defining:
262−-- effort, using 'genericParseJSON' with your preferred 'Options':
263--
367--
264-- @
368-- @
369+-- customOptions = 'defaultOptions'
370+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
371+-- }
372+--
265−-- instance FromJSON Coord where
373+-- instance 'FromJSON' Coord where
266−-- parseJSON = 'genericParseJSON' 'defaultOptions'
374+-- 'parseJSON' = 'genericParseJSON' customOptions
267-- @
375-- @
268−
269class FromJSON a where
376class FromJSON a where
270 parseJSON :: Value -> Parser a
377 parseJSON :: Value -> Parser a
271
378
272− default parseJSON :: (Generic a, GFromJSON (Rep a)) => Value -> Parser a
379+ default parseJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
273 parseJSON = genericParseJSON defaultOptions
380 parseJSON = genericParseJSON defaultOptions
274
381
275 parseJSONList :: Value -> Parser [a]
382 parseJSONList :: Value -> Parser [a]
276− parseJSONList (Array a)
383+ parseJSONList = withArray "[]" $ \a ->
277− = sequence
278− . zipWith (parseIndexedJSON parseJSON) [0..]
384+ zipWithM (parseIndexedJSON parseJSON) [0..]
279 . V.toList
385 . V.toList
280 $ a
386 $ a
281
387
282− parseJSONList v = typeMismatch "[a]" v
283−
284−
285-------------------------------------------------------------------------------
388-------------------------------------------------------------------------------
286-- Classes and types for map keys
389-- Classes and types for map keys
287-------------------------------------------------------------------------------
390-------------------------------------------------------------------------------
288
391
392+-- | Read the docs for 'ToJSONKey' first. This class is a conversion
393+-- in the opposite direction. If you have a newtype wrapper around 'Text',
394+-- the recommended way to define instances is with generalized newtype deriving:
395+--
396+-- > newtype SomeId = SomeId { getSomeId :: Text }
397+-- > deriving (Eq,Ord,Hashable,FromJSONKey)
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+-- @
289class FromJSONKey a where
409class FromJSONKey a where
410+ -- | Strategy for parsing the key of a map-like container.
290 fromJSONKey :: FromJSONKeyFunction a
411 fromJSONKey :: FromJSONKeyFunction a
291 default fromJSONKey :: FromJSON a => FromJSONKeyFunction a
412 default fromJSONKey :: FromJSON a => FromJSONKeyFunction a
292 fromJSONKey = FromJSONKeyValue parseJSON
413 fromJSONKey = FromJSONKeyValue parseJSON
414+
415+ -- | This is similar in spirit to the 'readList' method of 'Read'.
416+ -- It makes it possible to give 'String' keys special treatment
417+ -- without using @OverlappingInstances@. End users should always
418+ -- be able to use the default implementation of this method.
293 fromJSONKeyList :: FromJSONKeyFunction [a]
419 fromJSONKeyList :: FromJSONKeyFunction [a]
294 default fromJSONKeyList :: FromJSON a => FromJSONKeyFunction [a]
420 default fromJSONKeyList :: FromJSON a => FromJSONKeyFunction [a]
295 fromJSONKeyList = FromJSONKeyValue parseJSON
421 fromJSONKeyList = FromJSONKeyValue parseJSON
296
422
297−-- | With GHC 7.8 + we carry around 'Coercible Text a' dictionary,
423+-- | With GHC 7.8+ we carry around @'Coercible' 'Text' a@ dictionary,
298−-- to have even some amount of safety net.
424+-- to give us an assurance that the program will not segfault.
299−-- Unfortunately we cannot enforce that 'Hashable' instance agree on the type level
425+-- Unfortunately we cannot enforce that the 'Eq' instances or the
426+-- 'Hashable' instances for 'Text' and @a@ agree.
300--
427--
301−-- ATM this type is intentionally not exported. FromJSONKeyFunction can be inspected,
428+-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
302−-- but cannot be constructed.
429+-- can be inspected, but cannot be constructed.
303data CoerceText a where
430data CoerceText a where
304−#if HAS_COERCIBLE
305 CoerceText :: Coercible Text a => CoerceText a
431 CoerceText :: Coercible Text a => CoerceText a
306−#else
307− CoerceText :: CoerceText a
308−#endif
309
432
433+-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
434+-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
435+-- instance. The other three data constructors for this type all correspond to
436+-- 'ToJSONKeyText'. Strictly speaking, 'FromJSONKeyTextParser' is more powerful than
437+-- 'FromJSONKeyText', which is in turn more powerful than 'FromJSONKeyCoerce'.
438+-- For performance reasons, these exist as three options instead of one.
310data FromJSONKeyFunction a
439data FromJSONKeyFunction a
311− = FromJSONKeyCoerce (CoerceText a)
440+ = FromJSONKeyCoerce !(CoerceText a)
441+ -- ^ uses 'coerce' ('unsafeCoerce' in older GHCs)
312− | FromJSONKeyText (Text -> a)
442+ | FromJSONKeyText !(Text -> a)
443+ -- ^ conversion from 'Text' that always succeeds
313− | FromJSONKeyTextParser (Text -> Parser a)
444+ | FromJSONKeyTextParser !(Text -> Parser a)
445+ -- ^ conversion from 'Text' that may fail
314− | FromJSONKeyValue (Value -> Parser a)
446+ | FromJSONKeyValue !(Value -> Parser a)
315−
447+ -- ^ conversion for non-textual keys
448+
449+-- | Only law abiding up to interpretation
316instance Functor FromJSONKeyFunction where
450instance Functor FromJSONKeyFunction where
317− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
451+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
318 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
452 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
319 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
453 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
320 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
454 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
321
455
322-- | Construct 'FromJSONKeyFunction' for types coercible from 'Text'. This
456-- | Construct 'FromJSONKeyFunction' for types coercible from 'Text'. This
323-- conversion is still unsafe, as 'Hashable' and 'Eq' instances of @a@ should be
457-- conversion is still unsafe, as 'Hashable' and 'Eq' instances of @a@ should be
458+-- compatible with 'Text' i.e. hash values should be equal for wrapped values as well.
459+-- This property will always be maintained if the 'Hashable' and 'Eq' instances
460+-- are derived with generalized newtype deriving.
324-- compatible with 'Text' i.e. hash values be equal for wrapped values as well.
461-- compatible with 'Text' i.e. hash values be equal for wrapped values as well.
462+--
463+-- On pre GHC 7.8 this is unconstrainted function.
325fromJSONKeyCoerce ::
464fromJSONKeyCoerce ::
326−#if HAS_COERCIBLE
327 Coercible Text a =>
465 Coercible Text a =>
328−#endif
329 FromJSONKeyFunction a
466 FromJSONKeyFunction a
330fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
467fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
331
468
469+-- | Semantically the same as @coerceFromJSONKeyFunction = fmap coerce = coerce@.
470+--
471+-- See note on 'fromJSONKeyCoerce'.
332coerceFromJSONKeyFunction ::
472coerceFromJSONKeyFunction ::
333−#if HAS_COERCIBLE
334 Coercible a b =>
473 Coercible a b =>
335−#endif
336 FromJSONKeyFunction a -> FromJSONKeyFunction b
474 FromJSONKeyFunction a -> FromJSONKeyFunction b
337−coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
475+coerceFromJSONKeyFunction = coerce
338−coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
339−coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
340−coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
341
476
342{-# RULES
477{-# RULES
343 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
478 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
344 fmap id x = x
479 fmap id x = x
345 #-}
480 #-}
346−#if HAS_COERCIBLE
347{-# RULES
481{-# RULES
348 "FromJSONKeyCoerce: fmap coerce" forall x .
482 "FromJSONKeyCoerce: fmap coerce" forall x .
349 fmap coerce x = coerceFromJSONKeyFunction x
483 fmap coerce x = coerceFromJSONKeyFunction x
350 #-}
484 #-}
351−#endif
352
485
486+-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
353mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
487mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
354mapFromJSONKeyFunction = fmap
488mapFromJSONKeyFunction = fmap
355
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+
356-------------------------------------------------------------------------------
519-------------------------------------------------------------------------------
357-- Functions needed for documentation
520-- Functions needed for documentation
358-------------------------------------------------------------------------------
521-------------------------------------------------------------------------------
359
522
360-- | Fail parsing due to a type mismatch, with a descriptive message.
523-- | Fail parsing due to a type mismatch, with a descriptive message.
361--
524--
362−-- Example usage:
525+-- The following wrappers should generally be prefered:
526+-- 'withObject', 'withArray', 'withText', 'withBool'.
363--
527--
364−-- @
528+-- ==== Error message example
365−-- instance FromJSON Coord where
529+--
366−-- parseJSON ('Object' v) = {- type matches, life is good -}
530+-- > typeMismatch "Object" (String "oops")
367−-- parseJSON wat = 'typeMismatch' \"Coord\" wat
531+-- > -- Error: "expected Object, but encountered String"
368−-- @
369−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\"@).
370 -> Value -- ^ The actual value encountered.
535 -> Value -- ^ The actual value encountered.
371 -> Parser a
536 -> Parser a
372typeMismatch expected actual =
537typeMismatch expected actual =
373− fail $ "expected " ++ expected ++ ", encountered " ++ name
538+ fail $ "expected " ++ expected ++ ", but encountered " ++ typeOf actual
374− 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
375− name = case actual of
551+typeOf v = case v of
376− Object _ -> "Object"
552+ Object _ -> "Object"
377− Array _ -> "Array"
553+ Array _ -> "Array"
378− String _ -> "String"
554+ String _ -> "String"
379− Number _ -> "Number"
555+ Number _ -> "Number"
380− Bool _ -> "Boolean"
556+ Bool _ -> "Boolean"
381− Null -> "Null"
557+ Null -> "Null"
382
558
383-------------------------------------------------------------------------------
559-------------------------------------------------------------------------------
384-- Lifings of FromJSON and ToJSON to unary and binary type constructors
560-- Lifings of FromJSON and ToJSON to unary and binary type constructors
385-------------------------------------------------------------------------------
561-------------------------------------------------------------------------------
386
562
387-- | Lifting of the 'FromJSON' class to unary type constructors.
563-- | Lifting of the 'FromJSON' class to unary type constructors.
564+--
565+-- Instead of manually writing your 'FromJSON1' instance, there are two options
566+-- to do it automatically:
567+--
568+-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
569+-- instance at compile time. The generated instance is optimized for your type
570+-- so it will probably be more efficient than the following option.
571+--
572+-- * The compiler can provide a default generic implementation for
573+-- 'liftParseJSON'.
574+--
575+-- To use the second, simply add a @deriving 'Generic1'@ clause to your
576+-- datatype and declare a 'FromJSON1' instance for your datatype without giving
577+-- a definition for 'liftParseJSON'.
578+--
579+-- For example:
580+--
581+-- @
582+-- {-\# LANGUAGE DeriveGeneric \#-}
583+--
584+-- import "GHC.Generics"
585+--
586+-- data Pair a b = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
587+--
588+-- instance 'FromJSON' a => 'FromJSON1' (Pair a)
589+-- @
590+--
591+-- If the default implementation doesn't give exactly the results you want,
592+-- you can customize the generic decoding with only a tiny amount of
593+-- effort, using 'genericLiftParseJSON' with your preferred 'Options':
594+--
595+-- @
596+-- customOptions = 'defaultOptions'
597+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
598+-- }
599+--
600+-- instance 'FromJSON' a => 'FromJSON1' (Pair a) where
601+-- 'liftParseJSON' = 'genericLiftParseJSON' customOptions
602+-- @
388class FromJSON1 f where
603class FromJSON1 f where
389 liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
604 liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
605+
606+ default liftParseJSON :: (Generic1 f, GFromJSON One (Rep1 f))
607+ => (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
608+ liftParseJSON = genericLiftParseJSON defaultOptions
609+
390 liftParseJSONList :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser [f a]
610 liftParseJSONList :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser [f a]
391 liftParseJSONList f g v = listParser (liftParseJSON f g) v
611 liftParseJSONList f g v = listParser (liftParseJSON f g) v
392
612
@@ -395,8 +615,13 @@ parseJSON1 :: (FromJSON1 f, FromJSON a) => Value -> Parser (f a)
395parseJSON1 = liftParseJSON parseJSON parseJSONList
615parseJSON1 = liftParseJSON parseJSON parseJSONList
396{-# INLINE parseJSON1 #-}
616{-# INLINE parseJSON1 #-}
397
617
398−
399-- | Lifting of the 'FromJSON' class to binary type constructors.
618-- | Lifting of the 'FromJSON' class to binary type constructors.
619+--
620+-- Instead of manually writing your 'FromJSON2' instance, "Data.Aeson.TH"
621+-- provides Template Haskell functions which will derive an instance at compile time.
622+
623+-- The compiler cannot provide a default generic implementation for 'liftParseJSON2',
624+-- unlike 'parseJSON' and 'liftParseJSON'.
400class FromJSON2 f where
625class FromJSON2 f where
401 liftParseJSON2
626 liftParseJSON2
402 :: (Value -> Parser a)
627 :: (Value -> Parser a)
@@ -410,9 +635,8 @@ class FromJSON2 f where
410 -> (Value -> Parser b)
635 -> (Value -> Parser b)
411 -> (Value -> Parser [b])
636 -> (Value -> Parser [b])
412 -> Value -> Parser [f a b]
637 -> Value -> Parser [f a b]
413− liftParseJSONList2 fa ga fb gb v = case v of
638+ liftParseJSONList2 fa ga fb gb = withArray "[]" $ \vals ->
414− 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)
415− _ -> typeMismatch "[a]" v
416
640
417-- | Lift the standard 'parseJSON' function through the type constructor.
641-- | Lift the standard 'parseJSON' function through the type constructor.
418parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
642parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b)
@@ -423,9 +647,10 @@ parseJSON2 = liftParseJSON2 parseJSON parseJSONList parseJSON parseJSONList
423-- List functions
647-- List functions
424-------------------------------------------------------------------------------
648-------------------------------------------------------------------------------
425
649
650+-- | Helper function to use with 'liftParseJSON'. See 'Data.Aeson.ToJSON.listEncoding'.
426listParser :: (Value -> Parser a) -> Value -> Parser [a]
651listParser :: (Value -> Parser a) -> Value -> Parser [a]
427listParser f (Array xs) = fmap V.toList (V.mapM f xs)
652listParser f (Array xs) = fmap V.toList (V.mapM f xs)
428−listParser _ v = typeMismatch "[a]" v
653+listParser _ v = typeMismatch "Array" v
429{-# INLINE listParser #-}
654{-# INLINE listParser #-}
430
655
431-------------------------------------------------------------------------------
656-------------------------------------------------------------------------------
@@ -443,48 +668,139 @@ instance (FromJSON a) => FromJSON [a] where
443-- Functions
668-- Functions
444-------------------------------------------------------------------------------
669-------------------------------------------------------------------------------
445
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+
446−-- | @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@
447−-- 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"
448withObject :: String -> (Object -> Parser a) -> Value -> Parser a
686withObject :: String -> (Object -> Parser a) -> Value -> Parser a
449−withObject _ f (Object obj) = f obj
687+withObject _ f (Object obj) = f obj
450−withObject expected _ v = typeMismatch expected v
688+withObject name _ v = prependContext name (typeMismatch "Object" v)
451{-# INLINE withObject #-}
689{-# INLINE withObject #-}
452
690
453−-- | @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
454−-- 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"
455withText :: String -> (Text -> Parser a) -> Value -> Parser a
698withText :: String -> (Text -> Parser a) -> Value -> Parser a
456−withText _ f (String txt) = f txt
699+withText _ f (String txt) = f txt
457−withText expected _ v = typeMismatch expected v
700+withText name _ v = prependContext name (typeMismatch "String" v)
458{-# INLINE withText #-}
701{-# INLINE withText #-}
459
702
460−-- | @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
461−-- 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"
462withArray :: String -> (Array -> Parser a) -> Value -> Parser a
710withArray :: String -> (Array -> Parser a) -> Value -> Parser a
463−withArray _ f (Array arr) = f arr
711+withArray _ f (Array arr) = f arr
464−withArray expected _ v = typeMismatch expected v
712+withArray name _ v = prependContext name (typeMismatch "Array" v)
465{-# INLINE withArray #-}
713{-# INLINE withArray #-}
466
714
467−-- | @withNumber expected f value@ applies @f@ to the 'Number' when @value@ is a 'Number'.
468−-- and fails using @'typeMismatch' expected@ otherwise.
469−withNumber :: String -> (Number -> Parser a) -> Value -> Parser a
470−withNumber expected f = withScientific expected (f . scientificToNumber)
471−{-# INLINE withNumber #-}
472−{-# DEPRECATED withNumber "Use withScientific instead" #-}
473−
474−-- | @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
475−-- 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"
476withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
728withScientific :: String -> (Scientific -> Parser a) -> Value -> Parser a
477−withScientific _ f (Number scientific) = f scientific
729+withScientific _ f (Number scientific) = f scientific
478−withScientific expected _ v = typeMismatch expected v
730+withScientific name _ v = prependContext name (typeMismatch "Number" v)
479{-# INLINE withScientific #-}
731{-# INLINE withScientific #-}
480
732
481−-- | @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
482−-- 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"
483withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
789withBool :: String -> (Bool -> Parser a) -> Value -> Parser a
484−withBool _ f (Bool arr) = f arr
790+withBool _ f (Bool arr) = f arr
485−withBool expected _ v = typeMismatch expected v
791+withBool name _ v = prependContext name (typeMismatch "Boolean" v)
486{-# INLINE withBool #-}
792{-# INLINE withBool #-}
487
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+
488-- | 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.
489fromJSON :: (FromJSON a) => Value -> Result a
805fromJSON :: (FromJSON a) => Value -> Result a
490fromJSON = parse parseJSON
806fromJSON = parse parseJSON
@@ -503,32 +819,68 @@ ifromJSON = iparse parseJSON
503-- 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
504-- optional, use '.:?' instead.
820-- optional, use '.:?' instead.
505(.:) :: (FromJSON a) => Object -> Text -> Parser a
821(.:) :: (FromJSON a) => Object -> Text -> Parser a
506−obj .: key = case H.lookup key obj of
822+(.:) = explicitParseField parseJSON
507− Nothing -> fail $ "key " ++ show key ++ " not present"
508− Just v -> parseJSON v <?> Key key
509{-# INLINE (.:) #-}
823{-# INLINE (.:) #-}
510
824
511−-- | Retrieve the value associated with the given key of an 'Object'.
825+-- | Retrieve the value associated with the given key of an 'Object'. The
512−-- The result is 'Nothing' if the key is not present, or 'empty' if
826+-- result is 'Nothing' if the key is not present or if its value is 'Null',
513−-- the value cannot be converted to the desired type.
827+-- or 'empty' if the value cannot be converted to the desired type.
514--
828--
515-- 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
516-- from an object without affecting its validity. If the key and
830-- from an object without affecting its validity. If the key and
517-- value are mandatory, use '.:' instead.
831-- value are mandatory, use '.:' instead.
518(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
832(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
519−obj .:? key = case H.lookup key obj of
833+(.:?) = explicitParseFieldMaybe parseJSON
520− Nothing -> pure Nothing
521− Just v -> parseJSON v <?> Key key
522{-# INLINE (.:?) #-}
834{-# INLINE (.:?) #-}
523
835
524−-- | Like '.:?', but the resulting parser will fail,
836+-- | Retrieve the value associated with the given key of an 'Object'.
525−-- 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'.
526(.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
842(.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)
527−obj .:! key = case H.lookup key obj of
843+(.:!) = explicitParseFieldMaybe' parseJSON
528− Nothing -> pure Nothing
529− Just v -> Just <$> parseJSON v <?> Key key
530{-# INLINE (.:!) #-}
844{-# INLINE (.:!) #-}
531
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+
532-- | Helper for use in combination with '.:?' to provide default
884-- | Helper for use in combination with '.:?' to provide default
533-- values for optional JSON object fields.
885-- values for optional JSON object fields.
534--
886--
@@ -551,258 +903,554 @@ pmval .!= val = fromMaybe val <$> pmval
551-- Generic parseJSON
903-- Generic parseJSON
552-------------------------------------------------------------------------------
904-------------------------------------------------------------------------------
553
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+
554−instance OVERLAPPABLE_ (GFromJSON a) => GFromJSON (M1 i c a) where
911+instance OVERLAPPABLE_ (GFromJSON arity a) => GFromJSON arity (M1 i c a) where
555 -- 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
556 -- parsed value:
913 -- parsed value:
557− gParseJSON opts = fmap M1 . gParseJSON opts
914+ gParseJSON opts fargs = fmap M1 . gParseJSON opts fargs
915+
916+-- Information for error messages
558
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+
559−instance (FromJSON a) => GFromJSON (K1 i a) where
947+instance (FromJSON a) => GFromJSON arity (K1 i a) where
560 -- Constant values are decoded using their FromJSON instance:
948 -- Constant values are decoded using their FromJSON instance:
561− gParseJSON _opts = fmap K1 . parseJSON
949+ gParseJSON _opts _ = fmap K1 . parseJSON
950+
951+instance GFromJSON One Par1 where
952+ -- Direct occurrences of the last type parameter are decoded with the
953+ -- function passed in as an argument:
954+ gParseJSON _opts (From1Args pj _) = fmap Par1 . pj
955+
956+instance (FromJSON1 f) => GFromJSON One (Rec1 f) where
957+ -- Recursive occurrences of the last type parameter are decoded using their
958+ -- FromJSON1 instance:
959+ gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl
960+
961+instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where
962+ -- If an occurrence of the last type parameter is nested inside two
963+ -- composed types, it is decoded by using the outermost type's FromJSON1
964+ -- instance to generically decode the innermost type:
965+ gParseJSON opts fargs =
966+ let gpj = gParseJSON opts fargs in
967+ fmap Comp1 . liftParseJSON gpj (listParser gpj)
562
968
563−instance GFromJSON U1 where
969+--------------------------------------------------------------------------------
564− -- Empty constructors are expected to be encoded as an empty array:
565− gParseJSON _opts v
566− | isEmptyArray v = pure U1
567− | otherwise = typeMismatch "unit constructor (U1)" v
568
970
569−instance (ConsFromJSON a) => GFromJSON (C1 c a) where
971+instance (GFromJSON' arity a, Datatype d) => GFromJSON arity (D1 d a) where
570− -- Constructors need to be decoded differently depending on whether they're
972+ -- Meta-information, which is not handled elsewhere, is just added to the
571− -- a record or not. This distinction is made by consParseJSON:
973+ -- parsed value:
572− gParseJSON opts = fmap M1 . consParseJSON opts
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)
573
1001
574−instance ( FromProduct a, FromProduct b
1002+-- | Multiple constructors.
575− , ProductSize a, ProductSize b ) => GFromJSON (a :*: b) where
576− -- Products are expected to be encoded to an array. Here we check whether we
577− -- got an array of the same size as the product, then parse each of the
578− -- product's elements using parseProduct:
579− gParseJSON opts = withArray "product (:*:)" $ \arr ->
580− let lenArray = V.length arr
581− lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
582− productSize in
583− if lenArray == lenProduct
584− then parseProduct opts arr 0 lenProduct
585− else fail $ "When expecting a product of " ++ show lenProduct ++
586− " values, encountered an Array of " ++ show lenArray ++
587− " elements instead"
588−
589−instance ( AllNullary (a :+: b) allNullary
1003+instance ( AllNullary (a :+: b) allNullary
590− , ParseSum (a :+: b) allNullary ) => GFromJSON (a :+: b) where
1004+ , ParseSum arity (a :+: b) allNullary
1005+ ) => GFromJSON' arity (a :+: b) where
591 -- If all constructors of a sum datatype are nullary and the
1006 -- If all constructors of a sum datatype are nullary and the
592 -- 'allNullaryToStringTag' option is set they are expected to be
1007 -- 'allNullaryToStringTag' option is set they are expected to be
593 -- encoded as strings. This distinction is made by 'parseSum':
1008 -- encoded as strings. This distinction is made by 'parseSum':
1009+ gParseJSON' p =
594− gParseJSON opts = (unTagged :: Tagged allNullary (Parser ((a :+: b) d)) ->
1010+ (unTagged :: Tagged allNullary (Parser ((a :+: b) _d)) ->
595− (Parser ((a :+: b) d)))
1011+ Parser ((a :+: b) _d))
596− . parseSum opts
1012+ . parseSum p
597
1013
598--------------------------------------------------------------------------------
1014--------------------------------------------------------------------------------
599
1015
600−class ParseSum f allNullary where
1016+class ParseSum arity f allNullary where
1017+ parseSum :: TypeName :* Options :* FromArgs arity a
1018+ -> Value
601− parseSum :: Options -> Value -> Tagged allNullary (Parser (f a))
1019+ -> Tagged allNullary (Parser (f a))
602−
1020+
1021+instance ( ConstructorNames f
603−instance ( SumFromString (a :+: b)
1022+ , SumFromString f
604− , FromPair (a :+: b)
1023+ , FromPair arity f
1024+ , FromTaggedObject arity f
1025+ , FromUntaggedValue arity f
605− , FromTaggedObject (a :+: b) ) => ParseSum (a :+: b) True where
1026+ ) => ParseSum arity f True where
606− parseSum opts
1027+ parseSum p@(tname :* opts :* _)
607− | allNullaryToStringTag opts = Tagged . parseAllNullarySum opts
1028+ | allNullaryToStringTag opts = Tagged . parseAllNullarySum tname opts
608− | otherwise = Tagged . parseNonAllNullarySum opts
1029+ | otherwise = Tagged . parseNonAllNullarySum p
609−
1030+
610−instance ( FromPair (a :+: b)
1031+instance ( ConstructorNames f
1032+ , FromPair arity f
1033+ , FromTaggedObject arity f
1034+ , FromUntaggedValue arity f
611− , FromTaggedObject (a :+: b) ) => ParseSum (a :+: b) False where
1035+ ) => ParseSum arity f False where
612− parseSum opts = Tagged . parseNonAllNullarySum opts
1036+ parseSum p = Tagged . parseNonAllNullarySum p
613
1037
614--------------------------------------------------------------------------------
1038--------------------------------------------------------------------------------
615
1039
616−parseAllNullarySum :: SumFromString f => Options -> Value -> Parser (f a)
1040+parseAllNullarySum :: (SumFromString f, ConstructorNames f)
1041+ => TypeName -> Options -> Value -> Parser (f a)
617−parseAllNullarySum opts = withText "Text" $ \key ->
1042+parseAllNullarySum tname opts =
1043+ withText tname $ \tag ->
618− maybe (notFound key) return $
1044+ maybe (badTag tag) return $
619− 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])
620
1062
621class SumFromString f where
1063class SumFromString f where
622− parseSumFromString :: Options -> Text -> Maybe (f a)
1064+ parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
623
1065
624instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1066instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
625 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1067 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
626 (R1 <$> parseSumFromString opts key)
1068 (R1 <$> parseSumFromString opts key)
627
1069
628instance (Constructor c) => SumFromString (C1 c U1) where
1070instance (Constructor c) => SumFromString (C1 c U1) where
629− parseSumFromString opts key | key == name = Just $ M1 U1
1071+ parseSumFromString modifier key
1072+ | key == name = Just $ M1 U1
630− | otherwise = Nothing
1073+ | otherwise = Nothing
631− where
1074+ where
632− name = pack $ constructorTagModifier opts $
633− conName (undefined :: t c U1 p)
1075+ name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
634
1076
635−--------------------------------------------------------------------------------
1077+-- For genericFromJSONKey
1078+instance SumFromString a => SumFromString (D1 d a) where
1079+ parseSumFromString modifier key = M1 <$> parseSumFromString modifier key
1080+
1081+-- | List of all constructor tags.
1082+constructorTags :: ConstructorNames a => (String -> t) -> Tagged2 a [t]
1083+constructorTags modifier =
1084+ fmap DList.toList (constructorNames' modifier)
1085+
1086+-- | List of all constructor names of an ADT, after a given conversion
1087+-- function. (Better inlining.)
1088+class ConstructorNames a where
1089+ constructorNames' :: (String -> t) -> Tagged2 a (DList.DList t)
1090+
1091+instance (ConstructorNames a, ConstructorNames b) => ConstructorNames (a :+: b) where
1092+ constructorNames' = liftA2 append constructorNames' constructorNames'
1093+ where
1094+ append
1095+ :: Tagged2 a (DList.DList t)
1096+ -> Tagged2 b (DList.DList t)
1097+ -> Tagged2 (a :+: b) (DList.DList t)
1098+ append (Tagged2 xs) (Tagged2 ys) = Tagged2 (DList.append xs ys)
1099+
1100+instance Constructor c => ConstructorNames (C1 c a) where
1101+ constructorNames' f = Tagged2 (pure (f cname))
1102+ where
1103+ cname = conName (undefined :: M1 _i c _f _p)
636
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+--------------------------------------------------------------------------------
637−parseNonAllNullarySum :: ( FromPair (a :+: b)
1113+parseNonAllNullarySum :: forall f c arity.
1114+ ( FromPair arity f
638− , FromTaggedObject (a :+: b)
1115+ , FromTaggedObject arity f
1116+ , FromUntaggedValue arity f
1117+ , ConstructorNames f
1118+ ) => TypeName :* Options :* FromArgs arity c
639− ) => Options -> Value -> Parser ((a :+: b) c)
1119+ -> Value -> Parser (f c)
640−parseNonAllNullarySum opts =
1120+parseNonAllNullarySum p@(tname :* opts :* _) =
641 case sumEncoding opts of
1121 case sumEncoding opts of
642 TaggedObject{..} ->
1122 TaggedObject{..} ->
643− withObject "Object" $ \obj -> do
1123+ withObject tname $ \obj -> do
1124+ tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
1125+ fromMaybe (badTag tag <?> Key tagKey) $
1126+ parseFromTaggedObject (tag :* contentsFieldName :* p) obj
1127+ where
644− tag <- obj .: pack tagFieldName
1128+ tagKey = pack tagFieldName
645− fromMaybe (notFound tag) $
1129+ badTag tag = failWith_ $ \cnames ->
646− parseFromTaggedObject opts contentsFieldName obj tag
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])
647
1133
648 ObjectWithSingleField ->
1134 ObjectWithSingleField ->
649− withObject "Object" $ \obj ->
650− case H.toList obj of
1135+ withObject tname $ \obj -> case H.toList obj of
651− [pair@(tag, _)] -> fromMaybe (notFound tag) $
1136+ [(tag, v)] -> maybe (badTag tag) (<?> Key tag) $
652− parsePair opts pair
1137+ parsePair (tag :* p) v
653− _ -> 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
654
1145
655 TwoElemArray ->
1146 TwoElemArray ->
656− withArray "Array" $ \arr ->
1147+ withArray tname $ \arr -> case V.length arr of
657− if V.length arr == 2
1148+ 2 | String tag <- V.unsafeIndex arr 0 ->
658− then case V.unsafeIndex arr 0 of
1149+ maybe (badTag tag <?> Index 0) (<?> Index 1) $
659− String tag -> fromMaybe (notFound tag) $
660− parsePair opts (tag, V.unsafeIndex arr 1)
1150+ parsePair (tag :* p) (V.unsafeIndex arr 1)
1151+ | otherwise ->
1152+ contextType tname $
661− _ -> fail "First element is not a String"
1153+ fail "tag element is not a String" <?> Index 0
662− 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
1161+
1162+ UntaggedValue -> parseUntaggedValue p
1163+ where
1164+ failWith_ = failWithCTags tname (constructorTagModifier opts)
663
1165
664--------------------------------------------------------------------------------
1166--------------------------------------------------------------------------------
665
1167
666−class FromTaggedObject f where
1168+class 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.
667− parseFromTaggedObject :: Options -> String -> Object -> Text
1171+ parseFromTaggedObject
1172+ :: Text :* String :* TypeName :* Options :* FromArgs arity a
1173+ -> Object
668− -> Maybe (Parser (f a))
1174+ -> Maybe (Parser (f a))
669−
1175+
670−instance (FromTaggedObject a, FromTaggedObject b) =>
1176+instance ( FromTaggedObject arity a, FromTaggedObject arity b) =>
671− FromTaggedObject (a :+: b) where
1177+ FromTaggedObject arity (a :+: b) where
672− parseFromTaggedObject opts contentsFieldName obj tag =
1178+ parseFromTaggedObject p obj =
673− (fmap L1 <$> parseFromTaggedObject opts contentsFieldName obj tag) <|>
1179+ (fmap L1 <$> parseFromTaggedObject p obj) <|>
674− (fmap R1 <$> parseFromTaggedObject opts contentsFieldName obj tag)
1180+ (fmap R1 <$> parseFromTaggedObject p obj)
675−
1181+
1182+instance ( IsRecord f isRecord
676−instance ( FromTaggedObject' f
1183+ , FromTaggedObject' arity f isRecord
1184+ , Constructor c
677− , Constructor c ) => FromTaggedObject (C1 c f) where
1185+ ) => FromTaggedObject arity (C1 c f) where
678− parseFromTaggedObject opts 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)) .
679− | tag == name = Just $ M1 <$> parseFromTaggedObject'
1190+ parseFromTaggedObject' (contentsFieldName :* cname :* p)
680− opts contentsFieldName obj
681− | otherwise = Nothing
1191+ | otherwise = const Nothing
682− where
1192+ where
683− name = pack $ constructorTagModifier opts $
1193+ tag' = pack $ constructorTagModifier opts cname
684− conName (undefined :: t c f p)
1194+ cname = conName (undefined :: M1 _i c _f _p)
685
1195
686--------------------------------------------------------------------------------
1196--------------------------------------------------------------------------------
687
1197
688−class FromTaggedObject' f where
1198+class FromTaggedObject' arity f isRecord where
1199+ -- The first component of the parameter tuple is the contents field name.
689− parseFromTaggedObject' :: Options -> String -> Object -> Parser (f a)
1200+ parseFromTaggedObject'
1201+ :: String :* ConName :* TypeName :* Options :* FromArgs arity a
1202+ -> Object -> Tagged isRecord (Parser (f a))
1203+
1204+instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
1205+ -- Records are unpacked in the tagged object
1206+ parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
1207+
1208+instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
1209+ -- Nonnullary nonrecords are encoded in the contents field
1210+ parseFromTaggedObject' p obj = Tagged $ do
1211+ contents <- contextCons cname tname (obj .: key)
1212+ consParseJSON p' contents <?> Key key
1213+ where
1214+ key = pack contentsFieldName
1215+ contentsFieldName :* p'@(cname :* tname :* _) = p
690
1216
691−class FromTaggedObject'' f isRecord where
1217+instance OVERLAPPING_ FromTaggedObject' arity U1 False where
1218+ -- Nullary constructors don't need a contents field
692− parseFromTaggedObject'' :: Options -> String -> Object
1219+ parseFromTaggedObject' _ _ = Tagged (pure U1)
693− -> Tagged isRecord (Parser (f a))
694
1220
695−instance ( IsRecord f isRecord
1221+--------------------------------------------------------------------------------
696− , FromTaggedObject'' f isRecord
697− ) => FromTaggedObject' f where
698− parseFromTaggedObject' opts contentsFieldName =
699− (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a)) .
700− parseFromTaggedObject'' opts contentsFieldName
701
1222
702−instance (FromRecord f) => FromTaggedObject'' f True where
1223+-- | Constructors need to be decoded differently depending on whether they're
703− parseFromTaggedObject'' opts _ = Tagged . parseRecord opts Nothing
1224+-- a record or not. This distinction is made by 'ConsParseJSON'.
1225+class ConsFromJSON arity f where
1226+ consParseJSON
1227+ :: ConName :* TypeName :* Options :* FromArgs arity a
1228+ -> Value -> Parser (f a)
1229+
1230+class ConsFromJSON' arity f isRecord where
1231+ consParseJSON'
1232+ :: ConName :* TypeName :* Options :* FromArgs arity a
1233+ -> Value -> Tagged isRecord (Parser (f a))
1234+
1235+instance ( IsRecord f isRecord
1236+ , ConsFromJSON' arity f isRecord
1237+ ) => ConsFromJSON arity f where
1238+ consParseJSON p =
1239+ (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
1240+ . consParseJSON' p
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))
1248+
1249+instance RecordFromJSON arity f => ConsFromJSON' arity f True where
1250+ consParseJSON' p@(cname :* tname :* _) =
1251+ Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
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)
704
1265
1266+instance OVERLAPPING_
705−instance (GFromJSON f) => FromTaggedObject'' f False where
1267+ GFromJSON arity f => ConsFromJSON' arity (S1 s f) False where
706− parseFromTaggedObject'' opts contentsFieldName = Tagged .
1268+ consParseJSON' (_ :* _ :* opts :* fargs) =
707− (gParseJSON opts <=< (.: pack contentsFieldName))
1269+ Tagged . fmap M1 . gParseJSON opts fargs
708
1270
709−instance OVERLAPPING_ FromTaggedObject'' U1 False where
1271+instance (ProductFromJSON arity f, ProductSize f
710− parseFromTaggedObject'' _ _ _ = Tagged (pure U1)
1272+ ) => ConsFromJSON' arity f False where
1273+ consParseJSON' p = Tagged . productParseJSON0 p
711
1274
712--------------------------------------------------------------------------------
1275--------------------------------------------------------------------------------
713
1276
714−class ConsFromJSON f where
1277+class FieldNames f where
715− consParseJSON :: Options -> Value -> Parser (f a)
1278+ fieldNames :: f a -> [Text] -> [Text]
716−
717−class ConsFromJSON' f isRecord where
718− consParseJSON' :: Options -> (Maybe Text) -- ^ A dummy label
719− -- (Nothing to use proper label)
720− -> Value -> Tagged isRecord (Parser (f a))
721−
722−instance ( IsRecord f isRecord
723− , ConsFromJSON' f isRecord
724− ) => ConsFromJSON f where
725− consParseJSON opts v = let
726− (v2,lab) = case (unwrapUnaryRecords opts,isUnary (undefined :: f a)) of
727− -- use a dummy object with a dummy label
728− (True,True) -> ((object [(pack "dummy",v)]),Just $ pack "dummy")
729− _ ->(v,Nothing)
730− in (unTagged :: Tagged isRecord (Parser (f a)) -> Parser (f a))
731− $ consParseJSON' opts lab v2
732−
733
1279
734−instance (FromRecord f) => ConsFromJSON' f True where
1280+instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where
735− consParseJSON' opts mlab = Tagged . (withObject "record (:*:)"
1281+ fieldNames _ =
736− $ parseRecord opts mlab)
1282+ fieldNames (undefined :: a x) .
1283+ fieldNames (undefined :: b y)
737
1284
738−instance (GFromJSON f) => ConsFromJSON' f False where
1285+instance (Selector s) => FieldNames (S1 s f) where
739− consParseJSON' opts _ = Tagged . gParseJSON opts
1286+ fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)
740
1287
741−--------------------------------------------------------------------------------
1288+class RecordFromJSON arity f where
1289+ recordParseJSON
1290+ :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
1291+ -> Object -> Parser (f a)
742
1292
1293+instance ( FieldNames f
1294+ , RecordFromJSON' arity f
1295+ ) => RecordFromJSON arity f where
1296+ recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
1297+ \obj -> checkUnknown obj >> recordParseJSON' p obj
1298+ where
1299+ knownFields :: H.HashMap Text ()
1300+ knownFields = H.fromList $ map (,()) $
1301+ fieldNames (undefined :: f a)
1302+ [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]
1303+ checkUnknown =
1304+ if not (rejectUnknownFields opts)
1305+ then \_ -> return ()
1306+ else \obj -> case H.keys (H.difference obj knownFields) of
1307+ [] -> return ()
1308+ unknownFields -> contextCons cname tname $
1309+ fail ("unknown fields: " ++ show unknownFields)
1310+
743−class FromRecord f where
1311+class RecordFromJSON' arity f where
744− parseRecord :: Options -> (Maybe Text) -- ^ A dummy label
1312+ recordParseJSON'
745− -- (Nothing to use proper label)
1313+ :: ConName :* TypeName :* Options :* FromArgs arity a
746− -> Object -> Parser (f 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
1329+ label = pack $ fieldLabelModifier opts sname
1330+ sname = selName (undefined :: M1 _i s _f _p)
747
1331
748−instance (FromRecord a, FromRecord b) => FromRecord (a :*: b) where
1332+instance INCOHERENT_ (Selector s, FromJSON a) =>
749− parseRecord opts _ obj = (:*:) <$> parseRecord opts Nothing obj
1333+ RecordFromJSON' arity (S1 s (K1 i (Maybe a))) where
750− <*> parseRecord opts Nothing obj
1334+ recordParseJSON' (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1335+ where
1336+ label = fieldLabelModifier opts sname
1337+ sname = selName (undefined :: M1 _i s _f _p)
751
1338
752−instance (Selector s, GFromJSON a) => FromRecord (S1 s a) where
1339+-- Parse an Option like a Maybe.
753− parseRecord opts lab = (<?> Key label) . gParseJSON opts <=< (.: label)
754− where
755− label = fromMaybe defLabel lab
756− defLabel = pack . fieldLabelModifier opts $
757− selName (undefined :: t s a p)
758−
759−instance OVERLAPPING_ (Selector s, FromJSON a) =>
1340+instance INCOHERENT_ (Selector s, FromJSON a) =>
760− FromRecord (S1 s (K1 i (Maybe a))) where
1341+ RecordFromJSON' arity (S1 s (K1 i (Semigroup.Option a))) where
761− parseRecord _ (Just lab) obj = (M1 . K1) <$> obj .:? lab
1342+ recordParseJSON' p obj = wrap <$> recordParseJSON' p obj
762− parseRecord opts Nothing obj = (M1 . K1) <$> obj .:? pack label
1343+ where
763− where
1344+ wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
764− label = fieldLabelModifier opts $
1345+ wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
765− selName (undefined :: t s (K1 i (Maybe a)) p)
766
1346
767--------------------------------------------------------------------------------
1347--------------------------------------------------------------------------------
768
1348
769−class FromProduct f where
1349+productParseJSON0
770− parseProduct :: Options -> Array -> Int -> Int -> Parser (f a)
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+--
771
1368
772−instance (FromProduct a, FromProduct b) => FromProduct (a :*: b) where
1369+class ProductFromJSON arity f where
773− parseProduct opts arr ix len =
1370+ productParseJSON :: ConName :* TypeName :* Options :* FromArgs arity a
774− (:*:) <$> parseProduct opts arr ix lenL
1371+ -> Array -> Int -> Int
775− <*> parseProduct opts arr ixR lenR
1372+ -> Parser (f a)
1373+
1374+instance ( ProductFromJSON arity a
1375+ , ProductFromJSON arity b
1376+ ) => ProductFromJSON arity (a :*: b) where
1377+ productParseJSON p arr ix len =
1378+ (:*:) <$> productParseJSON p arr ix lenL
1379+ <*> productParseJSON p arr ixR lenR
776 where
1380 where
777 lenL = len `unsafeShiftR` 1
1381 lenL = len `unsafeShiftR` 1
778 ixR = ix + lenL
1382 ixR = ix + lenL
779 lenR = len - lenL
1383 lenR = len - lenL
780
1384
781−instance (GFromJSON a) => FromProduct (S1 s a) where
1385+instance (GFromJSON arity a) => ProductFromJSON arity (S1 s a) where
782− parseProduct opts arr ix _ = gParseJSON opts $ V.unsafeIndex arr ix
1386+ productParseJSON (_ :* _ :* opts :* fargs) arr ix _ =
1387+ M1 <$> gParseJSON opts fargs (V.unsafeIndex arr ix) <?> Index ix
783
1388
784--------------------------------------------------------------------------------
1389--------------------------------------------------------------------------------
785
1390
786−class FromPair f where
1391+class FromPair arity f where
1392+ -- The first component of the parameter tuple is the tag to match.
1393+ parsePair :: Text :* TypeName :* Options :* FromArgs arity a
1394+ -> Value
787− parsePair :: Options -> Pair -> Maybe (Parser (f a))
1395+ -> Maybe (Parser (f a))
788−
1396+
789−instance (FromPair a, FromPair b) => FromPair (a :+: b) where
1397+instance ( FromPair arity a
1398+ , FromPair arity b
1399+ ) => FromPair arity (a :+: b) where
1400+ parsePair p pair =
790− parsePair opts pair = (fmap L1 <$> parsePair opts pair) <|>
1401+ (fmap L1 <$> parsePair p pair) <|>
791− (fmap R1 <$> parsePair opts pair)
1402+ (fmap R1 <$> parsePair p pair)
792−
1403+
793−instance (Constructor c, GFromJSON a, ConsFromJSON a) => FromPair (C1 c a) where
1404+instance ( Constructor c
1405+ , ConsFromJSON arity a
1406+ ) => FromPair arity (C1 c a) where
794− parsePair opts (tag, value)
1407+ parsePair (tag :* p@(_ :* opts :* _)) v
795− | tag == tag' = Just $ gParseJSON opts value
1408+ | tag == tag' = Just $ M1 <$> consParseJSON (cname :* p) v
796 | otherwise = Nothing
1409 | otherwise = Nothing
797− where
1410+ where
798− tag' = pack $ constructorTagModifier opts $
1411+ tag' = pack $ constructorTagModifier opts cname
799− conName (undefined :: t c a p)
1412+ cname = conName (undefined :: M1 _i c _a _p)
800
1413
801--------------------------------------------------------------------------------
1414--------------------------------------------------------------------------------
802
1415
1416+class FromUntaggedValue arity f where
1417+ parseUntaggedValue :: TypeName :* Options :* FromArgs arity a
1418+ -> Value
803−notFound :: Text -> Parser a
1419+ -> Parser (f a)
804−notFound key = fail $ "The key \"" ++ unpack key ++ "\" was not found"
1420+
805−{-# INLINE notFound #-}
1421+instance
1422+ ( FromUntaggedValue arity a
1423+ , FromUntaggedValue arity b
1424+ ) => FromUntaggedValue arity (a :+: b)
1425+ where
1426+ parseUntaggedValue p value =
1427+ L1 <$> parseUntaggedValue p value <|>
1428+ R1 <$> parseUntaggedValue p value
1429+
1430+instance OVERLAPPABLE_
1431+ ( ConsFromJSON arity a
1432+ , Constructor c
1433+ ) => FromUntaggedValue arity (C1 c a)
1434+ where
1435+ parseUntaggedValue p = fmap M1 . consParseJSON (cname :* p)
1436+ where
1437+ cname = conName (undefined :: M1 _i c _f _p)
1438+
1439+instance OVERLAPPING_
1440+ ( Constructor c )
1441+ => FromUntaggedValue arity (C1 c U1)
1442+ where
1443+ parseUntaggedValue (tname :* opts :* _) v =
1444+ contextCons cname tname $ case v of
1445+ String tag
1446+ | tag == tag' -> pure $ M1 U1
1447+ | otherwise -> fail_ tag
1448+ _ -> typeMismatch "String" v
1449+ where
1450+ tag' = pack $ constructorTagModifier opts cname
1451+ cname = conName (undefined :: M1 _i c _f _p)
1452+ fail_ tag = fail $
1453+ "expected tag " ++ show tag' ++ ", but found tag " ++ show tag
806
1454
807-------------------------------------------------------------------------------
1455-------------------------------------------------------------------------------
808-- Instances
1456-- Instances
@@ -812,13 +1460,6 @@ notFound key = fail $ "The key \"" ++ unpack key ++ "\" was not found"
812-- base
1460-- base
813-------------------------------------------------------------------------------
1461-------------------------------------------------------------------------------
814
1462
815−instance FromJSON1 Identity where
816− liftParseJSON p _ a = Identity <$> p a
817− {-# INLINE liftParseJSON #-}
818−
819−instance (FromJSON a) => FromJSON (Identity a) where
820− parseJSON = parseJSON1
821− {-# INLINE parseJSON #-}
822
1463
823instance FromJSON2 Const where
1464instance FromJSON2 Const where
824 liftParseJSON2 p _ _ _ = fmap Const . p
1465 liftParseJSON2 p _ _ _ = fmap Const . p
@@ -848,6 +1489,10 @@ instance FromJSON2 Either where
848 liftParseJSON2 pA _ pB _ (Object (H.toList -> [(key, value)]))
1489 liftParseJSON2 pA _ pB _ (Object (H.toList -> [(key, value)]))
849 | key == left = Left <$> pA value <?> Key left
1490 | key == left = Left <$> pA value <?> Key left
850 | key == right = Right <$> pB value <?> Key right
1491 | key == right = Right <$> pB value <?> Key right
1492+ where
1493+ left, right :: Text
1494+ left = "Left"
1495+ right = "Right"
851
1496
852 liftParseJSON2 _ _ _ _ _ = fail $
1497 liftParseJSON2 _ _ _ _ _ = fail $
853 "expected an object with a single property " ++
1498 "expected an object with a single property " ++
@@ -863,147 +1508,241 @@ instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
863 parseJSON = parseJSON2
1508 parseJSON = parseJSON2
864 {-# INLINE parseJSON #-}
1509 {-# INLINE parseJSON #-}
865
1510
866−left, right :: Text
1511+instance FromJSON Void where
867−left = "Left"
1512+ parseJSON _ = fail "Cannot parse Void"
868−right = "Right"
1513+ {-# INLINE parseJSON #-}
869−
870
1514
871instance FromJSON Bool where
1515instance FromJSON Bool where
872− parseJSON = withBool "Bool" pure
1516+ parseJSON (Bool b) = pure b
1517+ parseJSON v = typeMismatch "Bool" v
873 {-# INLINE parseJSON #-}
1518 {-# INLINE parseJSON #-}
874
1519
1520+instance FromJSONKey Bool where
1521+ fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1522+ "true" -> pure True
1523+ "false" -> pure False
1524+ _ -> fail $ "cannot parse key " ++ show t ++ " into Bool"
1525+
875instance FromJSON Ordering where
1526instance FromJSON Ordering where
876 parseJSON = withText "Ordering" $ \s ->
1527 parseJSON = withText "Ordering" $ \s ->
877 case s of
1528 case s of
878 "LT" -> return LT
1529 "LT" -> return LT
879 "EQ" -> return EQ
1530 "EQ" -> return EQ
880 "GT" -> return GT
1531 "GT" -> return GT
1532+ _ -> fail $ "parsing Ordering failed, unexpected " ++ show s ++
881− _ -> fail "Parsing Ordering value failed: expected \"LT\", \"EQ\", or \"GT\""
1533+ " (expected \"LT\", \"EQ\", or \"GT\")"
882
1534
883instance FromJSON () where
1535instance FromJSON () where
884 parseJSON = withArray "()" $ \v ->
1536 parseJSON = withArray "()" $ \v ->
885 if V.null v
1537 if V.null v
886 then pure ()
1538 then pure ()
887− else fail "Expected an empty array"
1539+ else prependContext "()" $ fail "expected an empty array"
888 {-# INLINE parseJSON #-}
1540 {-# INLINE parseJSON #-}
889
1541
890instance FromJSON Char where
1542instance FromJSON Char where
891− parseJSON = withText "Char" $ \t ->
1543+ parseJSON = withText "Char" parseChar
892− if T.compareLength t 1 == EQ
893− then pure $ T.head t
894− else fail "Expected a string of length 1"
895 {-# INLINE parseJSON #-}
1544 {-# INLINE parseJSON #-}
896
1545
897− parseJSONList = withText "String" $ pure . T.unpack
1546+ parseJSONList (String s) = pure (T.unpack s)
1547+ parseJSONList v = typeMismatch "String" v
898 {-# INLINE parseJSONList #-}
1548 {-# INLINE parseJSONList #-}
899
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+
900instance FromJSON Double where
1556instance FromJSON Double where
901 parseJSON = parseRealFloat "Double"
1557 parseJSON = parseRealFloat "Double"
902 {-# INLINE parseJSON #-}
1558 {-# INLINE parseJSON #-}
903
1559
904−instance FromJSON Number where
1560+instance FromJSONKey Double where
905− parseJSON (Number s) = pure $ scientificToNumber s
1561+ fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
906− parseJSON Null = pure (D (0/0))
1562+ "NaN" -> pure (0/0)
907− parseJSON v = typeMismatch "Number" v
1563+ "Infinity" -> pure (1/0)
908− {-# INLINE parseJSON #-}
1564+ "-Infinity" -> pure (negate 1/0)
1565+ _ -> Scientific.toRealFloat <$> parseScientificText t
909
1566
910instance FromJSON Float where
1567instance FromJSON Float where
911 parseJSON = parseRealFloat "Float"
1568 parseJSON = parseRealFloat "Float"
912 {-# INLINE parseJSON #-}
1569 {-# INLINE parseJSON #-}
913
1570
1571+instance FromJSONKey Float where
1572+ fromJSONKey = FromJSONKeyTextParser $ \t -> case t of
1573+ "NaN" -> pure (0/0)
1574+ "Infinity" -> pure (1/0)
1575+ "-Infinity" -> pure (negate 1/0)
1576+ _ -> Scientific.toRealFloat <$> parseScientificText t
1577+
914−instance FromJSON (Ratio Integer) where
1578+instance (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
915− parseJSON = withObject "Rational" $ \obj ->
1589+ objParser = withObject "Rational" $ \obj -> do
916− (%) <$> obj .: "numerator"
1590+ numerator <- obj .: "numerator"
1591+ denominator <- obj .: "denominator"
1592+ if denominator == 0
1593+ then fail "Ratio denominator was 0"
917− <*> obj .: "denominator"
1594+ else pure $ numerator % denominator
918 {-# INLINE parseJSON #-}
1595 {-# INLINE parseJSON #-}
919
1596
920−-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
1597+-- | This instance includes a bounds check to prevent maliciously
921−-- 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
922−-- system by specifying a scientific number with a big exponent like
1599+-- newtype 'Scientific' and provide your own instance using
923−-- @1e1000000000@.
1600+-- 'withScientific' if you want to allow larger inputs.
924instance HasResolution a => FromJSON (Fixed a) where
1601instance HasResolution a => FromJSON (Fixed a) where
925− parseJSON = withScientific "Fixed" $ pure . realToFrac
1602+ parseJSON = prependContext "Fixed" . withBoundedScientific' (pure . realToFrac)
926 {-# INLINE parseJSON #-}
1603 {-# INLINE parseJSON #-}
927
1604
928instance FromJSON Int where
1605instance FromJSON Int where
929− parseJSON = parseIntegral "Int"
1606+ parseJSON = parseBoundedIntegral "Int"
930 {-# INLINE parseJSON #-}
1607 {-# INLINE parseJSON #-}
931
1608
932−-- | /WARNING:/ Only parse Integers from trusted input since an
1609+instance FromJSONKey Int where
1610+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int"
1611+
1612+-- | This instance includes a bounds check to prevent maliciously
933−-- 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
934−-- specifying a scientific number with a big exponent like
1614+-- newtype 'Scientific' and provide your own instance using
935−-- @1e1000000000@.
1615+-- 'withScientific' if you want to allow larger inputs.
936instance FromJSON Integer where
1616instance FromJSON Integer where
937− parseJSON = withScientific "Integral" $ pure . truncate
1617+ parseJSON = parseIntegral "Integer"
938 {-# INLINE parseJSON #-}
1618 {-# INLINE parseJSON #-}
939
1619
1620+instance FromJSONKey Integer where
1621+ fromJSONKey = FromJSONKeyTextParser $ parseIntegralText "Integer"
1622+
940instance FromJSON Natural where
1623instance FromJSON Natural where
941− parseJSON = withScientific "Natural" $ \s ->
1624+ parseJSON value = do
942− if Scientific.coefficient s < 0
1625+ integer <- parseIntegral "Natural" value
1626+ parseNatural integer
1627+
1628+instance FromJSONKey Natural where
1629+ fromJSONKey = FromJSONKeyTextParser $ \text -> do
1630+ integer <- parseIntegralText "Natural" text
1631+ parseNatural integer
1632+
1633+parseNatural :: Integer -> Parser Natural
1634+parseNatural integer =
1635+ if integer < 0 then
943− then fail $ "Expected a Natural number but got the negative number: " <> show s
1636+ fail $ "parsing Natural failed, unexpected negative number " <> show integer
944− else pure $ truncate s
1637+ else
1638+ pure $ fromIntegral integer
945
1639
946instance FromJSON Int8 where
1640instance FromJSON Int8 where
947− parseJSON = parseIntegral "Int8"
1641+ parseJSON = parseBoundedIntegral "Int8"
948 {-# INLINE parseJSON #-}
1642 {-# INLINE parseJSON #-}
949
1643
1644+instance FromJSONKey Int8 where
1645+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int8"
1646+
950instance FromJSON Int16 where
1647instance FromJSON Int16 where
951− parseJSON = parseIntegral "Int16"
1648+ parseJSON = parseBoundedIntegral "Int16"
952 {-# INLINE parseJSON #-}
1649 {-# INLINE parseJSON #-}
953
1650
1651+instance FromJSONKey Int16 where
1652+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int16"
1653+
954instance FromJSON Int32 where
1654instance FromJSON Int32 where
955− parseJSON = parseIntegral "Int32"
1655+ parseJSON = parseBoundedIntegral "Int32"
956 {-# INLINE parseJSON #-}
1656 {-# INLINE parseJSON #-}
957
1657
1658+instance FromJSONKey Int32 where
1659+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int32"
1660+
958instance FromJSON Int64 where
1661instance FromJSON Int64 where
959− parseJSON = parseIntegral "Int64"
1662+ parseJSON = parseBoundedIntegral "Int64"
960 {-# INLINE parseJSON #-}
1663 {-# INLINE parseJSON #-}
961
1664
1665+instance FromJSONKey Int64 where
1666+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Int64"
1667+
962instance FromJSON Word where
1668instance FromJSON Word where
963− parseJSON = parseIntegral "Word"
1669+ parseJSON = parseBoundedIntegral "Word"
964 {-# INLINE parseJSON #-}
1670 {-# INLINE parseJSON #-}
965
1671
1672+instance FromJSONKey Word where
1673+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word"
1674+
966instance FromJSON Word8 where
1675instance FromJSON Word8 where
967− parseJSON = parseIntegral "Word8"
1676+ parseJSON = parseBoundedIntegral "Word8"
968 {-# INLINE parseJSON #-}
1677 {-# INLINE parseJSON #-}
969
1678
1679+instance FromJSONKey Word8 where
1680+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word8"
1681+
970instance FromJSON Word16 where
1682instance FromJSON Word16 where
971− parseJSON = parseIntegral "Word16"
1683+ parseJSON = parseBoundedIntegral "Word16"
972 {-# INLINE parseJSON #-}
1684 {-# INLINE parseJSON #-}
973
1685
1686+instance FromJSONKey Word16 where
1687+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word16"
1688+
974instance FromJSON Word32 where
1689instance FromJSON Word32 where
975− parseJSON = parseIntegral "Word32"
1690+ parseJSON = parseBoundedIntegral "Word32"
976 {-# INLINE parseJSON #-}
1691 {-# INLINE parseJSON #-}
977
1692
1693+instance FromJSONKey Word32 where
1694+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word32"
1695+
978instance FromJSON Word64 where
1696instance FromJSON Word64 where
979− parseJSON = parseIntegral "Word64"
1697+ parseJSON = parseBoundedIntegral "Word64"
1698+ {-# INLINE parseJSON #-}
1699+
1700+instance FromJSONKey Word64 where
1701+ fromJSONKey = FromJSONKeyTextParser $ parseBoundedIntegralText "Word64"
1702+
1703+instance FromJSON CTime where
1704+ parseJSON = fmap CTime . parseJSON
980 {-# INLINE parseJSON #-}
1705 {-# INLINE parseJSON #-}
981
1706
982instance FromJSON Text where
1707instance FromJSON Text where
983 parseJSON = withText "Text" pure
1708 parseJSON = withText "Text" pure
984 {-# INLINE parseJSON #-}
1709 {-# INLINE parseJSON #-}
985
1710
1711+instance FromJSONKey Text where
1712+ fromJSONKey = fromJSONKeyCoerce
1713+
1714+
986instance FromJSON LT.Text where
1715instance FromJSON LT.Text where
987 parseJSON = withText "Lazy Text" $ pure . LT.fromStrict
1716 parseJSON = withText "Lazy Text" $ pure . LT.fromStrict
988 {-# INLINE parseJSON #-}
1717 {-# INLINE parseJSON #-}
989
1718
1719+instance FromJSONKey LT.Text where
1720+ fromJSONKey = FromJSONKeyText LT.fromStrict
1721+
1722+
990instance FromJSON Version where
1723instance FromJSON Version where
1724+ parseJSON = withText "Version" parseVersionText
991 {-# INLINE parseJSON #-}
1725 {-# INLINE parseJSON #-}
1726+
1727+instance FromJSONKey Version where
1728+ fromJSONKey = FromJSONKeyTextParser parseVersionText
1729+
1730+parseVersionText :: Text -> Parser Version
992− parseJSON = withText "Version" $ go . readP_to_S parseVersion . unpack
1731+parseVersionText = go . readP_to_S parseVersion . unpack
993− where
1732+ where
994− go [(v,[])] = return v
1733+ go [(v,[])] = return v
995− go (_ : xs) = go xs
1734+ go (_ : xs) = go xs
996− go _ = fail $ "could not parse Version"
1735+ go _ = fail "parsing Version failed"
997
1736
998-------------------------------------------------------------------------------
1737-------------------------------------------------------------------------------
999-- semigroups NonEmpty
1738-- semigroups NonEmpty
1000-------------------------------------------------------------------------------
1739-------------------------------------------------------------------------------
1001
1740
1002instance FromJSON1 NonEmpty where
1741instance FromJSON1 NonEmpty where
1003− liftParseJSON p _ = withArray "NonEmpty a" $
1742+ liftParseJSON p _ = withArray "NonEmpty" $
1004 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1743 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1005 where
1744 where
1006− ne [] = fail "Expected a NonEmpty but got an empty list"
1745+ ne [] = fail "parsing NonEmpty failed, unexpected empty list"
1007 ne (x:xs) = pure (x :| xs)
1746 ne (x:xs) = pure (x :| xs)
1008 {-# INLINE liftParseJSON #-}
1747 {-# INLINE liftParseJSON #-}
1009
1748
@@ -1019,12 +1758,103 @@ instance FromJSON Scientific where
1019 parseJSON = withScientific "Scientific" pure
1758 parseJSON = withScientific "Scientific" pure
1020 {-# INLINE parseJSON #-}
1759 {-# INLINE parseJSON #-}
1021
1760
1761+-------------------------------------------------------------------------------
1762+-- DList
1763+-------------------------------------------------------------------------------
1764+
1765+instance FromJSON1 DList.DList where
1766+ liftParseJSON p _ = withArray "DList" $
1767+ fmap DList.fromList .
1768+ Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1769+ {-# INLINE liftParseJSON #-}
1770+
1771+instance (FromJSON a) => FromJSON (DList.DList a) where
1772+ parseJSON = parseJSON1
1773+ {-# INLINE parseJSON #-}
1774+
1775+-------------------------------------------------------------------------------
1776+-- tranformers - Functors
1777+-------------------------------------------------------------------------------
1778+
1779+instance FromJSON1 Identity where
1780+ liftParseJSON p _ a = Identity <$> p a
1781+ {-# INLINE liftParseJSON #-}
1782+
1783+ liftParseJSONList _ p a = fmap Identity <$> p a
1784+ {-# INLINE liftParseJSONList #-}
1785+
1786+instance (FromJSON a) => FromJSON (Identity a) where
1787+ parseJSON = parseJSON1
1788+ {-# INLINE parseJSON #-}
1789+
1790+ parseJSONList = liftParseJSONList parseJSON parseJSONList
1791+ {-# INLINE parseJSONList #-}
1792+
1793+instance (FromJSONKey a) => FromJSONKey (Identity a) where
1794+ fromJSONKey = coerceFromJSONKeyFunction (fromJSONKey :: FromJSONKeyFunction a)
1795+ fromJSONKeyList = coerceFromJSONKeyFunction (fromJSONKeyList :: FromJSONKeyFunction [a])
1796+
1797+
1798+instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Compose f g) where
1799+ liftParseJSON p pl a = Compose <$> liftParseJSON g gl a
1800+ where
1801+ g = liftParseJSON p pl
1802+ gl = liftParseJSONList p pl
1803+ {-# INLINE liftParseJSON #-}
1804+
1805+ liftParseJSONList p pl a = map Compose <$> liftParseJSONList g gl a
1806+ where
1807+ g = liftParseJSON p pl
1808+ gl = liftParseJSONList p pl
1809+ {-# INLINE liftParseJSONList #-}
1810+
1811+instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Compose f g a) where
1812+ parseJSON = parseJSON1
1813+ {-# INLINE parseJSON #-}
1814+
1815+ parseJSONList = liftParseJSONList parseJSON parseJSONList
1816+ {-# INLINE parseJSONList #-}
1817+
1818+
1819+instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Product f g) where
1820+ liftParseJSON p pl a = uncurry Pair <$> liftParseJSON2 px pxl py pyl a
1821+ where
1822+ px = liftParseJSON p pl
1823+ pxl = liftParseJSONList p pl
1824+ py = liftParseJSON p pl
1825+ pyl = liftParseJSONList p pl
1826+ {-# INLINE liftParseJSON #-}
1827+
1828+instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Product f g a) where
1829+ parseJSON = parseJSON1
1830+ {-# INLINE parseJSON #-}
1831+
1832+
1833+instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Sum f g) where
1834+ liftParseJSON p pl (Object (H.toList -> [(key, value)]))
1835+ | key == inl = InL <$> liftParseJSON p pl value <?> Key inl
1836+ | key == inr = InR <$> liftParseJSON p pl value <?> Key inl
1837+ where
1838+ inl, inr :: Text
1839+ inl = "InL"
1840+ inr = "InR"
1841+
1842+ liftParseJSON _ _ _ = fail $
1843+ "parsing Sum failed, expected an object with a single property " ++
1844+ "where the property key should be either " ++
1845+ "\"InL\" or \"InR\""
1846+ {-# INLINE liftParseJSON #-}
1847+
1848+instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Sum f g a) where
1849+ parseJSON = parseJSON1
1850+ {-# INLINE parseJSON #-}
1851+
1022-------------------------------------------------------------------------------
1852-------------------------------------------------------------------------------
1023-- containers
1853-- containers
1024-------------------------------------------------------------------------------
1854-------------------------------------------------------------------------------
1025
1855
1026instance FromJSON1 Seq.Seq where
1856instance FromJSON1 Seq.Seq where
1027− liftParseJSON p _ = withArray "Seq a" $
1857+ liftParseJSON p _ = withArray "Seq" $
1028 fmap Seq.fromList .
1858 fmap Seq.fromList .
1029 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1859 Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1030 {-# INLINE liftParseJSON #-}
1860 {-# INLINE liftParseJSON #-}
@@ -1058,15 +1888,15 @@ instance FromJSON a => FromJSON (IntMap.IntMap a) where
1058
1888
1059instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1889instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where
1060 liftParseJSON p _ = case fromJSONKey of
1890 liftParseJSON p _ = case fromJSONKey of
1061− FromJSONKeyCoerce _-> withObject "Map k v" $
1891+ FromJSONKeyCoerce _ -> withObject "Map" $
1062 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)
1063− FromJSONKeyText f -> withObject "Map k v" $
1893+ FromJSONKeyText f -> withObject "Map" $
1064 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)
1065− FromJSONKeyTextParser f -> withObject "Map k v" $
1895+ FromJSONKeyTextParser f -> withObject "Map" $
1066− H.foldrWithKey (\k v m -> M.insert <$> f 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)
1067− FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1897+ FromJSONKeyValue f -> withArray "Map" $ \arr ->
1068− M.fromList <$> (Tr.sequence .
1898+ fmap M.fromList . Tr.sequence .
1069− zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr)
1899+ zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1070 {-# INLINE liftParseJSON #-}
1900 {-# INLINE liftParseJSON #-}
1071
1901
1072instance (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
@@ -1086,12 +1916,24 @@ instance (FromJSON v) => FromJSON (Tree.Tree v) where
1086 parseJSON = parseJSON1
1916 parseJSON = parseJSON1
1087 {-# INLINE parseJSON #-}
1917 {-# INLINE parseJSON #-}
1088
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+
1089-------------------------------------------------------------------------------
1931-------------------------------------------------------------------------------
1090-- vector
1932-- vector
1091-------------------------------------------------------------------------------
1933-------------------------------------------------------------------------------
1092
1934
1093instance FromJSON1 Vector where
1935instance FromJSON1 Vector where
1094− liftParseJSON p _ = withArray "Vector a" $
1936+ liftParseJSON p _ = withArray "Vector" $
1095 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1937 V.mapM (uncurry $ parseIndexedJSON p) . V.indexed
1096 {-# INLINE liftParseJSON #-}
1938 {-# INLINE liftParseJSON #-}
1097
1939
@@ -1105,14 +1947,14 @@ vectorParseJSON s = withArray s $ fmap V.convert . V.mapM (uncurry $ parseIndexe
1105{-# INLINE vectorParseJSON #-}
1947{-# INLINE vectorParseJSON #-}
1106
1948
1107instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1949instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
1108− parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
1950+ parseJSON = vectorParseJSON "Data.Vector.Storable.Vector"
1109
1951
1110instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1952instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
1111− parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
1953+ parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector"
1112 {-# INLINE parseJSON #-}
1954 {-# INLINE parseJSON #-}
1113
1955
1114instance (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
1115− parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
1957+ parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector"
1116 {-# INLINE parseJSON #-}
1958 {-# INLINE parseJSON #-}
1117
1959
1118-------------------------------------------------------------------------------
1960-------------------------------------------------------------------------------
@@ -1126,15 +1968,15 @@ instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
1126
1968
1127instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1969instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where
1128 liftParseJSON p _ = case fromJSONKey of
1970 liftParseJSON p _ = case fromJSONKey of
1129− FromJSONKeyCoerce _ -> withObject "HashMap ~Text v" $
1971+ FromJSONKeyCoerce _ -> withObject "HashMap ~Text" $
1130 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1972 uc . H.traverseWithKey (\k v -> p v <?> Key k)
1131− FromJSONKeyText f -> withObject "HashMap k v" $
1973+ FromJSONKeyText f -> withObject "HashMap" $
1132 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1974 fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
1133− FromJSONKeyTextParser f -> withObject "HashMap k v" $
1975+ FromJSONKeyTextParser f -> withObject "HashMap" $
1134− H.foldrWithKey (\k v m -> H.insert <$> f 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)
1135− FromJSONKeyValue f -> withArray "Map k v" $ \arr ->
1977+ FromJSONKeyValue f -> withArray "Map" $ \arr ->
1136− H.fromList <$> (Tr.sequence .
1978+ fmap H.fromList . Tr.sequence .
1137− zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr)
1979+ zipWith (parseIndexedJSONPair f p) [0..] . V.toList $ arr
1138 where
1980 where
1139 uc :: Parser (H.HashMap Text v) -> Parser (H.HashMap k v)
1981 uc :: Parser (H.HashMap Text v) -> Parser (H.HashMap k v)
1140 uc = unsafeCoerce
1982 uc = unsafeCoerce
@@ -1155,11 +1997,33 @@ instance FromJSON DotNetTime where
1155 parseJSON = withText "DotNetTime" $ \t ->
1997 parseJSON = withText "DotNetTime" $ \t ->
1156 let (s,m) = T.splitAt (T.length t - 5) t
1998 let (s,m) = T.splitAt (T.length t - 5) t
1157 t' = T.concat [s,".",m]
1999 t' = T.concat [s,".",m]
1158− in case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
2000+ in case parseTimeM True defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
1159 Just d -> pure (DotNetTime d)
2001 Just d -> pure (DotNetTime d)
1160 _ -> fail "could not parse .NET time"
2002 _ -> fail "could not parse .NET time"
1161 {-# INLINE parseJSON #-}
2003 {-# INLINE parseJSON #-}
1162
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+
1163-------------------------------------------------------------------------------
2027-------------------------------------------------------------------------------
1164-- time
2028-- time
1165-------------------------------------------------------------------------------
2029-------------------------------------------------------------------------------
@@ -1167,53 +2031,211 @@ instance FromJSON DotNetTime where
1167instance FromJSON Day where
2031instance FromJSON Day where
1168 parseJSON = withText "Day" (Time.run Time.day)
2032 parseJSON = withText "Day" (Time.run Time.day)
1169
2033
2034+instance FromJSONKey Day where
2035+ fromJSONKey = FromJSONKeyTextParser (Time.run Time.day)
2036+
2037+
1170instance FromJSON TimeOfDay where
2038instance FromJSON TimeOfDay where
1171 parseJSON = withText "TimeOfDay" (Time.run Time.timeOfDay)
2039 parseJSON = withText "TimeOfDay" (Time.run Time.timeOfDay)
1172
2040
2041+instance FromJSONKey TimeOfDay where
2042+ fromJSONKey = FromJSONKeyTextParser (Time.run Time.timeOfDay)
2043+
2044+
1173instance FromJSON LocalTime where
2045instance FromJSON LocalTime where
1174 parseJSON = withText "LocalTime" (Time.run Time.localTime)
2046 parseJSON = withText "LocalTime" (Time.run Time.localTime)
1175
2047
2048+instance FromJSONKey LocalTime where
2049+ fromJSONKey = FromJSONKeyTextParser (Time.run Time.localTime)
2050+
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.
1176instance FromJSON ZonedTime where
2063instance FromJSON ZonedTime where
1177 parseJSON = withText "ZonedTime" (Time.run Time.zonedTime)
2064 parseJSON = withText "ZonedTime" (Time.run Time.zonedTime)
1178
2065
2066+instance FromJSONKey ZonedTime where
2067+ fromJSONKey = FromJSONKeyTextParser (Time.run Time.zonedTime)
2068+
2069+
1179instance FromJSON UTCTime where
2070instance FromJSON UTCTime where
1180 parseJSON = withText "UTCTime" (Time.run Time.utcTime)
2071 parseJSON = withText "UTCTime" (Time.run Time.utcTime)
1181
2072
1182−-- | /WARNING:/ Only parse lengths of time from trusted input
2073+instance FromJSONKey UTCTime where
2074+ fromJSONKey = FromJSONKeyTextParser (Time.run Time.utcTime)
2075+
2076+
2077+-- | This instance includes a bounds check to prevent maliciously
1183−-- 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
1184−-- system by specifying a scientific number with a big exponent like
2079+-- newtype 'Scientific' and provide your own instance using
1185−-- @1e1000000000@.
2080+-- 'withScientific' if you want to allow larger inputs.
1186instance FromJSON NominalDiffTime where
2081instance FromJSON NominalDiffTime where
1187− parseJSON = withScientific "NominalDiffTime" $ pure . realToFrac
2082+ parseJSON = withBoundedScientific "NominalDiffTime" $ pure . realToFrac
2083+ {-# INLINE parseJSON #-}
2084+
2085+
2086+-- | This instance includes a bounds check to prevent maliciously
2087+-- large inputs to fill up the memory of the target system. You can
2088+-- newtype 'Scientific' and provide your own instance using
2089+-- 'withScientific' if you want to allow larger inputs.
2090+instance FromJSON DiffTime where
2091+ parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
1188 {-# INLINE parseJSON #-}
2092 {-# INLINE parseJSON #-}
1189
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+
1190-------------------------------------------------------------------------------
2128-------------------------------------------------------------------------------
1191-- base Monoid/Semigroup
2129-- base Monoid/Semigroup
1192-------------------------------------------------------------------------------
2130-------------------------------------------------------------------------------
1193
2131
1194−instance FromJSON1 Dual where
2132+instance FromJSON1 Monoid.Dual where
1195− liftParseJSON p _ = fmap Dual . p
2133+ liftParseJSON p _ = fmap Monoid.Dual . p
1196 {-# INLINE liftParseJSON #-}
2134 {-# INLINE liftParseJSON #-}
1197
2135
1198−instance FromJSON a => FromJSON (Dual a) where
2136+instance FromJSON a => FromJSON (Monoid.Dual a) where
1199 parseJSON = parseJSON1
2137 parseJSON = parseJSON1
1200 {-# INLINE parseJSON #-}
2138 {-# INLINE parseJSON #-}
1201
2139
1202
2140
1203−instance FromJSON1 First where
2141+instance FromJSON1 Monoid.First where
1204− liftParseJSON p p' = fmap First . liftParseJSON p p'
2142+ liftParseJSON p p' = fmap Monoid.First . liftParseJSON p p'
1205 {-# INLINE liftParseJSON #-}
2143 {-# INLINE liftParseJSON #-}
1206
2144
1207−instance FromJSON a => FromJSON (First a) where
2145+instance FromJSON a => FromJSON (Monoid.First a) where
1208 parseJSON = parseJSON1
2146 parseJSON = parseJSON1
1209 {-# INLINE parseJSON #-}
2147 {-# INLINE parseJSON #-}
1210
2148
1211
2149
1212−instance FromJSON1 Last where
2150+instance FromJSON1 Monoid.Last where
1213− liftParseJSON p p' = fmap Last . liftParseJSON p p'
2151+ liftParseJSON p p' = fmap Monoid.Last . liftParseJSON p p'
1214 {-# INLINE liftParseJSON #-}
2152 {-# INLINE liftParseJSON #-}
1215
2153
1216−instance FromJSON a => FromJSON (Last a) where
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
2191+ {-# INLINE liftParseJSON #-}
2192+
2193+ liftParseJSONList _ p a = fmap Semigroup.First <$> p a
2194+ {-# INLINE liftParseJSONList #-}
2195+
2196+instance (FromJSON a) => FromJSON (Semigroup.First a) where
2197+ parseJSON = parseJSON1
2198+ {-# INLINE parseJSON #-}
2199+
2200+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2201+ {-# INLINE parseJSONList #-}
2202+
2203+
2204+instance FromJSON1 Semigroup.Last where
2205+ liftParseJSON p _ a = Semigroup.Last <$> p a
2206+ {-# INLINE liftParseJSON #-}
2207+
2208+ liftParseJSONList _ p a = fmap Semigroup.Last <$> p a
2209+ {-# INLINE liftParseJSONList #-}
2210+
2211+instance (FromJSON a) => FromJSON (Semigroup.Last a) where
2212+ parseJSON = parseJSON1
2213+ {-# INLINE parseJSON #-}
2214+
2215+ parseJSONList = liftParseJSONList parseJSON parseJSONList
2216+ {-# INLINE parseJSONList #-}
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+
2234+instance FromJSON1 Semigroup.Option where
2235+ liftParseJSON p p' = fmap Semigroup.Option . liftParseJSON p p'
2236+ {-# INLINE liftParseJSON #-}
2237+
2238+instance FromJSON a => FromJSON (Semigroup.Option a) where
1217 parseJSON = parseJSON1
2239 parseJSON = parseJSON1
1218 {-# INLINE parseJSON #-}
2240 {-# INLINE parseJSON #-}
1219
2241
@@ -1221,11 +2243,21 @@ instance FromJSON a => FromJSON (Last a) where
1221-- tagged
2243-- tagged
1222-------------------------------------------------------------------------------
2244-------------------------------------------------------------------------------
1223
2245
2246+instance FromJSON1 Proxy where
2247+ {-# INLINE liftParseJSON #-}
2248+ liftParseJSON _ _ = fromNull "Proxy" Proxy
2249+
1224instance FromJSON (Proxy a) where
2250instance FromJSON (Proxy a) where
1225 {-# INLINE parseJSON #-}
2251 {-# INLINE parseJSON #-}
1226− parseJSON Null = pure Proxy
2252+ parseJSON = fromNull "Proxy" Proxy
1227− parseJSON v = typeMismatch "Proxy" v
1228
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 #-}
1229
2261
1230instance FromJSON1 (Tagged a) where
2262instance FromJSON1 (Tagged a) where
1231 liftParseJSON p _ = fmap Tagged . p
2263 liftParseJSON p _ = fmap Tagged . p
@@ -1243,40 +2275,22 @@ instance FromJSONKey b => FromJSONKey (Tagged a b) where
1243-- Instances for converting from map keys
2275-- Instances for converting from map keys
1244-------------------------------------------------------------------------------
2276-------------------------------------------------------------------------------
1245
2277
1246−instance FromJSONKey Text where
1247− fromJSONKey = fromJSONKeyCoerce
1248−
1249−instance FromJSONKey Int where
1250− -- not sure if there if there is already a helper in
1251− -- aeson for doing this.
1252− fromJSONKey = FromJSONKeyTextParser $ \t -> case TR.decimal t of
1253− Left err -> fail err
1254− Right (v,t2) -> if T.null t2
1255− then return v
1256− else fail "Was not an integer, had extra stuff."
1257−
1258instance (FromJSON a, FromJSON b) => FromJSONKey (a,b)
2278instance (FromJSON a, FromJSON b) => FromJSONKey (a,b)
1259instance (FromJSON a, FromJSON b, FromJSON c) => FromJSONKey (a,b,c)
2279instance (FromJSON a, FromJSON b, FromJSON c) => FromJSONKey (a,b,c)
1260instance (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)
1261
2281
1262instance FromJSONKey Char where
2282instance FromJSONKey Char where
1263− fromJSONKey = FromJSONKeyTextParser $ \t ->
2283+ fromJSONKey = FromJSONKeyTextParser parseChar
1264− if T.length t == 1
1265− then return (T.index t 0)
1266− else typeMismatch "Expected Char but String didn't contain exactly one character" (String t)
1267 fromJSONKeyList = FromJSONKeyText T.unpack
2284 fromJSONKeyList = FromJSONKeyText T.unpack
1268
2285
1269instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
2286instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where
1270 fromJSONKey = fromJSONKeyList
2287 fromJSONKey = fromJSONKeyList
1271
2288
1272−instance (FromJSONKey a, FromJSON a) => FromJSONKey (Identity a) where
1273− fromJSONKey = mapFromJSONKeyFunction Identity fromJSONKey
1274−
1275-------------------------------------------------------------------------------
2289-------------------------------------------------------------------------------
1276-- Tuple instances, see tuple-instances-from.hs
2290-- Tuple instances, see tuple-instances-from.hs
1277-------------------------------------------------------------------------------
2291-------------------------------------------------------------------------------
1278
2292
1279−instance FromJSON2 ((,) ) where
2293+instance FromJSON2 (,) where
1280 liftParseJSON2 pA _ pB _ = withArray "(a, b)" $ \t ->
2294 liftParseJSON2 pA _ pB _ = withArray "(a, b)" $ \t ->
1281 let n = V.length t
2295 let n = V.length t
1282 in if n == 2
2296 in if n == 2
Data/Aeson/Types/Generic.hs+49−15
@@ -1,13 +1,14 @@
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 #-}
11
12
12#include "overlapping-compat.h"
13#include "overlapping-compat.h"
13
14
@@ -23,19 +24,29 @@
23--
24--
24-- Helpers for generic derivations.
25-- Helpers for generic derivations.
25
26
26−module Data.Aeson.Types.Generic where
27+module Data.Aeson.Types.Generic
28+ (
29+ IsRecord
30+ , AllNullary
31+ , Tagged2(..)
32+ , True
33+ , False
34+ , And
35+ , Zero
36+ , One
37+ , ProductSize(..)
38+ , (:*)(..)
39+ ) where
40+
41+import Prelude.Compat
27
42
28import GHC.Generics
43import GHC.Generics
29
44
30--------------------------------------------------------------------------------
45--------------------------------------------------------------------------------
31
46
32class IsRecord (f :: * -> *) isRecord | f -> isRecord
47class IsRecord (f :: * -> *) isRecord | f -> isRecord
33− where
34− isUnary :: f a -> Bool
35− isUnary = const True
36
48
37instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
49instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
38− where isUnary = const False
39#if MIN_VERSION_base(4,9,0)
50#if MIN_VERSION_base(4,9,0)
40instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
51instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
41#else
52#else
@@ -43,22 +54,30 @@ instance OVERLAPPING_ IsRecord (M1 S NoSelector f) False
43#endif
54#endif
44instance (IsRecord f isRecord) => IsRecord (M1 S c f) isRecord
55instance (IsRecord f isRecord) => IsRecord (M1 S c f) isRecord
45instance IsRecord (K1 i c) True
56instance IsRecord (K1 i c) True
57+instance IsRecord Par1 True
58+instance IsRecord (Rec1 f) True
59+instance IsRecord (f :.: g) True
46instance IsRecord U1 False
60instance IsRecord U1 False
47− where isUnary = const False
48
61
49--------------------------------------------------------------------------------
62--------------------------------------------------------------------------------
50
63
51class AllNullary (f :: * -> *) allNullary | f -> allNullary
64class AllNullary (f :: * -> *) allNullary | f -> allNullary
65+
52instance ( AllNullary a allNullaryL
66instance ( AllNullary a allNullaryL
53 , AllNullary b allNullaryR
67 , AllNullary b allNullaryR
54 , And allNullaryL allNullaryR allNullary
68 , And allNullaryL allNullaryR allNullary
55 ) => AllNullary (a :+: b) allNullary
69 ) => AllNullary (a :+: b) allNullary
56instance AllNullary a allNullary => AllNullary (M1 i c a) allNullary
70instance AllNullary a allNullary => AllNullary (M1 i c a) allNullary
57instance AllNullary (a :*: b) False
71instance AllNullary (a :*: b) False
72+instance AllNullary (a :.: b) False
58instance AllNullary (K1 i c) False
73instance AllNullary (K1 i c) False
74+instance AllNullary Par1 False
75+instance AllNullary (Rec1 f) False
59instance AllNullary U1 True
76instance AllNullary U1 True
60
77
61newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
78newtype Tagged2 (s :: * -> *) b = Tagged2 {unTagged2 :: b}
79+ deriving Functor
80+
62--------------------------------------------------------------------------------
81--------------------------------------------------------------------------------
63
82
64data True
83data True
@@ -73,6 +92,14 @@ instance And True False False
73
92
74--------------------------------------------------------------------------------
93--------------------------------------------------------------------------------
75
94
95+-- | A type-level indicator that 'ToJSON' or 'FromJSON' is being derived generically.
96+data Zero
97+
98+-- | A type-level indicator that 'ToJSON1' or 'FromJSON1' is being derived generically.
99+data One
100+
101+--------------------------------------------------------------------------------
102+
76class ProductSize f where
103class ProductSize f where
77 productSize :: Tagged2 f Int
104 productSize :: Tagged2 f Int
78
105
@@ -82,3 +109,10 @@ instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
82
109
83instance ProductSize (S1 s a) where
110instance ProductSize (S1 s a) where
84 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+195−32
@@ -1,5 +1,9 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveGeneric #-}
1−{-# LANGUAGE CPP, DeriveDataTypeable, GeneralizedNewtypeDeriving, Rank2Types,
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2− RecordWildCards #-}
5+{-# LANGUAGE NoImplicitPrelude #-}
6+{-# LANGUAGE Rank2Types #-}
3#if __GLASGOW_HASKELL__ >= 800
7#if __GLASGOW_HASKELL__ >= 800
4-- a) THQ works on cross-compilers and unregisterised GHCs
8-- a) THQ works on cross-compilers and unregisterised GHCs
5-- 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)
@@ -41,16 +45,33 @@ module Data.Aeson.Types.Internal
41 , parseEither
45 , parseEither
42 , parseMaybe
46 , parseMaybe
43 , modifyFailure
47 , modifyFailure
48+ , prependFailure
49+ , parserThrowError
50+ , parserCatchError
44 , formatError
51 , formatError
52+ , formatPath
53+ , formatRelativePath
45 , (<?>)
54 , (<?>)
46 -- * Constructors and accessors
55 -- * Constructors and accessors
47 , object
56 , object
48
57
49 -- * Generic and TH encoding configuration
58 -- * Generic and TH encoding configuration
50− , Options(..)
59+ , Options(
60+ fieldLabelModifier
61+ , constructorTagModifier
62+ , allNullaryToStringTag
63+ , omitNothingFields
64+ , sumEncoding
65+ , unwrapUnaryRecords
66+ , tagSingleConstructors
67+ , rejectUnknownFields
68+ )
69+
51 , SumEncoding(..)
70 , SumEncoding(..)
71+ , JSONKeyOptions(keyModifier)
52 , defaultOptions
72 , defaultOptions
53 , defaultTaggedObject
73 , defaultTaggedObject
74+ , defaultJSONKeyOptions
54
75
55 -- * Used for changing CamelCase names into something else.
76 -- * Used for changing CamelCase names into something else.
56 , camelTo
77 , camelTo
@@ -60,35 +81,34 @@ module Data.Aeson.Types.Internal
60 , DotNetTime(..)
81 , DotNetTime(..)
61 ) where
82 ) where
62
83
84+import Prelude.Compat
85+
86+import Control.Applicative (Alternative(..))
63import Control.Arrow (first)
87import Control.Arrow (first)
64−import Control.Applicative
65import Control.DeepSeq (NFData(..))
88import Control.DeepSeq (NFData(..))
66import Control.Monad (MonadPlus(..), ap)
89import Control.Monad (MonadPlus(..), ap)
67−import qualified Control.Monad.Fail as Fail
68import Data.Char (isLower, isUpper, toLower, isAlpha, isAlphaNum)
90import Data.Char (isLower, isUpper, toLower, isAlpha, isAlphaNum)
69import Data.Data (Data)
91import Data.Data (Data)
70import Data.Foldable (foldl')
92import Data.Foldable (foldl')
71import Data.HashMap.Strict (HashMap)
93import Data.HashMap.Strict (HashMap)
72import Data.Hashable (Hashable(..))
94import Data.Hashable (Hashable(..))
73−import Data.Semigroup (Semigroup((<>)))
95+import Data.List (intercalate)
74import Data.Scientific (Scientific)
96import Data.Scientific (Scientific)
97+import Data.Semigroup (Semigroup((<>)))
75import Data.String (IsString(..))
98import Data.String (IsString(..))
76import Data.Text (Text, pack, unpack)
99import Data.Text (Text, pack, unpack)
77import Data.Time (UTCTime)
100import Data.Time (UTCTime)
78import Data.Time.Format (FormatTime)
101import Data.Time.Format (FormatTime)
79import Data.Typeable (Typeable)
102import Data.Typeable (Typeable)
80import Data.Vector (Vector)
103import Data.Vector (Vector)
104+import GHC.Generics (Generic)
105+import qualified Control.Monad as Monad
106+import qualified Control.Monad.Fail as Fail
81import qualified Data.HashMap.Strict as H
107import qualified Data.HashMap.Strict as H
82import qualified Data.Scientific as S
108import qualified Data.Scientific as S
83import qualified Data.Vector as V
109import qualified Data.Vector as V
84import qualified Language.Haskell.TH.Syntax as TH
110import qualified Language.Haskell.TH.Syntax as TH
85
111
86−#if !MIN_VERSION_base(4,8,0)
87−import Data.Foldable (Foldable(..))
88−import Data.Monoid (Monoid(..))
89−import Data.Traversable (Traversable(..))
90−#endif
91−
92#if !MIN_VERSION_unordered_containers(0,2,6)
112#if !MIN_VERSION_unordered_containers(0,2,6)
93import Data.List (sort)
113import Data.List (sort)
94#endif
114#endif
@@ -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 =
@@ -552,6 +660,20 @@ data SumEncoding =
552 -- by the encoded value of that field! If the constructor is not a
660 -- by the encoded value of that field! If the constructor is not a
553 -- record the encoded constructor contents will be stored under
661 -- record the encoded constructor contents will be stored under
554 -- the 'contentsFieldName' field.
662 -- the 'contentsFieldName' field.
663+ | UntaggedValue
664+ -- ^ Constructor names won't be encoded. Instead only the contents of the
665+ -- constructor will be encoded as if the type had a single constructor. JSON
666+ -- encodings have to be disjoint for decoding to work properly.
667+ --
668+ -- When decoding, constructors are tried in the order of definition. If some
669+ -- encodings overlap, the first one defined will succeed.
670+ --
671+ -- /Note:/ Nullary constructors are encoded as strings (using
672+ -- 'constructorTagModifier'). Having a nullary constructor alongside a
673+ -- single field constructor that encodes to a string leads to ambiguity.
674+ --
675+ -- /Note:/ Only the last error is kept when decoding, so in the case of
676+ -- malformed JSON, only an error for the last constructor will be reported.
555 | ObjectWithSingleField
677 | ObjectWithSingleField
556 -- ^ A constructor will be encoded to an object with a single
678 -- ^ A constructor will be encoded to an object with a single
557 -- field named after the constructor tag (modified by the
679 -- field named after the constructor tag (modified by the
@@ -564,6 +686,33 @@ data SumEncoding =
564 -- contents of the constructor.
686 -- contents of the constructor.
565 deriving (Eq, Show)
687 deriving (Eq, Show)
566
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+
567-- | Default encoding 'Options':
716-- | Default encoding 'Options':
568--
717--
569-- @
718-- @
@@ -574,6 +723,8 @@ data SumEncoding =
574-- , 'omitNothingFields' = False
723-- , 'omitNothingFields' = False
575-- , 'sumEncoding' = 'defaultTaggedObject'
724-- , 'sumEncoding' = 'defaultTaggedObject'
576-- , 'unwrapUnaryRecords' = False
725-- , 'unwrapUnaryRecords' = False
726+-- , 'tagSingleConstructors' = False
727+-- , 'rejectUnknownFields' = False
577-- }
728-- }
578-- @
729-- @
579defaultOptions :: Options
730defaultOptions :: Options
@@ -584,6 +735,8 @@ defaultOptions = Options
584 , omitNothingFields = False
735 , omitNothingFields = False
585 , sumEncoding = defaultTaggedObject
736 , sumEncoding = defaultTaggedObject
586 , unwrapUnaryRecords = False
737 , unwrapUnaryRecords = False
738+ , tagSingleConstructors = False
739+ , rejectUnknownFields = False
587 }
740 }
588
741
589-- | Default 'TaggedObject' 'SumEncoding' options:
742-- | Default 'TaggedObject' 'SumEncoding' options:
@@ -600,6 +753,16 @@ defaultTaggedObject = TaggedObject
600 , contentsFieldName = "contents"
753 , contentsFieldName = "contents"
601 }
754 }
602
755
756+-- | Default 'JSONKeyOptions':
757+--
758+-- @
759+-- defaultJSONKeyOptions = 'JSONKeyOptions'
760+-- { 'keyModifier' = 'id'
761+-- }
762+-- @
763+defaultJSONKeyOptions :: JSONKeyOptions
764+defaultJSONKeyOptions = JSONKeyOptions id
765+
603-- | Converts from CamelCase to another lower case, interspersing
766-- | Converts from CamelCase to another lower case, interspersing
604-- the character between all capital letters and their previous
767-- the character between all capital letters and their previous
605-- entries, except those capital letters that appear together,
768-- entries, except those capital letters that appear together,
Data/Aeson/Types/ToJSON.hs+1528−755
@@ -1,29 +1,27 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE EmptyDataDecls #-}
4−{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoImplicitPrelude #-}
8−{-# LANGUAGE NamedFieldPuns #-}
9−{-# LANGUAGE OverloadedStrings #-}
9+{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE PatternGuards #-}
10+{-# LANGUAGE PatternGuards #-}
11+{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE RecordWildCards #-}
12+{-# LANGUAGE RecordWildCards #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE TypeOperators #-}
14+{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE TypeSynonymInstances #-}
15−{-# LANGUAGE UndecidableInstances #-}
15+{-# LANGUAGE UndecidableInstances #-}
16−
17−#if __GLASGOW_HASKELL__ >= 706
18−{-# LANGUAGE PolyKinds #-}
19−#endif
20
16
21#include "overlapping-compat.h"
17#include "overlapping-compat.h"
18+#include "incoherent-compat.h"
22
19
23-- TODO: Drop this when we remove support for Data.Attoparsec.Number
20-- TODO: Drop this when we remove support for Data.Attoparsec.Number
24{-# OPTIONS_GHC -fno-warn-deprecations #-}
21{-# OPTIONS_GHC -fno-warn-deprecations #-}
25
22
26−module Data.Aeson.Types.ToJSON (
23+module Data.Aeson.Types.ToJSON
24+ (
27 -- * Core JSON classes
25 -- * Core JSON classes
28 ToJSON(..)
26 ToJSON(..)
29 -- * Liftings to unary and binary type constructors
27 -- * Liftings to unary and binary type constructors
@@ -35,95 +33,115 @@ module Data.Aeson.Types.ToJSON (
35 , toEncoding2
33 , toEncoding2
36 -- * Generic JSON classes
34 -- * Generic JSON classes
37 , GToJSON(..)
35 , GToJSON(..)
38− , GToEncoding(..)
36+ , ToArgs(..)
39 , genericToJSON
37 , genericToJSON
40 , genericToEncoding
38 , genericToEncoding
39+ , genericLiftToJSON
40+ , genericLiftToEncoding
41 -- * Classes and types for map keys
41 -- * Classes and types for map keys
42 , ToJSONKey(..)
42 , ToJSONKey(..)
43 , ToJSONKeyFunction(..)
43 , ToJSONKeyFunction(..)
44+ , toJSONKeyText
44 , contramapToJSONKeyFunction
45 , contramapToJSONKeyFunction
46+
47+ , GToJSONKey()
48+ , genericToJSONKey
49+
45 -- * Object key-value pairs
50 -- * Object key-value pairs
46 , KeyValue(..)
51 , KeyValue(..)
52+ , KeyValuePair(..)
53+ , FromPairs(..)
47 -- * Functions needed for documentation
54 -- * Functions needed for documentation
48 -- * Encoding functions
55 -- * Encoding functions
49 , listEncoding
56 , listEncoding
50 , listValue
57 , listValue
51 ) where
58 ) where
52
59
53−import Prelude ()
54import Prelude.Compat
60import Prelude.Compat
55
61
62+import Control.Applicative (Const(..))
63+import Control.Monad.ST (ST)
56−import Data.Aeson.Encoding.Internal (Encoding (..), Series, dict, emptyArray_,
64+import Data.Aeson.Encoding (Encoding, Encoding', Series, dict, emptyArray_)
57− tuple, (>*<))
65+import Data.Aeson.Encoding.Internal ((>*<))
58−import Data.Aeson.Functions (mapHashKeyVal, mapKeyVal)
66+import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
59−import Data.Aeson.Types.Generic
67+import Data.Aeson.Types.Generic (AllNullary, False, IsRecord, One, ProductSize, Tagged2(..), True, Zero, productSize)
60import Data.Aeson.Types.Internal
68import Data.Aeson.Types.Internal
61−
62−import qualified Data.Aeson.Encode.Builder as EB
63−import qualified Data.Aeson.Encoding.Internal as E
64−
65−import Control.Applicative (Const (..))
66−import Control.Monad.ST (ST)
67−import Data.Attoparsec.Number (Number (..))
69+import Data.Attoparsec.Number (Number(..))
68−import Data.Bits (unsafeShiftR)
70+import Data.Bits (unsafeShiftR)
69−import Data.DList (DList)
71+import Data.DList (DList)
70−import Data.Fixed (Fixed, HasResolution)
72+import Data.Fixed (Fixed, HasResolution, Nano)
71−import Data.Foldable (toList)
73+import Data.Foldable (toList)
72−import Data.Functor.Identity (Identity (..))
74+import Data.Functor.Compose (Compose(..))
75+import Data.Functor.Contravariant (Contravariant (..))
76+import Data.Functor.Identity (Identity(..))
77+import Data.Functor.Product (Product(..))
78+import Data.Functor.Sum (Sum(..))
73−import Data.Int (Int16, Int32, Int64, Int8)
79+import Data.Int (Int16, Int32, Int64, Int8)
74−import Data.List (intersperse)
80+import Data.List (intersperse)
75−import Data.List.NonEmpty (NonEmpty (..))
81+import Data.List.NonEmpty (NonEmpty(..))
76−import Data.Monoid (Dual (..), First (..), Last (..), (<>))
82+import Data.Semigroup ((<>))
77−import Data.Proxy (Proxy (..))
83+import Data.Proxy (Proxy(..))
78−import Data.Ratio (Ratio, denominator, numerator)
84+import Data.Ratio (Ratio, denominator, numerator)
79−import Data.Scientific (Scientific)
85+import Data.Scientific (Scientific)
80−import Data.Tagged (Tagged (..))
86+import Data.Tagged (Tagged(..))
81−import Data.Text (Text, pack)
87+import Data.Text (Text, pack)
82−import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay,
88+import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
83− 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 (..))
84−import Data.Time.Format (FormatTime, formatTime)
92+import Data.Time.Format.Compat (FormatTime, formatTime, defaultTimeLocale)
85−import Data.Time.Locale.Compat (defaultTimeLocale)
86−import Data.Vector (Vector)
93+import Data.Vector (Vector)
87−import Data.Version (Version, showVersion)
94+import Data.Version (Version, showVersion)
95+import Data.Void (Void, absurd)
88−import Data.Word (Word16, Word32, Word64, Word8)
96+import Data.Word (Word16, Word32, Word64, Word8)
89−import Foreign.Storable (Storable)
97+import Foreign.Storable (Storable)
98+import Foreign.C.Types (CTime (..))
90import GHC.Generics
99import GHC.Generics
91−import Numeric.Natural (Natural)
100+import Numeric.Natural (Natural)
92−
101+import qualified Data.Aeson.Encoding as E
93−import qualified Data.ByteString.Builder as B
102+import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
94−import qualified Data.ByteString.Lazy as L
103+import qualified Data.ByteString.Lazy as L
95−import qualified Data.DList as DList
104+import qualified Data.DList as DList
96−import qualified Data.HashMap.Strict as H
105+import qualified Data.HashMap.Strict as H
97−import qualified Data.HashSet as HashSet
106+import qualified Data.HashSet as HashSet
98−import qualified Data.IntMap as IntMap
107+import qualified Data.IntMap as IntMap
99−import qualified Data.IntSet as IntSet
108+import qualified Data.IntSet as IntSet
100−import qualified Data.List.NonEmpty as NE
109+import qualified Data.List.NonEmpty as NE
101−import qualified Data.Map as M
110+import qualified Data.Map as M
111+import qualified Data.Monoid as Monoid
102−import qualified Data.Scientific as Scientific
112+import qualified Data.Scientific as Scientific
103−import qualified Data.Sequence as Seq
113+import qualified Data.Semigroup as Semigroup
114+import qualified Data.Sequence as Seq
104−import qualified Data.Set as Set
115+import qualified Data.Set as Set
105−import qualified Data.Text as T
116+import qualified Data.Text as T
106−import qualified Data.Text.Encoding as T
117+import qualified Data.Text.Encoding as T
107−import qualified Data.Text.Lazy as LT
118+import qualified Data.Text.Lazy as LT
108−import qualified Data.Text.Lazy.Builder as LTB
119+import qualified Data.Tree as Tree
109−import qualified Data.Text.Lazy.Builder.Int as LTBI
120+import qualified Data.UUID.Types as UUID
110−import qualified Data.Tree as Tree
111−import qualified Data.Vector as V
121+import qualified Data.Vector as V
112−import qualified Data.Vector.Generic as VG
122+import qualified Data.Vector.Generic as VG
113−import qualified Data.Vector.Mutable as VM
123+import qualified Data.Vector.Mutable as VM
114−import qualified Data.Vector.Primitive as VP
124+import qualified Data.Vector.Primitive as VP
115−import qualified Data.Vector.Storable as VS
125+import qualified Data.Vector.Storable as VS
116−import qualified Data.Vector.Unboxed as VU
126+import qualified Data.Vector.Unboxed as VU
117−
127+
118−builder :: ToJSON a => a -> B.Builder
128+import qualified Data.Aeson.Encoding.Builder as EB
119−builder = fromEncoding . toEncoding
129+import qualified Data.ByteString.Builder as B
130+
131+import qualified GHC.Exts as Exts
132+import qualified Data.Primitive.Array as PM
133+import qualified Data.Primitive.SmallArray as PM
134+import qualified Data.Primitive.Types as PM
135+
136+#if MIN_VERSION_primitive(0,6,4)
137+#if !MIN_VERSION_primitive(0,7,0)
138+import qualified Data.Primitive.UnliftedArray as PM
139+#endif
140+import qualified Data.Primitive.PrimArray as PM
141+#endif
120
142
121toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
143toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
122−toJSONPair keySerialiser valSerializer (a, b) = Array $ V.create $ do
144+toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
123− mv <- VM.unsafeNew 2
124− VM.unsafeWrite mv 0 (keySerialiser a)
125− VM.unsafeWrite mv 1 (valSerializer b)
126− return mv
127{-# INLINE toJSONPair #-}
145{-# INLINE toJSONPair #-}
128
146
129realFloatToJSON :: RealFloat a => a -> Value
147realFloatToJSON :: RealFloat a => a -> Value
@@ -132,41 +150,59 @@ realFloatToJSON d
132 | otherwise = Number $ Scientific.fromFloatDigits d
150 | otherwise = Number $ Scientific.fromFloatDigits d
133{-# INLINE realFloatToJSON #-}
151{-# INLINE realFloatToJSON #-}
134
152
135−realFloatToEncoding :: RealFloat a => a -> Encoding
136−realFloatToEncoding d
137− | isNaN d || isInfinite d = Encoding EB.null_
138− | otherwise = toEncoding (Scientific.fromFloatDigits d)
139−{-# INLINE realFloatToEncoding #-}
140-------------------------------------------------------------------------------
153-------------------------------------------------------------------------------
141-- Generics
154-- Generics
142-------------------------------------------------------------------------------
155-------------------------------------------------------------------------------
143
156
144−-- | Class of generic representation types ('Rep') that can be converted to
157+-- | Class of generic representation types that can be converted to
145-- JSON.
158-- JSON.
146−class GToJSON 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'.
161+ -- default generic implementation of 'toJSON'
162+ -- (with @enc ~ 'Value'@ and @arity ~ 'Zero'@)
163+ -- and 'liftToJSON' (if the @arity@ is 'One').
164+ --
165+ -- It also provides a generic implementation of 'toEncoding'
166+ -- (with @enc ~ 'Encoding'@ and @arity ~ 'Zero'@)
167+ -- and 'liftToEncoding' (if the @arity@ is 'One').
149− gToJSON :: Options -> f a -> Value
168+ gToJSON :: Options -> ToArgs enc arity a -> f a -> enc
150−
169+
151−-- | Class of generic representation types ('Rep') that can be converted to
170+-- | A 'ToArgs' value either stores nothing (for 'ToJSON') or it stores the two
152−-- a JSON 'Encoding'.
171+-- function arguments that encode occurrences of the type parameter (for
153−class GToEncoding f where
172+-- 'ToJSON1').
154− -- | This method (applied to 'defaultOptions') can be used as the
173+data ToArgs res arity a where
155− -- default generic implementation of 'toEncoding'.
174+ NoToArgs :: ToArgs res Zero a
156− gToEncoding :: Options -> f a -> Encoding
175+ To1Args :: (a -> res) -> ([a] -> res) -> ToArgs res One a
157−
158
176
159-- | A configurable generic JSON creator. This function applied to
177-- | A configurable generic JSON creator. This function applied to
160-- 'defaultOptions' is used as the default for 'toJSON' when the type
178-- 'defaultOptions' is used as the default for 'toJSON' when the type
161-- is an instance of 'Generic'.
179-- is an instance of 'Generic'.
162−genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value
180+genericToJSON :: (Generic a, GToJSON Value Zero (Rep a))
181+ => Options -> a -> Value
163−genericToJSON opts = gToJSON opts . from
182+genericToJSON opts = gToJSON opts NoToArgs . from
183+
184+-- | A configurable generic JSON creator. This function applied to
185+-- 'defaultOptions' is used as the default for 'liftToJSON' when the type
186+-- is an instance of 'Generic1'.
187+genericLiftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))
188+ => Options -> (a -> Value) -> ([a] -> Value)
189+ -> f a -> Value
190+genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1
164
191
165-- | A configurable generic JSON encoder. This function applied to
192-- | A configurable generic JSON encoder. This function applied to
166-- 'defaultOptions' is used as the default for 'toEncoding' when the type
193-- 'defaultOptions' is used as the default for 'toEncoding' when the type
167-- is an instance of 'Generic'.
194-- is an instance of 'Generic'.
168−genericToEncoding :: (Generic a, GToEncoding (Rep a)) => Options -> a -> Encoding
195+genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a))
196+ => Options -> a -> Encoding
169−genericToEncoding opts = gToEncoding opts . from
197+genericToEncoding opts = gToJSON opts NoToArgs . from
198+
199+-- | A configurable generic JSON encoder. This function applied to
200+-- 'defaultOptions' is used as the default for 'liftToEncoding' when the type
201+-- is an instance of 'Generic1'.
202+genericLiftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))
203+ => Options -> (a -> Encoding) -> ([a] -> Encoding)
204+ -> f a -> Encoding
205+genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1
170
206
171-------------------------------------------------------------------------------
207-------------------------------------------------------------------------------
172-- Class
208-- Class
@@ -174,6 +210,9 @@ genericToEncoding opts = gToEncoding opts . from
174
210
175-- | A type that can be converted to JSON.
211-- | A type that can be converted to JSON.
176--
212--
213+-- Instances in general /must/ specify 'toJSON' and /should/ (but don't need
214+-- to) specify 'toEncoding'.
215+--
177-- An example type and instance:
216-- An example type and instance:
178--
217--
179-- @
218-- @
@@ -182,10 +221,10 @@ genericToEncoding opts = gToEncoding opts . from
182--
221--
183-- data Coord = Coord { x :: Double, y :: Double }
222-- data Coord = Coord { x :: Double, y :: Double }
184--
223--
185−-- instance ToJSON Coord where
224+-- instance 'ToJSON' Coord where
186−-- toJSON (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
225+-- 'toJSON' (Coord x y) = 'object' [\"x\" '.=' x, \"y\" '.=' y]
187--
226--
188−-- toEncoding (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
227+-- 'toEncoding' (Coord x y) = 'pairs' (\"x\" '.=' x '<>' \"y\" '.=' y)
189-- @
228-- @
190--
229--
191-- Instead of manually writing your 'ToJSON' instance, there are two options
230-- Instead of manually writing your 'ToJSON' instance, there are two options
@@ -193,17 +232,15 @@ genericToEncoding opts = gToEncoding opts . from
193--
232--
194-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
233-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
195-- 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
196−-- so will probably be more efficient than the following two options:
235+-- so it will probably be more efficient than the following option.
197--
236--
198-- * The compiler can provide a default generic implementation for
237-- * The compiler can provide a default generic implementation for
199-- 'toJSON'.
238-- 'toJSON'.
200--
239--
201-- 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
202−-- datatype and declare a 'ToJSON' instance for your datatype without giving
241+-- datatype and declare a 'ToJSON' instance. If you require nothing other than
203−-- definitions for 'toJSON' or 'toEncoding'.
242+-- 'defaultOptions', it is sufficient to write (and this is the only
204−--
243+-- alternative where the default 'toJSON' implementation is sufficient):
205−-- For example, the previous example can be simplified to a more
206−-- minimal instance:
207--
244--
208-- @
245-- @
209-- {-\# LANGUAGE DeriveGeneric \#-}
246-- {-\# LANGUAGE DeriveGeneric \#-}
@@ -212,35 +249,46 @@ genericToEncoding opts = gToEncoding opts . from
212--
249--
213-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
250-- data Coord = Coord { x :: Double, y :: Double } deriving 'Generic'
214--
251--
215−-- instance ToJSON Coord where
252+-- instance 'ToJSON' Coord where
216−-- toEncoding = 'genericToEncoding' 'defaultOptions'
253+-- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
217-- @
254-- @
218--
255--
219−-- 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
220−-- 'toEncoding' function is a relatively new addition to this class.
257+-- to implement both methods:
221−-- To allow users of older versions of this library to upgrade without
222−-- having to edit all of their instances or encounter surprising
223−-- incompatibilities, the default implementation of 'toEncoding' uses
224−-- 'toJSON'. This produces correct results, but since it performs an
225−-- intermediate conversion to a 'Value', it will be less efficient
226−-- than directly emitting an 'Encoding'. Our one-liner definition of
227−-- 'toEncoding' above bypasses the intermediate 'Value'.
228−--
229−-- If @DefaultSignatures@ doesn't give exactly the results you want,
230−-- you can customize the generic encoding with only a tiny amount of
231−-- effort, using 'genericToJSON' and 'genericToEncoding' with your
232−-- preferred 'Options':
233--
258--
234-- @
259-- @
260+-- customOptions = 'defaultOptions'
261+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
262+-- }
263+--
235−-- instance ToJSON Coord where
264+-- instance 'ToJSON' Coord where
236−-- toJSON = 'genericToJSON' 'defaultOptions'
265+-- 'toJSON' = 'genericToJSON' customOptions
237−-- toEncoding = 'genericToEncoding' 'defaultOptions'
266+-- 'toEncoding' = 'genericToEncoding' customOptions
238-- @
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.)
239class ToJSON a where
287class ToJSON a where
240 -- | Convert a Haskell value to a JSON-friendly intermediate type.
288 -- | Convert a Haskell value to a JSON-friendly intermediate type.
241 toJSON :: a -> Value
289 toJSON :: a -> Value
242
290
243− default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
291+ default toJSON :: (Generic a, GToJSON Value Zero (Rep a)) => a -> Value
244 toJSON = genericToJSON defaultOptions
292 toJSON = genericToJSON defaultOptions
245
293
246 -- | Encode a Haskell value as JSON.
294 -- | Encode a Haskell value as JSON.
@@ -257,12 +305,12 @@ class ToJSON a where
257 -- extension, and then have GHC generate a method body as follows.
305 -- extension, and then have GHC generate a method body as follows.
258 --
306 --
259 -- @
307 -- @
260− -- instance ToJSON Coord where
308+ -- instance 'ToJSON' Coord where
261− -- toEncoding = 'genericToEncoding' 'defaultOptions'
309+ -- 'toEncoding' = 'genericToEncoding' 'defaultOptions'
262 -- @
310 -- @
263
311
264 toEncoding :: a -> Encoding
312 toEncoding :: a -> Encoding
265− toEncoding = E.Encoding . EB.encodeToBuilder . toJSON
313+ toEncoding = E.value . toJSON
266 {-# INLINE toEncoding #-}
314 {-# INLINE toEncoding #-}
267
315
268 toJSONList :: [a] -> Value
316 toJSONList :: [a] -> Value
@@ -283,46 +331,280 @@ class KeyValue kv where
283 infixr 8 .=
331 infixr 8 .=
284
332
285instance KeyValue Series where
333instance KeyValue Series where
286− name .= value = E.Value $ E.text name <> E.colon <> toEncoding value
334+ name .= value = E.pair name (toEncoding value)
287 {-# INLINE (.=) #-}
335 {-# INLINE (.=) #-}
288
336
289instance KeyValue Pair where
337instance KeyValue Pair where
290 name .= value = (name, toJSON value)
338 name .= value = (name, toJSON value)
291 {-# INLINE (.=) #-}
339 {-# INLINE (.=) #-}
292
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+
293-------------------------------------------------------------------------------
348-------------------------------------------------------------------------------
294-- Classes and types for map keys
349-- Classes and types for map keys
295-------------------------------------------------------------------------------
350-------------------------------------------------------------------------------
296
351
352+-- | Typeclass for types that can be used as the key of a map-like container
353+-- (like 'Map' or 'HashMap'). For example, since 'Text' has a 'ToJSONKey'
354+-- instance and 'Char' has a 'ToJSON' instance, we can encode a value of
355+-- type 'Map' 'Text' 'Char':
356+--
357+-- >>> LBC8.putStrLn $ encode $ Map.fromList [("foo" :: Text, 'a')]
358+-- {"foo":"a"}
359+--
360+-- Since 'Int' also has a 'ToJSONKey' instance, we can similarly write:
361+--
362+-- >>> LBC8.putStrLn $ encode $ Map.fromList [(5 :: Int, 'a')]
363+-- {"5":"a"}
364+--
365+-- JSON documents only accept strings as object keys. For any type
366+-- from @base@ that has a natural textual representation, it can be
367+-- expected that its 'ToJSONKey' instance will choose that representation.
368+--
369+-- For data types that lack a natural textual representation, an alternative
370+-- is provided. The map-like container is represented as a JSON array
371+-- instead of a JSON object. Each value in the array is an array with
372+-- exactly two values. The first is the key and the second is the value.
373+--
374+-- For example, values of type '[Text]' cannot be encoded to a
375+-- string, so a 'Map' with keys of type '[Text]' is encoded as follows:
376+--
377+-- >>> LBC8.putStrLn $ encode $ Map.fromList [(["foo","bar","baz" :: Text], 'a')]
378+-- [[["foo","bar","baz"],"a"]]
379+--
380+-- The default implementation of 'ToJSONKey' chooses this method of
381+-- encoding a key, using the 'ToJSON' instance of the type.
382+--
383+-- To use your own data type as the key in a map, all that is needed
384+-- is to write a 'ToJSONKey' (and possibly a 'FromJSONKey') instance
385+-- for it. If the type cannot be trivially converted to and from 'Text',
386+-- it is recommended that 'ToJSONKeyValue' is used. Since the default
387+-- implementations of the typeclass methods can build this from a
388+-- 'ToJSON' instance, there is nothing that needs to be written:
389+--
390+-- > data Foo = Foo { fooAge :: Int, fooName :: Text }
391+-- > deriving (Eq,Ord,Generic)
392+-- > instance ToJSON Foo
393+-- > instance ToJSONKey Foo
394+--
395+-- That's it. We can now write:
396+--
397+-- >>> let m = Map.fromList [(Foo 4 "bar",'a'),(Foo 6 "arg",'b')]
398+-- >>> LBC8.putStrLn $ encode m
399+-- [[{"fooName":"bar","fooAge":4},"a"],[{"fooName":"arg","fooAge":6},"b"]]
400+--
401+-- The next case to consider is if we have a type that is a
402+-- newtype wrapper around 'Text'. The recommended approach is to use
403+-- generalized newtype deriving:
404+--
405+-- > newtype RecordId = RecordId { getRecordId :: Text }
406+-- > deriving (Eq,Ord,ToJSONKey)
407+--
408+-- Then we may write:
409+--
410+-- >>> LBC8.putStrLn $ encode $ Map.fromList [(RecordId "abc",'a')]
411+-- {"abc":"a"}
412+--
413+-- Simple sum types are a final case worth considering. Suppose we have:
414+--
415+-- > data Color = Red | Green | Blue
416+-- > deriving (Show,Read,Eq,Ord)
417+--
418+-- It is possible to get the 'ToJSONKey' instance for free as we did
419+-- with 'Foo'. However, in this case, we have a natural way to go to
420+-- and from 'Text' that does not require any escape sequences. So
421+-- 'ToJSONKeyText' can be used instead of 'ToJSONKeyValue' to encode maps
422+-- as objects instead of arrays of pairs. This instance may be
423+-- implemented using generics as follows:
424+--
425+-- @
426+-- instance 'ToJSONKey' Color where
427+-- 'toJSONKey' = 'genericToJSONKey' 'defaultJSONKeyOptions'
428+-- @
429+--
430+-- === __Low-level implementations__
431+--
432+-- The 'Show' instance can be used to help write 'ToJSONKey':
433+--
434+-- > instance ToJSONKey Color where
435+-- > toJSONKey = ToJSONKeyText f g
436+-- > where f = Text.pack . show
437+-- > g = text . Text.pack . show
438+-- > -- text function is from Data.Aeson.Encoding
439+--
440+-- The situation of needing to turning function @a -> Text@ into
441+-- a 'ToJSONKeyFunction' is common enough that a special combinator
442+-- is provided for it. The above instance can be rewritten as:
443+--
444+-- > instance ToJSONKey Color where
445+-- > toJSONKey = toJSONKeyText (Text.pack . show)
446+--
447+-- The performance of the above instance can be improved by
448+-- not using 'String' as an intermediate step when converting to
449+-- 'Text'. One option for improving performance would be to use
450+-- template haskell machinery from the @text-show@ package. However,
451+-- even with the approach, the 'Encoding' (a wrapper around a bytestring
452+-- builder) is generated by encoding the 'Text' to a 'ByteString',
453+-- an intermediate step that could be avoided. The fastest possible
454+-- implementation would be:
455+--
456+-- > -- Assuming that OverloadedStrings is enabled
457+-- > instance ToJSONKey Color where
458+-- > toJSONKey = ToJSONKeyText f g
459+-- > where f x = case x of {Red -> "Red";Green ->"Green";Blue -> "Blue"}
460+-- > g x = case x of {Red -> text "Red";Green -> text "Green";Blue -> text "Blue"}
461+-- > -- text function is from Data.Aeson.Encoding
462+--
463+-- This works because GHC can lift the encoded values out of the case
464+-- statements, which means that they are only evaluated once. This
465+-- approach should only be used when there is a serious need to
466+-- maximize performance.
467+
297class ToJSONKey a where
468class ToJSONKey a where
469+ -- | Strategy for rendering the key for a map-like container.
298 toJSONKey :: ToJSONKeyFunction a
470 toJSONKey :: ToJSONKeyFunction a
299 default toJSONKey :: ToJSON a => ToJSONKeyFunction a
471 default toJSONKey :: ToJSON a => ToJSONKeyFunction a
300− toJSONKey = ToJSONKeyValue (toJSON, toEncoding)
472+ toJSONKey = ToJSONKeyValue toJSON toEncoding
473+
474+ -- | This is similar in spirit to the 'showsList' method of 'Show'.
475+ -- It makes it possible to give 'String' keys special treatment
476+ -- without using @OverlappingInstances@. End users should always
477+ -- be able to use the default implementation of this method.
301 toJSONKeyList :: ToJSONKeyFunction [a]
478 toJSONKeyList :: ToJSONKeyFunction [a]
302 default toJSONKeyList :: ToJSON a => ToJSONKeyFunction [a]
479 default toJSONKeyList :: ToJSON a => ToJSONKeyFunction [a]
303− toJSONKeyList = ToJSONKeyValue (toJSON, toEncoding)
480+ toJSONKeyList = ToJSONKeyValue toJSON toEncoding
304
481
305data ToJSONKeyFunction a
482data ToJSONKeyFunction a
306− = ToJSONKeyText (a -> Text, a -> Encoding)
483+ = ToJSONKeyText !(a -> Text) !(a -> Encoding' Text)
484+ -- ^ key is encoded to string, produces object
307− | ToJSONKeyValue (a -> Value, a -> Encoding)
485+ | ToJSONKeyValue !(a -> Value) !(a -> Encoding)
486+ -- ^ key is encoded to value, produces array
308
487
488+-- | Helper for creating textual keys.
489+--
490+-- @
491+-- instance 'ToJSONKey' MyKey where
492+-- 'toJSONKey' = 'toJSONKeyText' myKeyToText
493+-- where
494+-- myKeyToText = Text.pack . show -- or showt from text-show
495+-- @
496+toJSONKeyText :: (a -> Text) -> ToJSONKeyFunction a
497+toJSONKeyText f = ToJSONKeyText f (E.text . f)
498+
499+-- | TODO: should this be exported?
500+toJSONKeyTextEnc :: (a -> Encoding' Text) -> ToJSONKeyFunction a
501+toJSONKeyTextEnc e = ToJSONKeyText tot e
502+ where
503+ -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
504+ tot = T.dropAround (== '"')
505+ . T.decodeLatin1
506+ . L.toStrict
507+ . E.encodingToLazyByteString
508+ . e
509+
510+instance Contravariant ToJSONKeyFunction where
511+ contramap = contramapToJSONKeyFunction
512+
513+-- | Contravariant map, as 'ToJSONKeyFunction' is a contravariant functor.
309contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
514contramapToJSONKeyFunction :: (b -> a) -> ToJSONKeyFunction a -> ToJSONKeyFunction b
310contramapToJSONKeyFunction h x = case x of
515contramapToJSONKeyFunction h x = case x of
311− ToJSONKeyText (f,g) -> ToJSONKeyText (f . h, g . h)
516+ ToJSONKeyText f g -> ToJSONKeyText (f . h) (g . h)
312− ToJSONKeyValue (f,g) -> ToJSONKeyValue (f . h, g . h)
517+ ToJSONKeyValue f g -> ToJSONKeyValue (f . h) (g . h)
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
313
541
314-------------------------------------------------------------------------------
542-------------------------------------------------------------------------------
315-- Lifings of FromJSON and ToJSON to unary and binary type constructors
543-- Lifings of FromJSON and ToJSON to unary and binary type constructors
316-------------------------------------------------------------------------------
544-------------------------------------------------------------------------------
317
545
546+
318-- | Lifting of the 'ToJSON' class to unary type constructors.
547-- | Lifting of the 'ToJSON' class to unary type constructors.
548+--
549+-- Instead of manually writing your 'ToJSON1' instance, there are two options
550+-- to do it automatically:
551+--
552+-- * "Data.Aeson.TH" provides Template Haskell functions which will derive an
553+-- instance at compile time. The generated instance is optimized for your type
554+-- so it will probably be more efficient than the following option.
555+--
556+-- * The compiler can provide a default generic implementation for
557+-- 'toJSON1'.
558+--
559+-- To use the second, simply add a @deriving 'Generic1'@ clause to your
560+-- datatype and declare a 'ToJSON1' instance for your datatype without giving
561+-- definitions for 'liftToJSON' or 'liftToEncoding'.
562+--
563+-- For example:
564+--
565+-- @
566+-- {-\# LANGUAGE DeriveGeneric \#-}
567+--
568+-- import "GHC.Generics"
569+--
570+-- data Pair = Pair { pairFst :: a, pairSnd :: b } deriving 'Generic1'
571+--
572+-- instance 'ToJSON' a => 'ToJSON1' (Pair a)
573+-- @
574+--
575+-- If the default implementation doesn't give exactly the results you want,
576+-- you can customize the generic encoding with only a tiny amount of
577+-- effort, using 'genericLiftToJSON' and 'genericLiftToEncoding' with
578+-- your preferred 'Options':
579+--
580+-- @
581+-- customOptions = 'defaultOptions'
582+-- { 'fieldLabelModifier' = 'map' 'Data.Char.toUpper'
583+-- }
584+--
585+-- instance 'ToJSON' a => 'ToJSON1' (Pair a) where
586+-- 'liftToJSON' = 'genericLiftToJSON' customOptions
587+-- 'liftToEncoding' = 'genericLiftToEncoding' customOptions
588+-- @
589+--
590+-- See also 'ToJSON'.
319class ToJSON1 f where
591class ToJSON1 f where
320 liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value
592 liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value
593+
594+ default liftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))
595+ => (a -> Value) -> ([a] -> Value) -> f a -> Value
596+ liftToJSON = genericLiftToJSON defaultOptions
597+
321 liftToJSONList :: (a -> Value) -> ([a] -> Value) -> [f a] -> Value
598 liftToJSONList :: (a -> Value) -> ([a] -> Value) -> [f a] -> Value
322 liftToJSONList f g = listValue (liftToJSON f g)
599 liftToJSONList f g = listValue (liftToJSON f g)
323
600
324− -- | Unfortunately there cannot be a default implementation of 'liftToEncoding'.
325 liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
601 liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
602+
603+ default liftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))
604+ => (a -> Encoding) -> ([a] -> Encoding)
605+ -> f a -> Encoding
606+ liftToEncoding = genericLiftToEncoding defaultOptions
607+
326 liftToEncodingList :: (a -> Encoding) -> ([a] -> Encoding) -> [f a] -> Encoding
608 liftToEncodingList :: (a -> Encoding) -> ([a] -> Encoding) -> [f a] -> Encoding
327 liftToEncodingList f g = listEncoding (liftToEncoding f g)
609 liftToEncodingList f g = listEncoding (liftToEncoding f g)
328
610
@@ -336,8 +618,13 @@ toEncoding1 :: (ToJSON1 f, ToJSON a) => f a -> Encoding
336toEncoding1 = liftToEncoding toEncoding toEncodingList
618toEncoding1 = liftToEncoding toEncoding toEncodingList
337{-# INLINE toEncoding1 #-}
619{-# INLINE toEncoding1 #-}
338
620
339−
340-- | Lifting of the 'ToJSON' class to binary type constructors.
621-- | Lifting of the 'ToJSON' class to binary type constructors.
622+--
623+-- Instead of manually writing your 'ToJSON2' instance, "Data.Aeson.TH"
624+-- provides Template Haskell functions which will derive an instance at compile time.
625+--
626+-- The compiler cannot provide a default generic implementation for 'liftToJSON2',
627+-- unlike 'toJSON' and 'liftToJSON'.
341class ToJSON2 f where
628class ToJSON2 f where
342 liftToJSON2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> f a b -> Value
629 liftToJSON2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> f a b -> Value
343 liftToJSONList2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> [f a b] -> Value
630 liftToJSONList2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> [f a b] -> Value
@@ -361,9 +648,25 @@ toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingLis
361-- Encoding functions
648-- Encoding functions
362-------------------------------------------------------------------------------
649-------------------------------------------------------------------------------
363
650
651+-- | Helper function to use with 'liftToEncoding'.
652+-- Useful when writing own 'ToJSON1' instances.
653+--
654+-- @
655+-- newtype F a = F [a]
656+--
657+-- -- This instance encodes 'String' as an array of chars
658+-- instance 'ToJSON1' F where
659+-- 'liftToJSON' tj _ (F xs) = 'liftToJSON' tj ('listValue' tj) xs
660+-- 'liftToEncoding' te _ (F xs) = 'liftToEncoding' te ('listEncoding' te) xs
661+--
662+-- instance 'Data.Aeson.FromJSON.FromJSON1' F where
663+-- 'Data.Aeson.FromJSON.liftParseJSON' p _ v = F \<$\> 'Data.Aeson.FromJSON.liftParseJSON' p ('Data.Aeson.FromJSON.listParser' p) v
664+-- @
364listEncoding :: (a -> Encoding) -> [a] -> Encoding
665listEncoding :: (a -> Encoding) -> [a] -> Encoding
365listEncoding = E.list
666listEncoding = E.list
667+{-# INLINE listEncoding #-}
366
668
669+-- | Helper function to use with 'liftToJSON', see 'listEncoding'.
367listValue :: (a -> Value) -> [a] -> Value
670listValue :: (a -> Value) -> [a] -> Value
368listValue f = Array . V.fromList . map f
671listValue f = Array . V.fromList . map f
369{-# INLINE listValue #-}
672{-# INLINE listValue #-}
@@ -382,6 +685,11 @@ instance ToJSON1 [] where
382 {-# INLINE liftToEncoding #-}
685 {-# INLINE liftToEncoding #-}
383
686
384instance (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+
385 toJSON = toJSON1
693 toJSON = toJSON1
386 {-# INLINE toJSON #-}
694 {-# INLINE toJSON #-}
387
695
@@ -392,214 +700,260 @@ instance (ToJSON a) => ToJSON [a] where
392-- Generic toJSON / toEncoding
700-- Generic toJSON / toEncoding
393-------------------------------------------------------------------------------
701-------------------------------------------------------------------------------
394
702
703+instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where
704+ -- Meta-information, which is not handled elsewhere, is ignored:
705+ gToJSON opts targs = gToJSON opts targs . unM1
706+ {-# INLINE gToJSON #-}
707+
708+instance GToJSON enc One Par1 where
709+ -- Direct occurrences of the last type parameter are encoded with the
710+ -- function passed in as an argument:
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 #-}
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+
395--------------------------------------------------------------------------------
744--------------------------------------------------------------------------------
396-- Generic toJSON
745-- Generic toJSON
397
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+
398−instance OVERLAPPABLE_ (GToJSON a) => GToJSON (M1 i c a) where
751+instance GToJSON Value arity V1 where
399− -- Meta-information, which is not handled elsewhere, is ignored:
752+ -- Empty values do not exist, which makes the job of formatting them
400− gToJSON opts = gToJSON opts . unM1
753+ -- rather easy:
754+ gToJSON _ _ x = x `seq` error "case: V1"
755+ {-# INLINE gToJSON #-}
401
756
402−instance (ToJSON a) => GToJSON (K1 i a) where
757+instance ToJSON a => GToJSON Value arity (K1 i a) where
403 -- Constant values are encoded using their ToJSON instance:
758 -- Constant values are encoded using their ToJSON instance:
404− gToJSON _opts = toJSON . unK1
759+ gToJSON _opts _ = toJSON . unK1
760+ {-# INLINE gToJSON #-}
405
761
406−instance GToJSON U1 where
762+instance ToJSON1 f => GToJSON Value One (Rec1 f) where
407− -- Empty constructors are encoded to an empty array:
763+ -- Recursive occurrences of the last type parameter are encoded using their
408− gToJSON _opts _ = emptyArray
764+ -- ToJSON1 instance:
765+ gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1
766+ {-# INLINE gToJSON #-}
409
767
410−instance (ConsToJSON a) => GToJSON (C1 c a) where
768+instance GToJSON Value arity U1 where
411− -- Constructors need to be encoded differently depending on whether they're
769+ -- Empty constructors are encoded to an empty array:
412− -- a record or not. This distinction is made by 'consToJSON':
770+ gToJSON _opts _ _ = emptyArray
413− gToJSON opts = consToJSON opts . unM1
771+ {-# INLINE gToJSON #-}
414
772
415−instance ( WriteProduct a, WriteProduct b
773+instance ( WriteProduct arity a, WriteProduct arity b
416− , ProductSize a, ProductSize b ) => GToJSON (a :*: b) where
774+ , ProductSize a, ProductSize b
775+ ) => GToJSON Value arity (a :*: b)
776+ where
417 -- 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
418 -- 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
419 -- 'writeProduct':
779 -- 'writeProduct':
420− gToJSON opts p =
780+ gToJSON opts targs p =
421 Array $ V.create $ do
781 Array $ V.create $ do
422 mv <- VM.unsafeNew lenProduct
782 mv <- VM.unsafeNew lenProduct
423− writeProduct opts mv 0 lenProduct p
783+ writeProduct opts targs mv 0 lenProduct p
424 return mv
784 return mv
425 where
785 where
426 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
786 lenProduct = (unTagged2 :: Tagged2 (a :*: b) Int -> Int)
427 productSize
787 productSize
428−
788+ {-# INLINE gToJSON #-}
429−instance ( AllNullary (a :+: b) allNullary
789+
430− , SumToJSON (a :+: b) allNullary ) => GToJSON (a :+: b) where
790+instance ( ToJSON1 f
431− -- If all constructors of a sum datatype are nullary and the
791+ , GToJSON Value One g
432− -- 'allNullaryToStringTag' option is set they are encoded to
792+ ) => GToJSON Value One (f :.: g)
433− -- strings. This distinction is made by 'sumToJSON':
793+ where
434− gToJSON opts = (unTagged :: Tagged allNullary Value -> Value)
794+ -- If an occurrence of the last type parameter is nested inside two
435− . sumToJSON opts
795+ -- composed types, it is encoded by using the outermost type's ToJSON1
796+ -- instance to generically encode the innermost type:
797+ gToJSON opts targs =
798+ let gtj = gToJSON opts targs in
799+ liftToJSON gtj (listValue gtj) . unComp1
800+ {-# INLINE gToJSON #-}
436
801
437--------------------------------------------------------------------------------
802--------------------------------------------------------------------------------
438-- Generic toEncoding
803-- Generic toEncoding
439
804
440−instance OVERLAPPABLE_ (GToEncoding a) => GToEncoding (M1 i c a) where
441− -- Meta-information, which is not handled elsewhere, is ignored:
442− gToEncoding opts = gToEncoding opts . unM1
443−
444−instance (ToJSON a) => GToEncoding (K1 i a) where
805+instance ToJSON a => GToJSON Encoding arity (K1 i a) where
445 -- Constant values are encoded using their ToJSON instance:
806 -- Constant values are encoded using their ToJSON instance:
446− gToEncoding _opts = toEncoding . unK1
807+ gToJSON _opts _ = toEncoding . unK1
808+ {-# INLINE gToJSON #-}
447
809
448−instance GToEncoding U1 where
810+instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where
449− -- Empty constructors are encoded to an empty array:
811+ -- Recursive occurrences of the last type parameter are encoded using their
450− gToEncoding _opts _ = E.emptyArray_
812+ -- ToEncoding1 instance:
813+ gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1
814+ {-# INLINE gToJSON #-}
451
815
452−instance (ConsToEncoding a) => GToEncoding (C1 c a) where
816+instance GToJSON Encoding arity U1 where
453− -- Constructors need to be encoded differently depending on whether they're
817+ -- Empty constructors are encoded to an empty array:
454− -- a record or not. This distinction is made by 'consToEncoding':
818+ gToJSON _opts _ _ = E.emptyArray_
455− gToEncoding opts = consToEncoding opts . unM1
819+ {-# INLINE gToJSON #-}
456
820
457−instance ( EncodeProduct a, EncodeProduct b ) => GToEncoding (a :*: b) where
821+instance ( EncodeProduct arity a
822+ , EncodeProduct arity b
823+ ) => GToJSON Encoding arity (a :*: b)
824+ where
458 -- 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
459 -- 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
460 -- 'encodeProduct':
827 -- 'encodeProduct':
461− gToEncoding opts p = E.tuple $ encodeProduct opts p
828+ gToJSON opts targs p = E.list E.retagEncoding [encodeProduct opts targs p]
462−
829+ {-# INLINE gToJSON #-}
463−instance ( AllNullary (a :+: b) allNullary
830+
464− , SumToEncoding (a :+: b) allNullary ) => GToEncoding (a :+: b) where
831+instance ( ToJSON1 f
465− -- If all constructors of a sum datatype are nullary and the
832+ , GToJSON Encoding One g
466− -- 'allNullaryToStringTag' option is set they are encoded to
833+ ) => GToJSON Encoding One (f :.: g)
467− -- strings. This distinction is made by 'sumToEncoding':
834+ where
468− gToEncoding opts
835+ -- If an occurrence of the last type parameter is nested inside two
469− = (unTagged :: Tagged allNullary Encoding -> Encoding)
836+ -- composed types, it is encoded by using the outermost type's ToJSON1
470− . sumToEncoding opts
837+ -- instance to generically encode the innermost type:
838+ gToJSON opts targs =
839+ let gte = gToJSON opts targs in
840+ liftToEncoding gte (listEncoding gte) . unComp1
841+ {-# INLINE gToJSON #-}
471
842
472--------------------------------------------------------------------------------
843--------------------------------------------------------------------------------
473
844
474−class SumToJSON f allNullary where
845+class SumToJSON enc arity f allNullary where
846+ sumToJSON :: Options -> ToArgs enc arity a
475− sumToJSON :: Options -> f a -> Tagged allNullary Value
847+ -> f a -> Tagged allNullary enc
476−
848+
477−instance ( GetConName f
849+instance ( GetConName f
478− , TaggedObjectPairs f
850+ , FromString enc
479− , ObjectWithSingleFieldObj f
851+ , TaggedObject enc arity f
852+ , SumToJSON' ObjectWithSingleField enc arity f
853+ , SumToJSON' TwoElemArray enc arity f
854+ , SumToJSON' UntaggedValue enc arity f
480− , TwoElemArrayObj f ) => SumToJSON f True where
855+ ) => SumToJSON enc arity f True
856+ where
481− sumToJSON opts
857+ sumToJSON opts targs
482− | allNullaryToStringTag opts = Tagged . String . pack
858+ | allNullaryToStringTag opts = Tagged . fromString
483 . constructorTagModifier opts . getConName
859 . constructorTagModifier opts . getConName
484− | otherwise = Tagged . nonAllNullarySumToJSON opts
860+ | otherwise = Tagged . nonAllNullarySumToJSON opts targs
485−
861+
486−instance ( TwoElemArrayObj f
862+instance ( TaggedObject enc arity f
487− , TaggedObjectPairs f
863+ , SumToJSON' ObjectWithSingleField enc arity f
864+ , SumToJSON' TwoElemArray enc arity f
865+ , SumToJSON' UntaggedValue enc arity f
488− , ObjectWithSingleFieldObj f ) => SumToJSON f False where
866+ ) => SumToJSON enc arity f False
867+ where
489− sumToJSON opts = Tagged . nonAllNullarySumToJSON opts
868+ sumToJSON opts targs = Tagged . nonAllNullarySumToJSON opts targs
490−
869+
491−nonAllNullarySumToJSON :: ( TwoElemArrayObj f
870+nonAllNullarySumToJSON :: ( TaggedObject enc arity f
492− , TaggedObjectPairs f
871+ , SumToJSON' ObjectWithSingleField enc arity f
493− , ObjectWithSingleFieldObj f
872+ , SumToJSON' TwoElemArray enc arity f
494− ) => Options -> f a -> Value
873+ , SumToJSON' UntaggedValue enc arity f
874+ ) => Options -> ToArgs enc arity a
875+ -> f a -> enc
495−nonAllNullarySumToJSON opts =
876+nonAllNullarySumToJSON opts targs =
496 case sumEncoding opts of
877 case sumEncoding opts of
497− TaggedObject{..} ->
498− object . taggedObjectPairs opts tagFieldName contentsFieldName
499− ObjectWithSingleField -> Object . objectWithSingleFieldObj opts
500− TwoElemArray -> Array . twoElemArrayObj opts
501−
502−--------------------------------------------------------------------------------
503
878
504−class SumToEncoding f allNullary where
505− sumToEncoding :: Options -> f a -> Tagged allNullary Encoding
506−
507−instance ( GetConName f
508− , TaggedObjectEnc f
509− , ObjectWithSingleFieldEnc f
510− , TwoElemArrayEnc f ) => SumToEncoding f True where
511− sumToEncoding opts
512− | allNullaryToStringTag opts = Tagged . toEncoding .
513− constructorTagModifier opts . getConName
514− | otherwise = Tagged . nonAllNullarySumToEncoding opts
515−
516−instance ( TwoElemArrayEnc f
517− , TaggedObjectEnc f
518− , ObjectWithSingleFieldEnc f ) => SumToEncoding f False where
519− sumToEncoding opts = Tagged . nonAllNullarySumToEncoding opts
520−
521−nonAllNullarySumToEncoding :: ( TwoElemArrayEnc f
522− , TaggedObjectEnc f
523− , ObjectWithSingleFieldEnc f
524− ) => Options -> f a -> Encoding
525−nonAllNullarySumToEncoding opts =
526− case sumEncoding opts of
527 TaggedObject{..} ->
879 TaggedObject{..} ->
528− taggedObjectEnc opts tagFieldName contentsFieldName
880+ taggedObject opts targs tagFieldName contentsFieldName
529− ObjectWithSingleField -> objectWithSingleFieldEnc opts
530− TwoElemArray -> twoElemArrayEnc opts
531−
532−--------------------------------------------------------------------------------
533
881
534−class TaggedObjectPairs f where
882+ ObjectWithSingleField ->
535− taggedObjectPairs :: Options -> String -> String -> f a -> [Pair]
883+ (unTagged :: Tagged ObjectWithSingleField enc -> enc)
884+ . sumToJSON' opts targs
536
885
537−instance ( TaggedObjectPairs a
886+ TwoElemArray ->
538− , TaggedObjectPairs b ) => TaggedObjectPairs (a :+: b) where
887+ (unTagged :: Tagged TwoElemArray enc -> enc)
539− taggedObjectPairs opts tagFieldName contentsFieldName (L1 x) =
888+ . sumToJSON' opts targs
540− taggedObjectPairs opts tagFieldName contentsFieldName x
541− taggedObjectPairs opts tagFieldName contentsFieldName (R1 x) =
542− taggedObjectPairs opts tagFieldName contentsFieldName x
543
889
544−instance ( IsRecord a isRecord
890+ UntaggedValue ->
545− , TaggedObjectPairs' a isRecord
546− , Constructor c ) => TaggedObjectPairs (C1 c a) where
547− taggedObjectPairs opts tagFieldName contentsFieldName =
548− (pack tagFieldName .= constructorTagModifier opts
549− (conName (undefined :: t c a p)) :) .
550− (unTagged :: Tagged isRecord [Pair] -> [Pair]) .
891+ (unTagged :: Tagged UntaggedValue enc -> enc)
551− taggedObjectPairs' opts contentsFieldName . unM1
892+ . sumToJSON' opts targs
552
893
553−class TaggedObjectPairs' f isRecord where
894+--------------------------------------------------------------------------------
554− taggedObjectPairs' :: Options -> String -> f a -> Tagged isRecord [Pair]
555
895
556−instance OVERLAPPING_ TaggedObjectPairs' U1 False where
896+class FromString enc where
557− taggedObjectPairs' _ _ _ = Tagged []
897+ fromString :: String -> enc
558
898
559−instance (RecordToPairs f) => TaggedObjectPairs' f True where
899+instance FromString Encoding where
560− taggedObjectPairs' opts _ = Tagged . toList . recordToPairs opts
900+ fromString = toEncoding
561
901
562−instance (GToJSON f) => TaggedObjectPairs' f False where
902+instance FromString Value where
563− taggedObjectPairs' opts contentsFieldName =
903+ fromString = String . pack
564− Tagged . (:[]) . (pack contentsFieldName .=) . gToJSON opts
565
904
566--------------------------------------------------------------------------------
905--------------------------------------------------------------------------------
567
906
568−class TaggedObjectEnc f where
907+class TaggedObject enc arity f where
569− taggedObjectEnc :: Options -> String -> String -> f a -> Encoding
908+ taggedObject :: Options -> ToArgs enc arity a
570−
909+ -> String -> String
571−instance ( TaggedObjectEnc a
910+ -> f a -> enc
572− , TaggedObjectEnc b ) => TaggedObjectEnc (a :+: b) where
911+
912+instance ( TaggedObject enc arity a
913+ , TaggedObject enc arity b
914+ ) => TaggedObject enc arity (a :+: b)
915+ where
573− taggedObjectEnc opts tagFieldName contentsFieldName (L1 x) =
916+ taggedObject opts targs tagFieldName contentsFieldName (L1 x) =
574− taggedObjectEnc opts tagFieldName contentsFieldName x
917+ taggedObject opts targs tagFieldName contentsFieldName x
575− taggedObjectEnc opts tagFieldName contentsFieldName (R1 x) =
918+ taggedObject opts targs tagFieldName contentsFieldName (R1 x) =
576− taggedObjectEnc opts tagFieldName contentsFieldName x
919+ taggedObject opts targs tagFieldName contentsFieldName x
577−
920+
578−instance ( IsRecord a isRecord
921+instance ( IsRecord a isRecord
579− , TaggedObjectEnc' a isRecord
922+ , TaggedObject' enc pairs arity a isRecord
923+ , FromPairs enc pairs
924+ , FromString enc
925+ , KeyValuePair enc pairs
580− , Constructor c ) => TaggedObjectEnc (C1 c a) where
926+ , Constructor c
927+ ) => TaggedObject enc arity (C1 c a)
928+ where
581− taggedObjectEnc opts tagFieldName contentsFieldName v =
929+ taggedObject opts targs tagFieldName contentsFieldName =
582− E.openCurly <>
930+ fromPairs . mappend tag . contents
583− (toEncoding tagFieldName <>
931+ where
584− E.colon <>
932+ tag = tagFieldName `pair`
585− toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))) <>
933+ (fromString (constructorTagModifier opts (conName (undefined :: t c a p)))
934+ :: enc)
935+ contents =
586− ((unTagged :: Tagged isRecord Encoding -> Encoding) .
936+ (unTagged :: Tagged isRecord pairs -> pairs) .
587− taggedObjectEnc' opts contentsFieldName . unM1 $ v) <>
937+ taggedObject' opts targs contentsFieldName . unM1
588− E.closeCurly
938+
589−
590−class TaggedObjectEnc' f isRecord where
939+class TaggedObject' enc pairs arity f isRecord where
940+ taggedObject' :: Options -> ToArgs enc arity a
591− taggedObjectEnc' :: Options -> String -> f a -> Tagged isRecord Encoding
941+ -> String -> f a -> Tagged isRecord pairs
592−
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+
593−instance OVERLAPPING_ TaggedObjectEnc' U1 False where
950+instance OVERLAPPING_ Monoid pairs => TaggedObject' enc pairs arity U1 False where
594− taggedObjectEnc' _ _ _ = Tagged mempty
951+ taggedObject' _ _ _ _ = Tagged mempty
595−
952+
596−instance (RecordToEncoding f) => TaggedObjectEnc' f True where
953+instance ( RecordToPairs enc pairs arity f
597− taggedObjectEnc' opts _ = Tagged . (E.comma <>) . fst . recordToEncoding opts
954+ ) => TaggedObject' enc pairs arity f True
598−
955+ where
599−instance (GToEncoding f) => TaggedObjectEnc' f False where
956+ taggedObject' opts targs _ = Tagged . recordToPairs opts targs
600− taggedObjectEnc' opts contentsFieldName =
601− Tagged . (\z -> E.comma <> toEncoding contentsFieldName <> E.colon <> z) .
602− gToEncoding opts
603
957
604--------------------------------------------------------------------------------
958--------------------------------------------------------------------------------
605
959
@@ -614,248 +968,251 @@ instance (GetConName a, GetConName b) => GetConName (a :+: b) where
614instance (Constructor c) => GetConName (C1 c a) where
968instance (Constructor c) => GetConName (C1 c a) where
615 getConName = conName
969 getConName = conName
616
970
617−--------------------------------------------------------------------------------
971+-- For genericToJSONKey
618−
972+instance GetConName a => GetConName (D1 d a) where
619−class TwoElemArrayObj f where
973+ getConName (M1 x) = getConName x
620− twoElemArrayObj :: Options -> f a -> V.Vector Value
621−
622−instance (TwoElemArrayObj a, TwoElemArrayObj b) => TwoElemArrayObj (a :+: b) where
623− twoElemArrayObj opts (L1 x) = twoElemArrayObj opts x
624− twoElemArrayObj opts (R1 x) = twoElemArrayObj opts x
625−
626−instance ( GToJSON a, ConsToJSON a
627− , Constructor c ) => TwoElemArrayObj (C1 c a) where
628− twoElemArrayObj opts x = V.create $ do
629− mv <- VM.unsafeNew 2
630− VM.unsafeWrite mv 0 $ String $ pack $ constructorTagModifier opts
631− $ conName (undefined :: t c a p)
632− VM.unsafeWrite mv 1 $ gToJSON opts x
633− return mv
634
974
635--------------------------------------------------------------------------------
975--------------------------------------------------------------------------------
636
976
637−class TwoElemArrayEnc f where
977+-- Reflection of SumEncoding variants
638− twoElemArrayEnc :: Options -> f a -> Encoding
639
978
640−instance (TwoElemArrayEnc a, TwoElemArrayEnc b) => TwoElemArrayEnc (a :+: b) where
979+data ObjectWithSingleField
641− twoElemArrayEnc opts (L1 x) = twoElemArrayEnc opts x
980+data TwoElemArray
642− twoElemArrayEnc opts (R1 x) = twoElemArrayEnc opts x
981+data UntaggedValue
643−
644−instance ( GToEncoding a, ConsToEncoding a
645− , Constructor c ) => TwoElemArrayEnc (C1 c a) where
646− twoElemArrayEnc opts x = E.tuple $
647− toEncoding (constructorTagModifier opts (conName (undefined :: t c a p))) >*<
648− gToEncoding opts x
649
982
650--------------------------------------------------------------------------------
983--------------------------------------------------------------------------------
651
984
652−class ConsToJSON f where
985+class SumToJSON' s enc arity f where
653− consToJSON :: Options -> f a -> Value
986+ sumToJSON' :: Options -> ToArgs enc arity a
654−
655−class ConsToJSON' f isRecord where
656− consToJSON' :: Options -> Bool -- ^ Are we a record with one field?
657− -> f a -> Tagged isRecord Value
987+ -> f a -> Tagged s enc
658−
659−instance ( IsRecord f isRecord
660− , ConsToJSON' f isRecord ) => ConsToJSON f where
661− consToJSON opts = (unTagged :: Tagged isRecord Value -> Value)
662− . consToJSON' opts (isUnary (undefined :: f a))
663−
664−instance (RecordToPairs f) => ConsToJSON' f True where
665− consToJSON' opts isUn f = let
666− vals = toList $ recordToPairs opts f
667− in case (unwrapUnaryRecords opts,isUn,vals) of
668− (True,True,[(_,val)]) -> Tagged val
669− _ -> Tagged $ object vals
670
988
671−instance GToJSON f => ConsToJSON' f False where
989+instance ( SumToJSON' s enc arity a
672− consToJSON' opts _ = Tagged . gToJSON opts
990+ , SumToJSON' s enc arity b
991+ ) => SumToJSON' s enc arity (a :+: b)
992+ where
993+ sumToJSON' opts targs (L1 x) = sumToJSON' opts targs x
994+ sumToJSON' opts targs (R1 x) = sumToJSON' opts targs x
673
995
674--------------------------------------------------------------------------------
996--------------------------------------------------------------------------------
675
997
676−class ConsToEncoding f where
998+instance ( GToJSON Value arity a
677− consToEncoding :: Options -> f a -> Encoding
999+ , ConsToJSON Value arity a
678−
1000+ , Constructor c
679−class ConsToEncoding' f isRecord where
1001+ ) => SumToJSON' TwoElemArray Value arity (C1 c a) where
680− consToEncoding' :: Options -> Bool -- ^ Are we a record with one field?
1002+ sumToJSON' opts targs x = Tagged $ Array $ V.create $ do
681− -> f a -> Tagged isRecord Encoding
1003+ mv <- VM.unsafeNew 2
682−
1004+ VM.unsafeWrite mv 0 $ String $ pack $ constructorTagModifier opts
683−instance ( IsRecord f isRecord
1005+ $ conName (undefined :: t c a p)
684− , ConsToEncoding' f isRecord ) => ConsToEncoding f where
1006+ VM.unsafeWrite mv 1 $ gToJSON opts targs x
685− consToEncoding opts = (unTagged :: Tagged isRecord Encoding -> Encoding)
1007+ return mv
686− . consToEncoding' opts (isUnary (undefined :: f a))
687−
688−instance (RecordToEncoding f) => ConsToEncoding' f True where
689− consToEncoding' opts isUn x =
690− let (enc, mbVal) = recordToEncoding opts x
691− in case (unwrapUnaryRecords opts, isUn, mbVal) of
692− (True, True, Just val) -> Tagged val
693− _ -> Tagged $ E.wrapObject enc
694−
695−instance GToEncoding f => ConsToEncoding' f False where
696− consToEncoding' opts _ = Tagged . gToEncoding opts
697
1008
698--------------------------------------------------------------------------------
1009--------------------------------------------------------------------------------
699
1010
700−class RecordToPairs f where
1011+instance ( GToJSON Encoding arity a
701− recordToPairs :: Options -> f a -> DList Pair
1012+ , ConsToJSON Encoding arity a
702−
1013+ , Constructor c
703−instance (RecordToPairs a, RecordToPairs b) => RecordToPairs (a :*: b) where
1014+ ) => SumToJSON' TwoElemArray Encoding arity (C1 c a)
704− recordToPairs opts (a :*: b) = recordToPairs opts a <>
1015+ where
705− recordToPairs opts b
1016+ sumToJSON' opts targs x = Tagged $ E.list id
1017+ [ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))
1018+ , gToJSON opts targs x
1019+ ]
706
1020
707−instance (Selector s, GToJSON a) => RecordToPairs (S1 s a) where
1021+--------------------------------------------------------------------------------
708− recordToPairs = fieldToPair
709−
710−instance OVERLAPPING_ (Selector s, ToJSON a) =>
711− RecordToPairs (S1 s (K1 i (Maybe a))) where
712− recordToPairs opts (M1 k1) | omitNothingFields opts
713− , K1 Nothing <- k1 = DList.empty
714− recordToPairs opts m1 = fieldToPair opts m1
715
1022
716−fieldToPair :: (Selector s, GToJSON a) => Options -> S1 s a p -> DList Pair
1023+class ConsToJSON enc arity f where
717−fieldToPair opts m1 = pure ( pack $ fieldLabelModifier opts $ selName m1
1024+ consToJSON :: Options -> ToArgs enc arity a
718− , gToJSON opts (unM1 m1)
1025+ -> f a -> enc
719− )
1026+
1027+class ConsToJSON' enc arity f isRecord where
1028+ consToJSON' :: Options -> ToArgs enc arity a
1029+ -> f a -> Tagged isRecord enc
1030+
1031+instance ( IsRecord f isRecord
1032+ , ConsToJSON' enc arity f isRecord
1033+ ) => ConsToJSON enc arity f
1034+ where
1035+ consToJSON opts targs =
1036+ (unTagged :: Tagged isRecord enc -> enc)
1037+ . consToJSON' opts targs
1038+ {-# INLINE consToJSON #-}
1039+
1040+instance OVERLAPPING_
1041+ ( RecordToPairs enc pairs arity (S1 s f)
1042+ , FromPairs enc pairs
1043+ , GToJSON enc arity f
1044+ ) => ConsToJSON' enc arity (S1 s f) True
1045+ where
1046+ consToJSON' opts targs
1047+ | unwrapUnaryRecords opts = Tagged . gToJSON opts targs
1048+ | otherwise = Tagged . fromPairs . recordToPairs opts targs
1049+ {-# INLINE consToJSON' #-}
1050+
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' #-}
1057+
1058+instance GToJSON enc arity f => ConsToJSON' enc arity f False where
1059+ consToJSON' opts targs = Tagged . gToJSON opts targs
1060+ {-# INLINE consToJSON' #-}
720
1061
721--------------------------------------------------------------------------------
1062--------------------------------------------------------------------------------
722
1063
723−class RecordToEncoding f where
1064+class RecordToPairs enc pairs arity f where
724 -- 1st element: whole thing
1065 -- 1st element: whole thing
725 -- 2nd element: in case the record has only 1 field, just the value
1066 -- 2nd element: in case the record has only 1 field, just the value
726 -- of the field (without the key); 'Nothing' otherwise
1067 -- of the field (without the key); 'Nothing' otherwise
727− recordToEncoding :: Options -> f a -> (Encoding, Maybe Encoding)
1068+ recordToPairs :: Options -> ToArgs enc arity a
728−
1069+ -> f a -> pairs
729−instance (RecordToEncoding a, RecordToEncoding b) => RecordToEncoding (a :*: b) where
1070+
730− recordToEncoding opts (a :*: b) | omitNothingFields opts =
1071+instance ( Monoid pairs
731− (mconcat $ intersperse E.comma $
1072+ , RecordToPairs enc pairs arity a
732− filter (not . E.nullEncoding)
1073+ , RecordToPairs enc pairs arity b
733− [fst (recordToEncoding opts a), fst (recordToEncoding opts b)]
1074+ ) => RecordToPairs enc pairs arity (a :*: b)
734− , Nothing)
1075+ where
735− recordToEncoding opts (a :*: b) =
1076+ recordToPairs opts (targs :: ToArgs enc arity p) (a :*: b) =
736− (fst (recordToEncoding opts a) <> E.comma <>
1077+ pairsOf a `mappend` pairsOf b
737− fst (recordToEncoding opts b),
1078+ where
738− Nothing)
1079+ pairsOf :: (RecordToPairs enc pairs arity f) => f p -> pairs
739−
1080+ pairsOf = recordToPairs opts targs
1081+ {-# INLINE recordToPairs #-}
1082+
740−instance (Selector s, GToEncoding a) => RecordToEncoding (S1 s a) where
1083+instance ( Selector s
741− recordToEncoding = fieldToEncoding
1084+ , GToJSON enc arity a
742−
1085+ , KeyValuePair enc pairs
1086+ ) => RecordToPairs enc pairs arity (S1 s a)
1087+ where
1088+ recordToPairs = fieldToPair
1089+ {-# INLINE recordToPairs #-}
1090+
1091+instance INCOHERENT_
743−instance OVERLAPPING_ (Selector s, ToJSON a) =>
1092+ ( Selector s
744− RecordToEncoding (S1 s (K1 i (Maybe a))) where
1093+ , GToJSON enc arity (K1 i (Maybe a))
1094+ , KeyValuePair enc pairs
1095+ , Monoid pairs
1096+ ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))
1097+ where
745− recordToEncoding opts (M1 k1) | omitNothingFields opts
1098+ recordToPairs opts _ (M1 k1) | omitNothingFields opts
746− , K1 Nothing <- k1 = (mempty, Nothing)
1099+ , K1 Nothing <- k1 = mempty
747− recordToEncoding opts m1 = fieldToEncoding opts m1
1100+ recordToPairs opts targs m1 = fieldToPair opts targs m1
748−
1101+ {-# INLINE recordToPairs #-}
749−fieldToEncoding :: (Selector s, GToEncoding a) => Options -> S1 s a p -> (Encoding, Maybe Encoding)
1102+
750−fieldToEncoding opts m1 =
1103+instance INCOHERENT_
1104+ ( Selector s
1105+ , GToJSON enc arity (K1 i (Maybe a))
1106+ , KeyValuePair enc pairs
1107+ , Monoid pairs
1108+ ) => RecordToPairs enc pairs arity (S1 s (K1 i (Semigroup.Option a)))
1109+ where
1110+ recordToPairs opts targs = recordToPairs opts targs . unwrap
1111+ where
1112+ unwrap :: S1 s (K1 i (Semigroup.Option a)) p -> S1 s (K1 i (Maybe a)) p
1113+ unwrap (M1 (K1 (Semigroup.Option a))) = M1 (K1 a)
1114+ {-# INLINE recordToPairs #-}
1115+
1116+fieldToPair :: (Selector s
1117+ , GToJSON enc arity a
1118+ , KeyValuePair enc pairs)
1119+ => Options -> ToArgs enc arity p
1120+ -> S1 s a p -> pairs
1121+fieldToPair opts targs m1 =
751− let keyBuilder = toEncoding (fieldLabelModifier opts $ selName m1)
1122+ let key = fieldLabelModifier opts (selName m1)
752− valueBuilder = gToEncoding opts (unM1 m1)
1123+ value = gToJSON opts targs (unM1 m1)
753− in (keyBuilder <> E.colon <> valueBuilder, Just valueBuilder)
1124+ in key `pair` value
1125+{-# INLINE fieldToPair #-}
754
1126
755--------------------------------------------------------------------------------
1127--------------------------------------------------------------------------------
756
1128
757−class WriteProduct f where
1129+class WriteProduct arity f where
758 writeProduct :: Options
1130 writeProduct :: Options
1131+ -> ToArgs Value arity a
759 -> VM.MVector s Value
1132 -> VM.MVector s Value
760 -> Int -- ^ index
1133 -> Int -- ^ index
761 -> Int -- ^ length
1134 -> Int -- ^ length
762 -> f a
1135 -> f a
763 -> ST s ()
1136 -> ST s ()
764
1137
765−instance ( WriteProduct a
1138+instance ( WriteProduct arity a
766− , WriteProduct b ) => WriteProduct (a :*: b) where
1139+ , WriteProduct arity b
767− writeProduct opts mv ix len (a :*: b) = do
1140+ ) => WriteProduct arity (a :*: b) where
768− writeProduct opts mv ix lenL a
1141+ writeProduct opts targs mv ix len (a :*: b) = do
769− writeProduct opts mv ixR lenR b
1142+ writeProduct opts targs mv ix lenL a
1143+ writeProduct opts targs mv ixR lenR b
770 where
1144 where
771 lenL = len `unsafeShiftR` 1
1145 lenL = len `unsafeShiftR` 1
772 lenR = len - lenL
1146 lenR = len - lenL
773 ixR = ix + lenL
1147 ixR = ix + lenL
1148+ {-# INLINE writeProduct #-}
774
1149
775−instance OVERLAPPABLE_ (GToJSON a) => WriteProduct a where
1150+instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where
776− writeProduct opts mv ix _ = VM.unsafeWrite mv ix . gToJSON opts
1151+ writeProduct opts targs mv ix _ =
1152+ VM.unsafeWrite mv ix . gToJSON opts targs
1153+ {-# INLINE writeProduct #-}
777
1154
778--------------------------------------------------------------------------------
1155--------------------------------------------------------------------------------
779
1156
780−class EncodeProduct f where
1157+class EncodeProduct arity f where
781− encodeProduct :: Options -> f a -> Encoding
1158+ encodeProduct :: Options -> ToArgs Encoding arity a
1159+ -> f a -> Encoding' E.InArray
782
1160
783−instance ( EncodeProduct a
1161+instance ( EncodeProduct arity a
784− , EncodeProduct b ) => EncodeProduct (a :*: b) where
1162+ , EncodeProduct arity b
785− encodeProduct opts (a :*: b) | omitNothingFields opts =
1163+ ) => EncodeProduct arity (a :*: b) where
786− mconcat $ intersperse E.comma $
1164+ encodeProduct opts targs (a :*: b) | omitNothingFields opts =
1165+ E.econcat $ intersperse E.comma $
787 filter (not . E.nullEncoding)
1166 filter (not . E.nullEncoding)
788− [encodeProduct opts a, encodeProduct opts b]
1167+ [encodeProduct opts targs a, encodeProduct opts targs b]
789− encodeProduct opts (a :*: b) = encodeProduct opts a <>
1168+ encodeProduct opts targs (a :*: b) =
790− E.comma <>
1169+ encodeProduct opts targs a >*<
791− encodeProduct opts b
1170+ encodeProduct opts targs b
1171+ {-# INLINE encodeProduct #-}
792
1172
793−instance OVERLAPPABLE_ (GToEncoding a) => EncodeProduct a where
1173+instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where
794− encodeProduct opts = gToEncoding opts
1174+ encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a
1175+ {-# INLINE encodeProduct #-}
795
1176
796--------------------------------------------------------------------------------
1177--------------------------------------------------------------------------------
797
1178
798−class ObjectWithSingleFieldObj f where
1179+instance ( GToJSON enc arity a
799− objectWithSingleFieldObj :: Options -> f a -> Object
1180+ , ConsToJSON enc arity a
800−
1181+ , FromPairs enc pairs
801−instance ( ObjectWithSingleFieldObj a
1182+ , KeyValuePair enc pairs
802− , ObjectWithSingleFieldObj b ) => ObjectWithSingleFieldObj (a :+: b) where
1183+ , Constructor c
803− objectWithSingleFieldObj opts (L1 x) = objectWithSingleFieldObj opts x
1184+ ) => SumToJSON' ObjectWithSingleField enc arity (C1 c a)
804− objectWithSingleFieldObj opts (R1 x) = objectWithSingleFieldObj opts x
1185+ where
805−
1186+ sumToJSON' opts targs =
806−instance ( GToJSON a, ConsToJSON a
1187+ Tagged . fromPairs . (typ `pair`) . gToJSON opts targs
807− , Constructor c ) => ObjectWithSingleFieldObj (C1 c a) where
808− objectWithSingleFieldObj opts = H.singleton typ . gToJSON opts
809 where
1188 where
810− typ = pack $ constructorTagModifier opts $
1189+ typ = constructorTagModifier opts $
811 conName (undefined :: t c a p)
1190 conName (undefined :: t c a p)
812
1191
813--------------------------------------------------------------------------------
1192--------------------------------------------------------------------------------
814
1193
815−class ObjectWithSingleFieldEnc f where
1194+instance OVERLAPPABLE_
816− objectWithSingleFieldEnc :: Options -> f a -> Encoding
1195+ ( ConsToJSON enc arity a
817−
1196+ ) => SumToJSON' UntaggedValue enc arity (C1 c a)
818−instance ( ObjectWithSingleFieldEnc a
1197+ where
819− , ObjectWithSingleFieldEnc b ) => ObjectWithSingleFieldEnc (a :+: b) where
1198+ sumToJSON' opts targs = Tagged . gToJSON opts targs
820− objectWithSingleFieldEnc opts (L1 x) = objectWithSingleFieldEnc opts x
1199+
821− objectWithSingleFieldEnc opts (R1 x) = objectWithSingleFieldEnc opts x
1200+instance OVERLAPPING_
822−
1201+ ( Constructor c
823−instance ( GToEncoding a, ConsToEncoding a
1202+ , FromString enc
824− , Constructor c ) => ObjectWithSingleFieldEnc (C1 c a) where
1203+ ) => SumToJSON' UntaggedValue enc arity (C1 c U1)
825− objectWithSingleFieldEnc opts v =
1204+ where
826− E.openCurly <>
1205+ sumToJSON' opts _ _ = Tagged . fromString $
827− toEncoding
1206+ constructorTagModifier opts $ conName (undefined :: t c U1 p)
828− (constructorTagModifier opts
829− (conName (undefined :: t c a p))) <>
830− E.colon <>
831− gToEncoding opts v <>
832− E.closeCurly
833
1207
834-------------------------------------------------------------------------------
1208-------------------------------------------------------------------------------
835-- Instances
1209-- Instances
836-------------------------------------------------------------------------------
1210-------------------------------------------------------------------------------
837
1211
838−
839−
840-------------------------------------------------------------------------------
1212-------------------------------------------------------------------------------
841-- base
1213-- base
842-------------------------------------------------------------------------------
1214-------------------------------------------------------------------------------
843
1215
844−instance ToJSON1 Identity where
845− liftToJSON t _ (Identity a) = t a
846− {-# INLINE liftToJSON #-}
847−
848− liftToEncoding t _ (Identity a) = t a
849− {-# INLINE liftToEncoding #-}
850−
851−instance (ToJSON a) => ToJSON (Identity a) where
852− toJSON = toJSON1
853− {-# INLINE toJSON #-}
854−
855− toEncoding = toEncoding1
856− {-# INLINE toEncoding #-}
857−
858−
859instance ToJSON2 Const where
1216instance ToJSON2 Const where
860 liftToJSON2 t _ _ _ (Const x) = t x
1217 liftToJSON2 t _ _ _ (Const x) = t x
861 {-# INLINE liftToJSON2 #-}
1218 {-# INLINE liftToJSON2 #-}
@@ -884,7 +1241,7 @@ instance ToJSON1 Maybe where
884 {-# INLINE liftToJSON #-}
1241 {-# INLINE liftToJSON #-}
885
1242
886 liftToEncoding t _ (Just a) = t a
1243 liftToEncoding t _ (Just a) = t a
887− liftToEncoding _ _ Nothing = Encoding EB.null_
1244+ liftToEncoding _ _ Nothing = E.null_
888 {-# INLINE liftToEncoding #-}
1245 {-# INLINE liftToEncoding #-}
889
1246
890instance (ToJSON a) => ToJSON (Maybe a) where
1247instance (ToJSON a) => ToJSON (Maybe a) where
@@ -896,19 +1253,13 @@ instance (ToJSON a) => ToJSON (Maybe a) where
896
1253
897
1254
898instance ToJSON2 Either where
1255instance ToJSON2 Either where
899− liftToJSON2 toA _ _toB _ (Left a) = Object $ H.singleton left (toA a)
1256+ liftToJSON2 toA _ _toB _ (Left a) = Object $ H.singleton "Left" (toA a)
900− liftToJSON2 _toA _ toB _ (Right b) = Object $ H.singleton right (toB b)
1257+ liftToJSON2 _toA _ toB _ (Right b) = Object $ H.singleton "Right" (toB b)
901 {-# INLINE liftToJSON2 #-}
1258 {-# INLINE liftToJSON2 #-}
902
1259
903− liftToEncoding2 toA _ _toB _ (Left a) =
1260+ liftToEncoding2 toA _ _toB _ (Left a) = E.pairs $ E.pair "Left" $ toA a
904− Encoding (B.shortByteString "{\"Left\":")
905− <> toA a
906− <> Encoding (B.char7 '}')
907
1261
908− liftToEncoding2 _toA _ toB _ (Right b) =
1262+ liftToEncoding2 _toA _ toB _ (Right b) = E.pairs $ E.pair "Right" $ toB b
909− Encoding (B.shortByteString "{\"Right\":")
910− <> toB b
911− <> Encoding (B.char7 '}')
912 {-# INLINE liftToEncoding2 #-}
1263 {-# INLINE liftToEncoding2 #-}
913
1264
914instance (ToJSON a) => ToJSON1 (Either a) where
1265instance (ToJSON a) => ToJSON1 (Either a) where
@@ -925,18 +1276,24 @@ instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
925 toEncoding = toEncoding2
1276 toEncoding = toEncoding2
926 {-# INLINE toEncoding #-}
1277 {-# INLINE toEncoding #-}
927
1278
928−left, right :: Text
1279+instance ToJSON Void where
929−left = "Left"
1280+ toJSON = absurd
930−right = "Right"
1281+ {-# INLINE toJSON #-}
1282+
1283+ toEncoding = absurd
1284+ {-# INLINE toEncoding #-}
931
1285
932
1286
933instance ToJSON Bool where
1287instance ToJSON Bool where
934 toJSON = Bool
1288 toJSON = Bool
935 {-# INLINE toJSON #-}
1289 {-# INLINE toJSON #-}
936
1290
937− toEncoding = Encoding . EB.bool
1291+ toEncoding = E.bool
938 {-# INLINE toEncoding #-}
1292 {-# INLINE toEncoding #-}
939
1293
1294+instance ToJSONKey Bool where
1295+ toJSONKey = toJSONKeyText $ \x -> if x then "true" else "false"
1296+
940
1297
941instance ToJSON Ordering where
1298instance ToJSON Ordering where
942 toJSON = toJSON . orderingToText
1299 toJSON = toJSON . orderingToText
@@ -963,10 +1320,10 @@ instance ToJSON Char where
963 toJSONList = String . T.pack
1320 toJSONList = String . T.pack
964 {-# INLINE toJSONList #-}
1321 {-# INLINE toJSONList #-}
965
1322
966− toEncoding = Encoding . EB.string . (:[])
1323+ toEncoding = E.string . (:[])
967 {-# INLINE toEncoding #-}
1324 {-# INLINE toEncoding #-}
968
1325
969− toEncodingList = Encoding . EB.string
1326+ toEncodingList = E.string
970 {-# INLINE toEncodingList #-}
1327 {-# INLINE toEncodingList #-}
971
1328
972
1329
@@ -974,9 +1331,13 @@ instance ToJSON Double where
974 toJSON = realFloatToJSON
1331 toJSON = realFloatToJSON
975 {-# INLINE toJSON #-}
1332 {-# INLINE toJSON #-}
976
1333
977− toEncoding = realFloatToEncoding
1334+ toEncoding = E.double
978 {-# INLINE toEncoding #-}
1335 {-# INLINE toEncoding #-}
979
1336
1337+instance ToJSONKey Double where
1338+ toJSONKey = toJSONKeyTextEnc E.doubleText
1339+ {-# INLINE toJSONKey #-}
1340+
980
1341
981instance ToJSON Number where
1342instance ToJSON Number where
982 toJSON (D d) = toJSON d
1343 toJSON (D d) = toJSON d
@@ -992,20 +1353,23 @@ instance ToJSON Float where
992 toJSON = realFloatToJSON
1353 toJSON = realFloatToJSON
993 {-# INLINE toJSON #-}
1354 {-# INLINE toJSON #-}
994
1355
995− toEncoding = realFloatToEncoding
1356+ toEncoding = E.float
996 {-# INLINE toEncoding #-}
1357 {-# INLINE toEncoding #-}
997
1358
1359+instance ToJSONKey Float where
1360+ toJSONKey = toJSONKeyTextEnc E.floatText
1361+ {-# INLINE toJSONKey #-}
1362+
998
1363
999−instance ToJSON (Ratio Integer) where
1364+instance (ToJSON a, Integral a) => ToJSON (Ratio a) where
1000 toJSON r = object [ "numerator" .= numerator r
1365 toJSON r = object [ "numerator" .= numerator r
1001 , "denominator" .= denominator r
1366 , "denominator" .= denominator r
1002 ]
1367 ]
1003 {-# INLINE toJSON #-}
1368 {-# INLINE toJSON #-}
1004
1369
1005− toEncoding r = Encoding $
1370+ toEncoding r = E.pairs $
1006− B.shortByteString "{\"numerator\":" <> builder (numerator r) <>
1371+ "numerator" .= numerator r <>
1007− B.shortByteString ",\"denominator\":" <> builder (denominator r) <>
1372+ "denominator" .= denominator r
1008− B.char7 '}'
1009 {-# INLINE toEncoding #-}
1373 {-# INLINE toEncoding #-}
1010
1374
1011
1375
@@ -1013,24 +1377,37 @@ instance HasResolution a => ToJSON (Fixed a) where
1013 toJSON = Number . realToFrac
1377 toJSON = Number . realToFrac
1014 {-# INLINE toJSON #-}
1378 {-# INLINE toJSON #-}
1015
1379
1016− toEncoding = Encoding . EB.number . realToFrac
1380+ toEncoding = E.scientific . realToFrac
1017 {-# INLINE toEncoding #-}
1381 {-# INLINE toEncoding #-}
1018
1382
1383+instance HasResolution a => ToJSONKey (Fixed a) where
1384+ toJSONKey = toJSONKeyTextEnc (E.scientificText . realToFrac)
1385+ {-# INLINE toJSONKey #-}
1386+
1019
1387
1020instance ToJSON Int where
1388instance ToJSON Int where
1021 toJSON = Number . fromIntegral
1389 toJSON = Number . fromIntegral
1022 {-# INLINE toJSON #-}
1390 {-# INLINE toJSON #-}
1023
1391
1024− toEncoding = Encoding . B.intDec
1392+ toEncoding = E.int
1025 {-# INLINE toEncoding #-}
1393 {-# INLINE toEncoding #-}
1026
1394
1395+instance ToJSONKey Int where
1396+ toJSONKey = toJSONKeyTextEnc E.intText
1397+ {-# INLINE toJSONKey #-}
1398+
1399+
1027instance ToJSON Integer where
1400instance ToJSON Integer where
1028 toJSON = Number . fromInteger
1401 toJSON = Number . fromInteger
1029 {-# INLINE toJSON #-}
1402 {-# INLINE toJSON #-}
1030
1403
1031− toEncoding = Encoding . B.integerDec
1404+ toEncoding = E.integer
1032 {-# INLINE toEncoding #-}
1405 {-# INLINE toEncoding #-}
1033
1406
1407+instance ToJSONKey Integer where
1408+ toJSONKey = toJSONKeyTextEnc E.integerText
1409+ {-# INLINE toJSONKey #-}
1410+
1034
1411
1035instance ToJSON Natural where
1412instance ToJSON Natural where
1036 toJSON = toJSON . toInteger
1413 toJSON = toJSON . toInteger
@@ -1039,93 +1416,147 @@ instance ToJSON Natural where
1039 toEncoding = toEncoding . toInteger
1416 toEncoding = toEncoding . toInteger
1040 {-# INLINE toEncoding #-}
1417 {-# INLINE toEncoding #-}
1041
1418
1419+instance ToJSONKey Natural where
1420+ toJSONKey = toJSONKeyTextEnc (E.integerText . toInteger)
1421+ {-# INLINE toJSONKey #-}
1422+
1042
1423
1043instance ToJSON Int8 where
1424instance ToJSON Int8 where
1044 toJSON = Number . fromIntegral
1425 toJSON = Number . fromIntegral
1045 {-# INLINE toJSON #-}
1426 {-# INLINE toJSON #-}
1046
1427
1047− toEncoding = Encoding . B.int8Dec
1428+ toEncoding = E.int8
1048 {-# INLINE toEncoding #-}
1429 {-# INLINE toEncoding #-}
1049
1430
1431+instance ToJSONKey Int8 where
1432+ toJSONKey = toJSONKeyTextEnc E.int8Text
1433+ {-# INLINE toJSONKey #-}
1434+
1050
1435
1051instance ToJSON Int16 where
1436instance ToJSON Int16 where
1052 toJSON = Number . fromIntegral
1437 toJSON = Number . fromIntegral
1053 {-# INLINE toJSON #-}
1438 {-# INLINE toJSON #-}
1054
1439
1055− toEncoding = Encoding . B.int16Dec
1440+ toEncoding = E.int16
1056 {-# INLINE toEncoding #-}
1441 {-# INLINE toEncoding #-}
1057
1442
1443+instance ToJSONKey Int16 where
1444+ toJSONKey = toJSONKeyTextEnc E.int16Text
1445+ {-# INLINE toJSONKey #-}
1446+
1058
1447
1059instance ToJSON Int32 where
1448instance ToJSON Int32 where
1060 toJSON = Number . fromIntegral
1449 toJSON = Number . fromIntegral
1061 {-# INLINE toJSON #-}
1450 {-# INLINE toJSON #-}
1062
1451
1063− toEncoding = Encoding . B.int32Dec
1452+ toEncoding = E.int32
1064 {-# INLINE toEncoding #-}
1453 {-# INLINE toEncoding #-}
1065
1454
1455+instance ToJSONKey Int32 where
1456+ toJSONKey = toJSONKeyTextEnc E.int32Text
1457+ {-# INLINE toJSONKey #-}
1458+
1066
1459
1067instance ToJSON Int64 where
1460instance ToJSON Int64 where
1068 toJSON = Number . fromIntegral
1461 toJSON = Number . fromIntegral
1069 {-# INLINE toJSON #-}
1462 {-# INLINE toJSON #-}
1070
1463
1071− toEncoding = Encoding . B.int64Dec
1464+ toEncoding = E.int64
1072 {-# INLINE toEncoding #-}
1465 {-# INLINE toEncoding #-}
1073
1466
1467+instance ToJSONKey Int64 where
1468+ toJSONKey = toJSONKeyTextEnc E.int64Text
1469+ {-# INLINE toJSONKey #-}
1074
1470
1075instance ToJSON Word where
1471instance ToJSON Word where
1076 toJSON = Number . fromIntegral
1472 toJSON = Number . fromIntegral
1077 {-# INLINE toJSON #-}
1473 {-# INLINE toJSON #-}
1078
1474
1079− toEncoding = Encoding . B.wordDec
1475+ toEncoding = E.word
1080 {-# INLINE toEncoding #-}
1476 {-# INLINE toEncoding #-}
1081
1477
1478+instance ToJSONKey Word where
1479+ toJSONKey = toJSONKeyTextEnc E.wordText
1480+ {-# INLINE toJSONKey #-}
1481+
1082
1482
1083instance ToJSON Word8 where
1483instance ToJSON Word8 where
1084 toJSON = Number . fromIntegral
1484 toJSON = Number . fromIntegral
1085 {-# INLINE toJSON #-}
1485 {-# INLINE toJSON #-}
1086
1486
1087− toEncoding = Encoding . B.word8Dec
1487+ toEncoding = E.word8
1088 {-# INLINE toEncoding #-}
1488 {-# INLINE toEncoding #-}
1089
1489
1490+instance ToJSONKey Word8 where
1491+ toJSONKey = toJSONKeyTextEnc E.word8Text
1492+ {-# INLINE toJSONKey #-}
1493+
1090
1494
1091instance ToJSON Word16 where
1495instance ToJSON Word16 where
1092 toJSON = Number . fromIntegral
1496 toJSON = Number . fromIntegral
1093 {-# INLINE toJSON #-}
1497 {-# INLINE toJSON #-}
1094
1498
1095− toEncoding = Encoding . B.word16Dec
1499+ toEncoding = E.word16
1096 {-# INLINE toEncoding #-}
1500 {-# INLINE toEncoding #-}
1097
1501
1502+instance ToJSONKey Word16 where
1503+ toJSONKey = toJSONKeyTextEnc E.word16Text
1504+ {-# INLINE toJSONKey #-}
1505+
1098
1506
1099instance ToJSON Word32 where
1507instance ToJSON Word32 where
1100 toJSON = Number . fromIntegral
1508 toJSON = Number . fromIntegral
1101 {-# INLINE toJSON #-}
1509 {-# INLINE toJSON #-}
1102
1510
1103− toEncoding = Encoding . B.word32Dec
1511+ toEncoding = E.word32
1104 {-# INLINE toEncoding #-}
1512 {-# INLINE toEncoding #-}
1105
1513
1514+instance ToJSONKey Word32 where
1515+ toJSONKey = toJSONKeyTextEnc E.word32Text
1516+ {-# INLINE toJSONKey #-}
1517+
1518+
1106instance ToJSON Word64 where
1519instance ToJSON Word64 where
1107 toJSON = Number . fromIntegral
1520 toJSON = Number . fromIntegral
1108 {-# INLINE toJSON #-}
1521 {-# INLINE toJSON #-}
1109
1522
1110− toEncoding = Encoding . B.word64Dec
1523+ toEncoding = E.word64
1524+ {-# INLINE toEncoding #-}
1525+
1526+instance ToJSONKey Word64 where
1527+ toJSONKey = toJSONKeyTextEnc E.word64Text
1528+ {-# INLINE toJSONKey #-}
1529+
1530+instance ToJSON CTime where
1531+ toJSON (CTime i) = toJSON i
1532+ {-# INLINE toJSON #-}
1533+
1534+ toEncoding (CTime i) = toEncoding i
1111 {-# INLINE toEncoding #-}
1535 {-# INLINE toEncoding #-}
1112
1536
1113instance ToJSON Text where
1537instance ToJSON Text where
1114 toJSON = String
1538 toJSON = String
1115 {-# INLINE toJSON #-}
1539 {-# INLINE toJSON #-}
1116
1540
1117− toEncoding = Encoding . EB.text
1541+ toEncoding = E.text
1118 {-# INLINE toEncoding #-}
1542 {-# INLINE toEncoding #-}
1119
1543
1544+instance ToJSONKey Text where
1545+ toJSONKey = toJSONKeyText id
1546+ {-# INLINE toJSONKey #-}
1547+
1548+
1120instance ToJSON LT.Text where
1549instance ToJSON LT.Text where
1121 toJSON = String . LT.toStrict
1550 toJSON = String . LT.toStrict
1122 {-# INLINE toJSON #-}
1551 {-# INLINE toJSON #-}
1123
1552
1124− toEncoding t = Encoding $
1553+ toEncoding = E.lazyText
1125− B.char7 '"' <>
1126− LT.foldrChunks (\x xs -> EB.unquoted x <> xs) (B.char7 '"') t
1127 {-# INLINE toEncoding #-}
1554 {-# INLINE toEncoding #-}
1128
1555
1556+instance ToJSONKey LT.Text where
1557+ toJSONKey = toJSONKeyText LT.toStrict
1558+
1559+
1129instance ToJSON Version where
1560instance ToJSON Version where
1130 toJSON = toJSON . showVersion
1561 toJSON = toJSON . showVersion
1131 {-# INLINE toJSON #-}
1562 {-# INLINE toJSON #-}
@@ -1133,6 +1564,8 @@ instance ToJSON Version where
1133 toEncoding = toEncoding . showVersion
1564 toEncoding = toEncoding . showVersion
1134 {-# INLINE toEncoding #-}
1565 {-# INLINE toEncoding #-}
1135
1566
1567+instance ToJSONKey Version where
1568+ toJSONKey = toJSONKeyText (T.pack . showVersion)
1136
1569
1137-------------------------------------------------------------------------------
1570-------------------------------------------------------------------------------
1138-- semigroups NonEmpty
1571-- semigroups NonEmpty
@@ -1160,7 +1593,138 @@ instance ToJSON Scientific where
1160 toJSON = Number
1593 toJSON = Number
1161 {-# INLINE toJSON #-}
1594 {-# INLINE toJSON #-}
1162
1595
1163− toEncoding = Encoding . EB.number
1596+ toEncoding = E.scientific
1597+ {-# INLINE toEncoding #-}
1598+
1599+instance ToJSONKey Scientific where
1600+ toJSONKey = toJSONKeyTextEnc E.scientificText
1601+
1602+-------------------------------------------------------------------------------
1603+-- DList
1604+-------------------------------------------------------------------------------
1605+
1606+instance ToJSON1 DList.DList where
1607+ liftToJSON t _ = listValue t . toList
1608+ {-# INLINE liftToJSON #-}
1609+
1610+ liftToEncoding t _ = listEncoding t . toList
1611+ {-# INLINE liftToEncoding #-}
1612+
1613+instance (ToJSON a) => ToJSON (DList.DList a) where
1614+ toJSON = toJSON1
1615+ {-# INLINE toJSON #-}
1616+
1617+ toEncoding = toEncoding1
1618+ {-# INLINE toEncoding #-}
1619+
1620+-------------------------------------------------------------------------------
1621+-- transformers - Functors
1622+-------------------------------------------------------------------------------
1623+
1624+instance ToJSON1 Identity where
1625+ liftToJSON t _ (Identity a) = t a
1626+ {-# INLINE liftToJSON #-}
1627+
1628+ liftToJSONList _ tl xs = tl (map runIdentity xs)
1629+ {-# INLINE liftToJSONList #-}
1630+
1631+ liftToEncoding t _ (Identity a) = t a
1632+ {-# INLINE liftToEncoding #-}
1633+
1634+ liftToEncodingList _ tl xs = tl (map runIdentity xs)
1635+ {-# INLINE liftToEncodingList #-}
1636+
1637+instance (ToJSON a) => ToJSON (Identity a) where
1638+ toJSON = toJSON1
1639+ {-# INLINE toJSON #-}
1640+
1641+ toJSONList = liftToJSONList toJSON toJSONList
1642+ {-# INLINE toJSONList #-}
1643+
1644+ toEncoding = toEncoding1
1645+ {-# INLINE toEncoding #-}
1646+
1647+ toEncodingList = liftToEncodingList toEncoding toEncodingList
1648+ {-# INLINE toEncodingList #-}
1649+
1650+instance (ToJSONKey a) => ToJSONKey (Identity a) where
1651+ toJSONKey = contramapToJSONKeyFunction runIdentity toJSONKey
1652+ toJSONKeyList = contramapToJSONKeyFunction (map runIdentity) toJSONKeyList
1653+
1654+
1655+instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Compose f g) where
1656+ liftToJSON tv tvl (Compose x) = liftToJSON g gl x
1657+ where
1658+ g = liftToJSON tv tvl
1659+ gl = liftToJSONList tv tvl
1660+ {-# INLINE liftToJSON #-}
1661+
1662+ liftToJSONList te tel xs = liftToJSONList g gl (map getCompose xs)
1663+ where
1664+ g = liftToJSON te tel
1665+ gl = liftToJSONList te tel
1666+ {-# INLINE liftToJSONList #-}
1667+
1668+ liftToEncoding te tel (Compose x) = liftToEncoding g gl x
1669+ where
1670+ g = liftToEncoding te tel
1671+ gl = liftToEncodingList te tel
1672+ {-# INLINE liftToEncoding #-}
1673+
1674+ liftToEncodingList te tel xs = liftToEncodingList g gl (map getCompose xs)
1675+ where
1676+ g = liftToEncoding te tel
1677+ gl = liftToEncodingList te tel
1678+ {-# INLINE liftToEncodingList #-}
1679+
1680+instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Compose f g a) where
1681+ toJSON = toJSON1
1682+ {-# INLINE toJSON #-}
1683+
1684+ toJSONList = liftToJSONList toJSON toJSONList
1685+ {-# INLINE toJSONList #-}
1686+
1687+ toEncoding = toEncoding1
1688+ {-# INLINE toEncoding #-}
1689+
1690+ toEncodingList = liftToEncodingList toEncoding toEncodingList
1691+ {-# INLINE toEncodingList #-}
1692+
1693+
1694+instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Product f g) where
1695+ liftToJSON tv tvl (Pair x y) = liftToJSON2 tx txl ty tyl (x, y)
1696+ where
1697+ tx = liftToJSON tv tvl
1698+ txl = liftToJSONList tv tvl
1699+ ty = liftToJSON tv tvl
1700+ tyl = liftToJSONList tv tvl
1701+
1702+ liftToEncoding te tel (Pair x y) = liftToEncoding2 tx txl ty tyl (x, y)
1703+ where
1704+ tx = liftToEncoding te tel
1705+ txl = liftToEncodingList te tel
1706+ ty = liftToEncoding te tel
1707+ tyl = liftToEncodingList te tel
1708+
1709+instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Product f g a) where
1710+ toJSON = toJSON1
1711+ {-# INLINE toJSON #-}
1712+
1713+ toEncoding = toEncoding1
1714+ {-# INLINE toEncoding #-}
1715+
1716+instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Sum f g) where
1717+ liftToJSON tv tvl (InL x) = Object $ H.singleton "InL" (liftToJSON tv tvl x)
1718+ liftToJSON tv tvl (InR y) = Object $ H.singleton "InR" (liftToJSON tv tvl y)
1719+
1720+ liftToEncoding te tel (InL x) = E.pairs $ E.pair "InL" $ liftToEncoding te tel x
1721+ liftToEncoding te tel (InR y) = E.pairs $ E.pair "InR" $ liftToEncoding te tel y
1722+
1723+instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Sum f g a) where
1724+ toJSON = toJSON1
1725+ {-# INLINE toJSON #-}
1726+
1727+ toEncoding = toEncoding1
1164 {-# INLINE toEncoding #-}
1728 {-# INLINE toEncoding #-}
1165
1729
1166-------------------------------------------------------------------------------
1730-------------------------------------------------------------------------------
@@ -1228,17 +1792,15 @@ instance ToJSON a => ToJSON (IntMap.IntMap a) where
1228
1792
1229instance ToJSONKey k => ToJSON1 (M.Map k) where
1793instance ToJSONKey k => ToJSON1 (M.Map k) where
1230 liftToJSON g _ = case toJSONKey of
1794 liftToJSON g _ = case toJSONKey of
1231− ToJSONKeyText (f,_) -> Object . mapHashKeyVal f g
1795+ ToJSONKeyText f _ -> Object . mapHashKeyVal f g
1232− ToJSONKeyValue (f,_) -> Array . V.fromList . map (toJSONPair f g) . M.toList
1796+ ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . M.toList
1233 {-# INLINE liftToJSON #-}
1797 {-# INLINE liftToJSON #-}
1234
1798
1235− -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> M.Map k a -> Encoding
1236 liftToEncoding g _ = case toJSONKey of
1799 liftToEncoding g _ = case toJSONKey of
1237− ToJSONKeyText (_,f) -> dict f g M.foldrWithKey
1800+ ToJSONKeyText _ f -> dict f g M.foldrWithKey
1238− ToJSONKeyValue (_,f) -> listEncoding (pairEncoding f) . M.toList
1801+ ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . M.toList
1239 where
1802 where
1240− -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
1803+ pairEncoding f (a, b) = E.list id [f a, g b]
1241− pairEncoding f (a, b) = tuple $ f a >*< g b
1242 {-# INLINE liftToEncoding #-}
1804 {-# INLINE liftToEncoding #-}
1243
1805
1244
1806
@@ -1276,6 +1838,17 @@ instance (ToJSON v) => ToJSON (Tree.Tree v) where
1276 toEncoding = toEncoding1
1838 toEncoding = toEncoding1
1277 {-# INLINE toEncoding #-}
1839 {-# INLINE toEncoding #-}
1278
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
1279
1852
1280-------------------------------------------------------------------------------
1853-------------------------------------------------------------------------------
1281-- vector
1854-- vector
@@ -1285,29 +1858,20 @@ instance ToJSON1 Vector where
1285 liftToJSON t _ = Array . V.map t
1858 liftToJSON t _ = Array . V.map t
1286 {-# INLINE liftToJSON #-}
1859 {-# INLINE liftToJSON #-}
1287
1860
1288− liftToEncoding t _ xs
1861+ liftToEncoding t _ = listEncoding t . V.toList
1289− | V.null xs = emptyArray_
1290− | otherwise = Encoding $
1291− B.char7 '[' <> fromEncoding (t (V.unsafeHead xs)) <>
1292− V.foldr go (B.char7 ']') (V.unsafeTail xs)
1293− where
1294− go v b = B.char7 ',' <> fromEncoding (t v) <> b
1295 {-# INLINE liftToEncoding #-}
1862 {-# INLINE liftToEncoding #-}
1296
1863
1297instance (ToJSON a) => ToJSON (Vector a) where
1864instance (ToJSON a) => ToJSON (Vector a) where
1298− toJSON = Array . V.map toJSON
1865+ {-# SPECIALIZE instance ToJSON Array #-}
1866+
1867+ toJSON = toJSON1
1299 {-# INLINE toJSON #-}
1868 {-# INLINE toJSON #-}
1300
1869
1301− toEncoding = encodeVector
1870+ toEncoding = toEncoding1
1302 {-# INLINE toEncoding #-}
1871 {-# INLINE toEncoding #-}
1303
1872
1304encodeVector :: (ToJSON a, VG.Vector v a) => v a -> Encoding
1873encodeVector :: (ToJSON a, VG.Vector v a) => v a -> Encoding
1305−encodeVector xs
1874+encodeVector = listEncoding toEncoding . VG.toList
1306− | VG.null xs = emptyArray_
1307− | otherwise = Encoding $
1308− B.char7 '[' <> builder (VG.unsafeHead xs) <>
1309− VG.foldr go (B.char7 ']') (VG.unsafeTail xs)
1310− where go v b = B.char7 ',' <> builder v <> b
1311{-# INLINE encodeVector #-}
1875{-# INLINE encodeVector #-}
1312
1876
1313vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
1877vectorToJSON :: (VG.Vector v a, ToJSON a) => v a -> Value
@@ -1358,20 +1922,21 @@ instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
1358
1922
1359instance ToJSONKey k => ToJSON1 (H.HashMap k) where
1923instance ToJSONKey k => ToJSON1 (H.HashMap k) where
1360 liftToJSON g _ = case toJSONKey of
1924 liftToJSON g _ = case toJSONKey of
1361− ToJSONKeyText (f,_) -> Object . mapKeyVal f g
1925+ ToJSONKeyText f _ -> Object . mapKeyVal f g
1362− ToJSONKeyValue (f,_) -> Array . V.fromList . map (toJSONPair f g) . H.toList
1926+ ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . H.toList
1363 {-# INLINE liftToJSON #-}
1927 {-# INLINE liftToJSON #-}
1364
1928
1365 -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> H.HashMap k a -> Encoding
1929 -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> H.HashMap k a -> Encoding
1366 liftToEncoding g _ = case toJSONKey of
1930 liftToEncoding g _ = case toJSONKey of
1367− ToJSONKeyText (_,f) -> dict f g H.foldrWithKey
1931+ ToJSONKeyText _ f -> dict f g H.foldrWithKey
1368− ToJSONKeyValue (_,f) -> listEncoding (pairEncoding f) . H.toList
1932+ ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . H.toList
1369 where
1933 where
1370− -- pairEncoding :: (k -> Encoding) -> (k, a) -> Encoding
1934+ pairEncoding f (a, b) = E.list id [f a, g b]
1371− pairEncoding f (a, b) = tuple $ f a >*< g b
1372 {-# INLINE liftToEncoding #-}
1935 {-# INLINE liftToEncoding #-}
1373
1936
1374instance (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+
1375 toJSON = toJSON1
1940 toJSON = toJSON1
1376 {-# INLINE toJSON #-}
1941 {-# INLINE toJSON #-}
1377
1942
@@ -1386,7 +1951,7 @@ instance ToJSON Value where
1386 toJSON a = a
1951 toJSON a = a
1387 {-# INLINE toJSON #-}
1952 {-# INLINE toJSON #-}
1388
1953
1389− toEncoding = Encoding . EB.encodeToBuilder
1954+ toEncoding = E.value
1390 {-# INLINE toEncoding #-}
1955 {-# INLINE toEncoding #-}
1391
1956
1392instance ToJSON DotNetTime where
1957instance ToJSON DotNetTime where
@@ -1398,69 +1963,159 @@ dotNetTime :: DotNetTime -> String
1398dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
1963dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
1399 where secs = formatTime defaultTimeLocale "/Date(%s" t
1964 where secs = formatTime defaultTimeLocale "/Date(%s" t
1400
1965
1966+formatMillis :: (FormatTime t) => t -> String
1967+formatMillis = take 3 . formatTime defaultTimeLocale "%q"
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+
1401-------------------------------------------------------------------------------
1995-------------------------------------------------------------------------------
1402-- time
1996-- time
1403-------------------------------------------------------------------------------
1997-------------------------------------------------------------------------------
1404
1998
1405instance ToJSON Day where
1999instance ToJSON Day where
1406− toJSON = stringEncoding
2000+ toJSON = stringEncoding . E.day
1407− toEncoding z = Encoding (EB.quote $ EB.day z)
2001+ toEncoding = E.day
2002+
2003+instance ToJSONKey Day where
2004+ toJSONKey = toJSONKeyTextEnc E.day
1408
2005
1409
2006
1410instance ToJSON TimeOfDay where
2007instance ToJSON TimeOfDay where
1411− toJSON = stringEncoding
2008+ toJSON = stringEncoding . E.timeOfDay
1412− toEncoding z = Encoding (EB.quote $ EB.timeOfDay z)
2009+ toEncoding = E.timeOfDay
2010+
2011+instance ToJSONKey TimeOfDay where
2012+ toJSONKey = toJSONKeyTextEnc E.timeOfDay
1413
2013
1414
2014
1415instance ToJSON LocalTime where
2015instance ToJSON LocalTime where
1416− toJSON = stringEncoding
2016+ toJSON = stringEncoding . E.localTime
1417− toEncoding z = Encoding (EB.quote $ EB.localTime z)
2017+ toEncoding = E.localTime
2018+
2019+instance ToJSONKey LocalTime where
2020+ toJSONKey = toJSONKeyTextEnc E.localTime
2021+
1418
2022
1419instance ToJSON ZonedTime where
2023instance ToJSON ZonedTime where
1420− toJSON = stringEncoding
2024+ toJSON = stringEncoding . E.zonedTime
2025+ toEncoding = E.zonedTime
1421
2026
1422− toEncoding z = Encoding (EB.quote $ EB.zonedTime z)
2027+instance ToJSONKey ZonedTime where
2028+ toJSONKey = toJSONKeyTextEnc E.zonedTime
1423
2029
1424−formatMillis :: (FormatTime t) => t -> String
1425−formatMillis = take 3 . formatTime defaultTimeLocale "%q"
1426
2030
1427instance ToJSON UTCTime where
2031instance ToJSON UTCTime where
1428− toJSON = stringEncoding
2032+ toJSON = stringEncoding . E.utcTime
2033+ toEncoding = E.utcTime
1429
2034
1430− toEncoding t = Encoding (EB.quote $ EB.utcTime t)
2035+instance ToJSONKey UTCTime where
2036+ toJSONKey = toJSONKeyTextEnc E.utcTime
1431
2037
1432-- | Encode something t a JSON string.
2038-- | Encode something t a JSON string.
1433−--
2039+stringEncoding :: Encoding' Text -> Value
1434−-- TODO: remove me, this is a hack
1435−stringEncoding :: (ToJSON a) => a -> Value
1436stringEncoding = String
2040stringEncoding = String
1437 . T.dropAround (== '"')
2041 . T.dropAround (== '"')
1438 . T.decodeLatin1
2042 . T.decodeLatin1
1439 . L.toStrict
2043 . L.toStrict
1440− . B.toLazyByteString
2044+ . E.encodingToLazyByteString
1441− . fromEncoding
1442− . toEncoding
1443{-# INLINE stringEncoding #-}
2045{-# INLINE stringEncoding #-}
1444
2046
2047+
1445instance ToJSON NominalDiffTime where
2048instance ToJSON NominalDiffTime where
1446 toJSON = Number . realToFrac
2049 toJSON = Number . realToFrac
1447 {-# INLINE toJSON #-}
2050 {-# INLINE toJSON #-}
1448
2051
1449− toEncoding = Encoding . EB.number . realToFrac
2052+ toEncoding = E.scientific . realToFrac
1450 {-# INLINE toEncoding #-}
2053 {-# INLINE toEncoding #-}
1451
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+
1452-------------------------------------------------------------------------------
2107-------------------------------------------------------------------------------
1453-- base Monoid/Semigroup
2108-- base Monoid/Semigroup
1454-------------------------------------------------------------------------------
2109-------------------------------------------------------------------------------
1455
2110
1456−instance ToJSON1 Dual where
2111+instance ToJSON1 Monoid.Dual where
1457− liftToJSON t _ = t . getDual
2112+ liftToJSON t _ = t . Monoid.getDual
1458 {-# INLINE liftToJSON #-}
2113 {-# INLINE liftToJSON #-}
1459
2114
1460− liftToEncoding t _ = t . getDual
2115+ liftToEncoding t _ = t . Monoid.getDual
1461 {-# INLINE liftToEncoding #-}
2116 {-# INLINE liftToEncoding #-}
1462
2117
1463−instance ToJSON a => ToJSON (Dual a) where
2118+instance ToJSON a => ToJSON (Monoid.Dual a) where
1464 toJSON = toJSON1
2119 toJSON = toJSON1
1465 {-# INLINE toJSON #-}
2120 {-# INLINE toJSON #-}
1466
2121
@@ -1468,14 +2123,14 @@ instance ToJSON a => ToJSON (Dual a) where
1468 {-# INLINE toEncoding #-}
2123 {-# INLINE toEncoding #-}
1469
2124
1470
2125
1471−instance ToJSON1 First where
2126+instance ToJSON1 Monoid.First where
1472− liftToJSON t to' = liftToJSON t to' . getFirst
2127+ liftToJSON t to' = liftToJSON t to' . Monoid.getFirst
1473 {-# INLINE liftToJSON #-}
2128 {-# INLINE liftToJSON #-}
1474
2129
1475− liftToEncoding t to' = liftToEncoding t to' . getFirst
2130+ liftToEncoding t to' = liftToEncoding t to' . Monoid.getFirst
1476 {-# INLINE liftToEncoding #-}
2131 {-# INLINE liftToEncoding #-}
1477
2132
1478−instance ToJSON a => ToJSON (First a) where
2133+instance ToJSON a => ToJSON (Monoid.First a) where
1479 toJSON = toJSON1
2134 toJSON = toJSON1
1480 {-# INLINE toJSON #-}
2135 {-# INLINE toJSON #-}
1481
2136
@@ -1483,14 +2138,103 @@ instance ToJSON a => ToJSON (First a) where
1483 {-# INLINE toEncoding #-}
2138 {-# INLINE toEncoding #-}
1484
2139
1485
2140
1486−instance ToJSON1 Last where
2141+instance ToJSON1 Monoid.Last where
1487− liftToJSON t to' = liftToJSON t to' . getLast
2142+ liftToJSON t to' = liftToJSON t to' . Monoid.getLast
1488 {-# INLINE liftToJSON #-}
2143 {-# INLINE liftToJSON #-}
1489
2144
1490− liftToEncoding t to' = liftToEncoding t to' . getLast
2145+ liftToEncoding t to' = liftToEncoding t to' . Monoid.getLast
1491 {-# INLINE liftToEncoding #-}
2146 {-# INLINE liftToEncoding #-}
1492
2147
1493−instance ToJSON a => ToJSON (Last a) where
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
2202+ {-# INLINE liftToJSON #-}
2203+
2204+ liftToEncoding t _ (Semigroup.Last x) = t x
2205+ {-# INLINE liftToEncoding #-}
2206+
2207+instance ToJSON a => ToJSON (Semigroup.Last a) where
2208+ toJSON = toJSON1
2209+ {-# INLINE toJSON #-}
2210+
2211+ toEncoding = toEncoding1
2212+ {-# INLINE toEncoding #-}
2213+
2214+
2215+instance ToJSON1 Semigroup.WrappedMonoid where
2216+ liftToJSON t _ (Semigroup.WrapMonoid x) = t x
2217+ {-# INLINE liftToJSON #-}
2218+
2219+ liftToEncoding t _ (Semigroup.WrapMonoid x) = t x
2220+ {-# INLINE liftToEncoding #-}
2221+
2222+instance ToJSON a => ToJSON (Semigroup.WrappedMonoid a) where
2223+ toJSON = toJSON1
2224+ {-# INLINE toJSON #-}
2225+
2226+ toEncoding = toEncoding1
2227+ {-# INLINE toEncoding #-}
2228+
2229+
2230+instance ToJSON1 Semigroup.Option where
2231+ liftToJSON t to' = liftToJSON t to' . Semigroup.getOption
2232+ {-# INLINE liftToJSON #-}
2233+
2234+ liftToEncoding t to' = liftToEncoding t to' . Semigroup.getOption
2235+ {-# INLINE liftToEncoding #-}
2236+
2237+instance ToJSON a => ToJSON (Semigroup.Option a) where
1494 toJSON = toJSON1
2238 toJSON = toJSON1
1495 {-# INLINE toJSON #-}
2239 {-# INLINE toJSON #-}
1496
2240
@@ -1501,14 +2245,28 @@ instance ToJSON a => ToJSON (Last a) where
1501-- tagged
2245-- tagged
1502-------------------------------------------------------------------------------
2246-------------------------------------------------------------------------------
1503
2247
2248+instance ToJSON1 Proxy where
2249+ liftToJSON _ _ _ = Null
2250+ {-# INLINE liftToJSON #-}
2251+
2252+ liftToEncoding _ _ _ = E.null_
2253+ {-# INLINE liftToEncoding #-}
2254+
1504instance ToJSON (Proxy a) where
2255instance ToJSON (Proxy a) where
1505 toJSON _ = Null
2256 toJSON _ = Null
1506 {-# INLINE toJSON #-}
2257 {-# INLINE toJSON #-}
1507
2258
1508− toEncoding _ = Encoding EB.null_
2259+ toEncoding _ = E.null_
1509 {-# INLINE toEncoding #-}
2260 {-# INLINE toEncoding #-}
1510
2261
1511
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+
1512instance ToJSON1 (Tagged a) where
2270instance ToJSON1 (Tagged a) where
1513 liftToJSON t _ (Tagged x) = t x
2271 liftToJSON t _ (Tagged x) = t x
1514 {-# INLINE liftToJSON #-}
2272 {-# INLINE liftToJSON #-}
@@ -1523,7 +2281,6 @@ instance ToJSON b => ToJSON (Tagged a b) where
1523 toEncoding = toEncoding1
2281 toEncoding = toEncoding1
1524 {-# INLINE toEncoding #-}
2282 {-# INLINE toEncoding #-}
1525
2283
1526−
1527instance ToJSONKey b => ToJSONKey (Tagged a b) where
2284instance ToJSONKey b => ToJSONKey (Tagged a b) where
1528 toJSONKey = contramapToJSONKeyFunction unTagged toJSONKey
2285 toJSONKey = contramapToJSONKeyFunction unTagged toJSONKey
1529 toJSONKeyList = contramapToJSONKeyFunction (fmap unTagged) toJSONKeyList
2286 toJSONKeyList = contramapToJSONKeyFunction (fmap unTagged) toJSONKeyList
@@ -1532,41 +2289,22 @@ instance ToJSONKey b => ToJSONKey (Tagged a b) where
1532-- Instances for converting t map keys
2289-- Instances for converting t map keys
1533-------------------------------------------------------------------------------
2290-------------------------------------------------------------------------------
1534
2291
1535−instance ToJSONKey Text where
1536− toJSONKey = ToJSONKeyText (id,toEncoding)
1537−
1538−
1539−instance ToJSONKey Bool where
1540− toJSONKey = ToJSONKeyText
1541− ( (\x -> if x then "true" else "false")
1542− , (\x -> Encoding $ if x then "\"true\"" else "\"false\"")
1543− )
1544−
1545−instance ToJSONKey Int where
1546− toJSONKey = ToJSONKeyText
1547− ( LT.toStrict . LTB.toLazyText . LTBI.decimal
1548− , \x -> Encoding $ B.char7 '"' <> fromEncoding (toEncoding x) <> B.char7 '"'
1549− )
1550−
1551instance (ToJSON a, ToJSON b) => ToJSONKey (a,b)
2292instance (ToJSON a, ToJSON b) => ToJSONKey (a,b)
1552instance (ToJSON a, ToJSON b, ToJSON c) => ToJSONKey (a,b,c)
2293instance (ToJSON a, ToJSON b, ToJSON c) => ToJSONKey (a,b,c)
1553instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSONKey (a,b,c,d)
2294instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSONKey (a,b,c,d)
1554
2295
1555instance ToJSONKey Char where
2296instance ToJSONKey Char where
1556− toJSONKey = ToJSONKeyText (T.singleton, toEncoding)
2297+ toJSONKey = ToJSONKeyText T.singleton (E.string . (:[]))
1557− toJSONKeyList = ToJSONKeyText (T.pack, toEncoding . T.pack)
2298+ toJSONKeyList = toJSONKeyText T.pack
1558
2299
1559instance (ToJSONKey a, ToJSON a) => ToJSONKey [a] where
2300instance (ToJSONKey a, ToJSON a) => ToJSONKey [a] where
1560 toJSONKey = toJSONKeyList
2301 toJSONKey = toJSONKeyList
1561
2302
1562−instance (ToJSONKey a, ToJSON a) => ToJSONKey (Identity a) where
1563− toJSONKey = contramapToJSONKeyFunction runIdentity toJSONKey
1564−
1565-------------------------------------------------------------------------------
2303-------------------------------------------------------------------------------
1566−-- Tuple instances, see tuple-instances-to.hs
2304+-- Tuple instances
1567-------------------------------------------------------------------------------
2305-------------------------------------------------------------------------------
1568
2306
1569−instance ToJSON2 ((,) ) where
2307+instance ToJSON2 (,) where
1570 liftToJSON2 toA _ toB _ (a, b) = Array $ V.create $ do
2308 liftToJSON2 toA _ toB _ (a, b) = Array $ V.create $ do
1571 mv <- VM.unsafeNew 2
2309 mv <- VM.unsafeNew 2
1572 VM.unsafeWrite mv 0 (toA a)
2310 VM.unsafeWrite mv 0 (toA a)
@@ -1574,9 +2312,7 @@ instance ToJSON2 ((,) ) where
1574 return mv
2312 return mv
1575 {-# INLINE liftToJSON2 #-}
2313 {-# INLINE liftToJSON2 #-}
1576
2314
1577− liftToEncoding2 toA _ toB _ (a, b) = tuple $
2315+ liftToEncoding2 toA _ toB _ (a, b) = E.list id [toA a, toB b]
1578− toA a >*<
1579− toB b
1580 {-# INLINE liftToEncoding2 #-}
2316 {-# INLINE liftToEncoding2 #-}
1581
2317
1582instance (ToJSON a) => ToJSON1 ((,) a) where
2318instance (ToJSON a) => ToJSON1 ((,) a) where
@@ -1600,10 +2336,11 @@ instance (ToJSON a) => ToJSON2 ((,,) a) where
1600 return mv
2336 return mv
1601 {-# INLINE liftToJSON2 #-}
2337 {-# INLINE liftToJSON2 #-}
1602
2338
1603− liftToEncoding2 toB _ toC _ (a, b, c) = tuple $
2339+ liftToEncoding2 toB _ toC _ (a, b, c) = E.list id
1604− toEncoding a >*<
2340+ [ toEncoding a
1605− toB b >*<
2341+ , toB b
1606− toC c
2342+ , toC c
2343+ ]
1607 {-# INLINE liftToEncoding2 #-}
2344 {-# INLINE liftToEncoding2 #-}
1608
2345
1609instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where
2346instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where
@@ -1628,11 +2365,12 @@ instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where
1628 return mv
2365 return mv
1629 {-# INLINE liftToJSON2 #-}
2366 {-# INLINE liftToJSON2 #-}
1630
2367
1631− liftToEncoding2 toC _ toD _ (a, b, c, d) = tuple $
2368+ liftToEncoding2 toC _ toD _ (a, b, c, d) = E.list id
1632− toEncoding a >*<
2369+ [ toEncoding a
1633− toEncoding b >*<
2370+ , toEncoding b
1634− toC c >*<
2371+ , toC c
1635− toD d
2372+ , toD d
2373+ ]
1636 {-# INLINE liftToEncoding2 #-}
2374 {-# INLINE liftToEncoding2 #-}
1637
2375
1638instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where
2376instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where
@@ -1658,12 +2396,13 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where
1658 return mv
2396 return mv
1659 {-# INLINE liftToJSON2 #-}
2397 {-# INLINE liftToJSON2 #-}
1660
2398
1661− liftToEncoding2 toD _ toE _ (a, b, c, d, e) = tuple $
2399+ liftToEncoding2 toD _ toE _ (a, b, c, d, e) = E.list id
1662− toEncoding a >*<
2400+ [ toEncoding a
1663− toEncoding b >*<
2401+ , toEncoding b
1664− toEncoding c >*<
2402+ , toEncoding c
1665− toD d >*<
2403+ , toD d
1666− toE e
2404+ , toE e
2405+ ]
1667 {-# INLINE liftToEncoding2 #-}
2406 {-# INLINE liftToEncoding2 #-}
1668
2407
1669instance (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
@@ -1690,13 +2429,14 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) w
1690 return mv
2429 return mv
1691 {-# INLINE liftToJSON2 #-}
2430 {-# INLINE liftToJSON2 #-}
1692
2431
1693− liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = tuple $
2432+ liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = E.list id
1694− toEncoding a >*<
2433+ [ toEncoding a
1695− toEncoding b >*<
2434+ , toEncoding b
1696− toEncoding c >*<
2435+ , toEncoding c
1697− toEncoding d >*<
2436+ , toEncoding d
1698− toE e >*<
2437+ , toE e
1699− toF f
2438+ , toF f
2439+ ]
1700 {-# INLINE liftToEncoding2 #-}
2440 {-# INLINE liftToEncoding2 #-}
1701
2441
1702instance (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
@@ -1724,14 +2464,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,)
1724 return mv
2464 return mv
1725 {-# INLINE liftToJSON2 #-}
2465 {-# INLINE liftToJSON2 #-}
1726
2466
1727− 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
1728− toEncoding a >*<
2468+ [ toEncoding a
1729− toEncoding b >*<
2469+ , toEncoding b
1730− toEncoding c >*<
2470+ , toEncoding c
1731− toEncoding d >*<
2471+ , toEncoding d
1732− toEncoding e >*<
2472+ , toEncoding e
1733− toF f >*<
2473+ , toF f
1734− toG g
2474+ , toG g
2475+ ]
1735 {-# INLINE liftToEncoding2 #-}
2476 {-# INLINE liftToEncoding2 #-}
1736
2477
1737instance (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
@@ -1760,15 +2501,16 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2
1760 return mv
2501 return mv
1761 {-# INLINE liftToJSON2 #-}
2502 {-# INLINE liftToJSON2 #-}
1762
2503
1763− 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
1764− toEncoding a >*<
2505+ [ toEncoding a
1765− toEncoding b >*<
2506+ , toEncoding b
1766− toEncoding c >*<
2507+ , toEncoding c
1767− toEncoding d >*<
2508+ , toEncoding d
1768− toEncoding e >*<
2509+ , toEncoding e
1769− toEncoding f >*<
2510+ , toEncoding f
1770− toG g >*<
2511+ , toG g
1771− toH h
2512+ , toH h
2513+ ]
1772 {-# INLINE liftToEncoding2 #-}
2514 {-# INLINE liftToEncoding2 #-}
1773
2515
1774instance (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
@@ -1798,16 +2540,17 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g)
1798 return mv
2540 return mv
1799 {-# INLINE liftToJSON2 #-}
2541 {-# INLINE liftToJSON2 #-}
1800
2542
1801− 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
1802− toEncoding a >*<
2544+ [ toEncoding a
1803− toEncoding b >*<
2545+ , toEncoding b
1804− toEncoding c >*<
2546+ , toEncoding c
1805− toEncoding d >*<
2547+ , toEncoding d
1806− toEncoding e >*<
2548+ , toEncoding e
1807− toEncoding f >*<
2549+ , toEncoding f
1808− toEncoding g >*<
2550+ , toEncoding g
1809− toH h >*<
2551+ , toH h
1810− toI i
2552+ , toI i
2553+ ]
1811 {-# INLINE liftToEncoding2 #-}
2554 {-# INLINE liftToEncoding2 #-}
1812
2555
1813instance (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
@@ -1838,17 +2581,18 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
1838 return mv
2581 return mv
1839 {-# INLINE liftToJSON2 #-}
2582 {-# INLINE liftToJSON2 #-}
1840
2583
1841− 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
1842− toEncoding a >*<
2585+ [ toEncoding a
1843− toEncoding b >*<
2586+ , toEncoding b
1844− toEncoding c >*<
2587+ , toEncoding c
1845− toEncoding d >*<
2588+ , toEncoding d
1846− toEncoding e >*<
2589+ , toEncoding e
1847− toEncoding f >*<
2590+ , toEncoding f
1848− toEncoding g >*<
2591+ , toEncoding g
1849− toEncoding h >*<
2592+ , toEncoding h
1850− toI i >*<
2593+ , toI i
1851− toJ j
2594+ , toJ j
2595+ ]
1852 {-# INLINE liftToEncoding2 #-}
2596 {-# INLINE liftToEncoding2 #-}
1853
2597
1854instance (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
@@ -1880,18 +2624,19 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
1880 return mv
2624 return mv
1881 {-# INLINE liftToJSON2 #-}
2625 {-# INLINE liftToJSON2 #-}
1882
2626
1883− 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
1884− toEncoding a >*<
2628+ [ toEncoding a
1885− toEncoding b >*<
2629+ , toEncoding b
1886− toEncoding c >*<
2630+ , toEncoding c
1887− toEncoding d >*<
2631+ , toEncoding d
1888− toEncoding e >*<
2632+ , toEncoding e
1889− toEncoding f >*<
2633+ , toEncoding f
1890− toEncoding g >*<
2634+ , toEncoding g
1891− toEncoding h >*<
2635+ , toEncoding h
1892− toEncoding i >*<
2636+ , toEncoding i
1893− toJ j >*<
2637+ , toJ j
1894− toK k
2638+ , toK k
2639+ ]
1895 {-# INLINE liftToEncoding2 #-}
2640 {-# INLINE liftToEncoding2 #-}
1896
2641
1897instance (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
@@ -1924,19 +2669,20 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
1924 return mv
2669 return mv
1925 {-# INLINE liftToJSON2 #-}
2670 {-# INLINE liftToJSON2 #-}
1926
2671
1927− 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
1928− toEncoding a >*<
2673+ [ toEncoding a
1929− toEncoding b >*<
2674+ , toEncoding b
1930− toEncoding c >*<
2675+ , toEncoding c
1931− toEncoding d >*<
2676+ , toEncoding d
1932− toEncoding e >*<
2677+ , toEncoding e
1933− toEncoding f >*<
2678+ , toEncoding f
1934− toEncoding g >*<
2679+ , toEncoding g
1935− toEncoding h >*<
2680+ , toEncoding h
1936− toEncoding i >*<
2681+ , toEncoding i
1937− toEncoding j >*<
2682+ , toEncoding j
1938− toK k >*<
2683+ , toK k
1939− toL l
2684+ , toL l
2685+ ]
1940 {-# INLINE liftToEncoding2 #-}
2686 {-# INLINE liftToEncoding2 #-}
1941
2687
1942instance (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
@@ -1970,20 +2716,21 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
1970 return mv
2716 return mv
1971 {-# INLINE liftToJSON2 #-}
2717 {-# INLINE liftToJSON2 #-}
1972
2718
1973− 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
1974− toEncoding a >*<
2720+ [ toEncoding a
1975− toEncoding b >*<
2721+ , toEncoding b
1976− toEncoding c >*<
2722+ , toEncoding c
1977− toEncoding d >*<
2723+ , toEncoding d
1978− toEncoding e >*<
2724+ , toEncoding e
1979− toEncoding f >*<
2725+ , toEncoding f
1980− toEncoding g >*<
2726+ , toEncoding g
1981− toEncoding h >*<
2727+ , toEncoding h
1982− toEncoding i >*<
2728+ , toEncoding i
1983− toEncoding j >*<
2729+ , toEncoding j
1984− toEncoding k >*<
2730+ , toEncoding k
1985− toL l >*<
2731+ , toL l
1986− toM m
2732+ , toM m
2733+ ]
1987 {-# INLINE liftToEncoding2 #-}
2734 {-# INLINE liftToEncoding2 #-}
1988
2735
1989instance (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
@@ -2018,21 +2765,22 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2018 return mv
2765 return mv
2019 {-# INLINE liftToJSON2 #-}
2766 {-# INLINE liftToJSON2 #-}
2020
2767
2021− 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
2022− toEncoding a >*<
2769+ [ toEncoding a
2023− toEncoding b >*<
2770+ , toEncoding b
2024− toEncoding c >*<
2771+ , toEncoding c
2025− toEncoding d >*<
2772+ , toEncoding d
2026− toEncoding e >*<
2773+ , toEncoding e
2027− toEncoding f >*<
2774+ , toEncoding f
2028− toEncoding g >*<
2775+ , toEncoding g
2029− toEncoding h >*<
2776+ , toEncoding h
2030− toEncoding i >*<
2777+ , toEncoding i
2031− toEncoding j >*<
2778+ , toEncoding j
2032− toEncoding k >*<
2779+ , toEncoding k
2033− toEncoding l >*<
2780+ , toEncoding l
2034− toM m >*<
2781+ , toM m
2035− toN n
2782+ , toN n
2783+ ]
2036 {-# INLINE liftToEncoding2 #-}
2784 {-# INLINE liftToEncoding2 #-}
2037
2785
2038instance (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
@@ -2068,22 +2816,23 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2068 return mv
2816 return mv
2069 {-# INLINE liftToJSON2 #-}
2817 {-# INLINE liftToJSON2 #-}
2070
2818
2071− 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
2072− toEncoding a >*<
2820+ [ toEncoding a
2073− toEncoding b >*<
2821+ , toEncoding b
2074− toEncoding c >*<
2822+ , toEncoding c
2075− toEncoding d >*<
2823+ , toEncoding d
2076− toEncoding e >*<
2824+ , toEncoding e
2077− toEncoding f >*<
2825+ , toEncoding f
2078− toEncoding g >*<
2826+ , toEncoding g
2079− toEncoding h >*<
2827+ , toEncoding h
2080− toEncoding i >*<
2828+ , toEncoding i
2081− toEncoding j >*<
2829+ , toEncoding j
2082− toEncoding k >*<
2830+ , toEncoding k
2083− toEncoding l >*<
2831+ , toEncoding l
2084− toEncoding m >*<
2832+ , toEncoding m
2085− toN n >*<
2833+ , toN n
2086− toO o
2834+ , toO o
2835+ ]
2087 {-# INLINE liftToEncoding2 #-}
2836 {-# INLINE liftToEncoding2 #-}
2088
2837
2089instance (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
@@ -2097,3 +2846,27 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
2097 {-# INLINE toJSON #-}
2846 {-# INLINE toJSON #-}
2098 toEncoding = toEncoding2
2847 toEncoding = toEncoding2
2099 {-# INLINE toEncoding #-}
2848 {-# INLINE toEncoding #-}
2849+
2850+--------------------------------------------------------------------------------
2851+
2852+-- | Wrap a list of pairs as an object.
2853+class Monoid pairs => FromPairs enc pairs | enc -> pairs where
2854+ fromPairs :: pairs -> enc
2855+
2856+instance (a ~ Value) => FromPairs (Encoding' a) Series where
2857+ fromPairs = E.pairs
2858+
2859+instance FromPairs Value (DList Pair) where
2860+ fromPairs = object . toList
2861+
2862+-- | Like 'KeyValue' but the value is already converted to JSON
2863+-- ('Value' or 'Encoding'), and the result actually represents lists of pairs
2864+-- so it can be readily concatenated.
2865+class Monoid kv => KeyValuePair v kv where
2866+ pair :: String -> v -> kv
2867+
2868+instance (v ~ Value) => KeyValuePair v (DList Pair) where
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+152−67
@@ -1,12 +1,12 @@
1name: aeson
1name: aeson
2−version: 0.12.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,136 +33,234 @@ 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
58 default: False
50 default: False
59 manual: True
51 manual: True
60
52
53+flag fast
54+ description: compile without optimizations
55+ default: False
56+ manual: True
57+
58+flag bytestring-builder
59+ description: Depend on the bytestring-builder package for backwards compatibility.
60+ default: False
61+ manual: False
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+
61library
68library
62 default-language: Haskell2010
69 default-language: Haskell2010
70+ hs-source-dirs: . attoparsec-iso8601/
63
71
64 exposed-modules:
72 exposed-modules:
65 Data.Aeson
73 Data.Aeson
66− Data.Aeson.Encode
67 Data.Aeson.Encoding
74 Data.Aeson.Encoding
68− Data.Aeson.Encoding.Internal
69− Data.Aeson.Internal
70− Data.Aeson.Internal.Time
71 Data.Aeson.Parser
75 Data.Aeson.Parser
76+ Data.Aeson.Text
72 Data.Aeson.Types
77 Data.Aeson.Types
73 Data.Aeson.TH
78 Data.Aeson.TH
79+ Data.Aeson.QQ.Simple
74
80
75− other-modules:
81+ Data.Aeson.Encoding.Internal
76− Data.Aeson.Encode.Builder
82+ Data.Aeson.Internal
77− Data.Aeson.Functions
83+ Data.Aeson.Internal.Time
78 Data.Aeson.Parser.Internal
84 Data.Aeson.Parser.Internal
85+
86+ -- Deprecated modules
87+ exposed-modules:
88+ Data.Aeson.Encode
89+
90+ other-modules:
91+ Data.Aeson.Encoding.Builder
92+ Data.Aeson.Internal.Functions
93+ Data.Aeson.Parser.Unescape
79 Data.Aeson.Parser.Time
94 Data.Aeson.Parser.Time
80 Data.Aeson.Types.FromJSON
95 Data.Aeson.Types.FromJSON
81 Data.Aeson.Types.Generic
96 Data.Aeson.Types.Generic
82 Data.Aeson.Types.ToJSON
97 Data.Aeson.Types.ToJSON
83 Data.Aeson.Types.Class
98 Data.Aeson.Types.Class
84 Data.Aeson.Types.Internal
99 Data.Aeson.Types.Internal
100+ Data.Attoparsec.Time
101+ Data.Attoparsec.Time.Internal
85
102
103+ -- GHC bundled libs
86 build-depends:
104 build-depends:
87− attoparsec >= 0.13.0.1,
105+ base >= 4.7.0.0 && < 5,
88− base >= 4.5 && < 5,
106+ bytestring >= 0.10.4.0 && < 0.11,
89− base-compat >= 0.9.1 && <0.10,
107+ containers >= 0.5.5.1 && < 0.7,
90− bytestring >= 0.10.4.0,
108+ deepseq >= 1.3.0.0 && < 1.5,
91− containers,
109+ ghc-prim >= 0.2 && < 0.6,
92− deepseq,
110+ template-haskell >= 2.9.0.0 && < 2.16,
93− dlist >= 0.2,
111+ text >= 1.2.3.0 && < 1.3,
94− ghc-prim >= 0.2,
112+ time >= 1.4 && < 1.10
95− hashable >= 1.1.2.0,
113+
96− scientific >= 0.3.1 && < 0.4,
114+ if impl(ghc >= 8.0)
97− tagged >=0.8.3 && <0.9,
115+ build-depends: bytestring >= 0.10.8.1
98− template-haskell >= 2.7,
116+
99− text >= 1.1.1.0,
117+ -- Compat
100− time,
118+ build-depends:
101− time-locale-compat >=0.1.1 && <0.2,
119+ base-compat >= 0.9.1 && < 0.12,
102− unordered-containers >= 0.2.5.0,
120+ time-compat >= 1.9.2.2 && < 1.10
103− vector >= 0.8
121+
122+ if !impl(ghc >= 8.6)
123+ build-depends:
124+ contravariant >=1.4.1 && <1.6
104
125
105 if !impl(ghc >= 8.0)
126 if !impl(ghc >= 8.0)
106 -- `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
107 build-depends:
128 build-depends:
108− semigroups >= 0.16.1 && < 0.19,
129+ semigroups >= 0.18.5 && < 0.20,
109− transformers,
130+ transformers >= 0.3.0.0 && < 0.6,
131+ transformers-compat >= 0.6.2 && < 0.7,
110 fail == 4.9.*
132 fail == 4.9.*
111
133
112 if !impl(ghc >= 7.10)
134 if !impl(ghc >= 7.10)
113 -- `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
114− 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
115
158
116 if flag(developer)
159 if flag(developer)
117 ghc-options: -Werror
160 ghc-options: -Werror
118 ghc-prof-options: -auto-all
161 ghc-prof-options: -auto-all
119
162
120− ghc-options: -O2 -Wall
163+ if flag(fast)
164+ ghc-options: -O0
165+ else
166+ ghc-options: -O2
121
167
122 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
123
177
124test-suite tests
178test-suite tests
125 default-language: Haskell2010
179 default-language: Haskell2010
126− type: exitcode-stdio-1.0
180+ type: exitcode-stdio-1.0
127− hs-source-dirs: tests
181+ hs-source-dirs: tests ffi pure
128− main-is: Tests.hs
182+ main-is: Tests.hs
183+ c-sources: cbits/unescape_string.c
184+ ghc-options: -Wall -threaded -rtsopts
185+
129 other-modules:
186 other-modules:
187+ Data.Aeson.Parser.UnescapeFFI
188+ Data.Aeson.Parser.UnescapePure
130 DataFamilies.Properties
189 DataFamilies.Properties
131 DataFamilies.Instances
190 DataFamilies.Instances
132 DataFamilies.Encoders
191 DataFamilies.Encoders
133 DataFamilies.Types
192 DataFamilies.Types
134 Encoders
193 Encoders
194+ ErrorMessages
135 Functions
195 Functions
136 Instances
196 Instances
137 Options
197 Options
198+ PropUtils
138 Properties
199 Properties
200+ PropertyGeneric
201+ PropertyKeys
202+ PropertyRoundTrip
203+ PropertyRTFunctors
204+ PropertyTH
205+ SerializationFormatSpec
139 Types
206 Types
140 UnitTests
207 UnitTests
141 UnitTests.NullaryConstructors
208 UnitTests.NullaryConstructors
142
209
143− ghc-options: -Wall -threaded -rtsopts
144−
145 build-depends:
210 build-depends:
146− HUnit,
211+ QuickCheck >= 2.10.0.1 && < 2.14,
147− QuickCheck >= 2.7 && <2.8.3,
148 aeson,
212 aeson,
213+ integer-logarithms >= 1 && <1.1,
149 attoparsec,
214 attoparsec,
150 base,
215 base,
151− base-orphans >= 0.5.3 && <0.6,
216+ base-compat,
152− bytestring,
217+ base-orphans >= 0.5.3 && <0.9,
218+ base16-bytestring,
153 containers,
219 containers,
154− generic-deriving >= 1.10 && < 1.11,
220+ directory,
221+ dlist,
222+ Diff >= 0.4 && < 0.5,
223+ filepath,
224+ generic-deriving >= 1.10 && < 1.14,
155 ghc-prim >= 0.2,
225 ghc-prim >= 0.2,
156− hashable >= 1.1.2.0,
226+ hashable >= 1.2.4.0,
227+ scientific,
157 tagged,
228 tagged,
158 template-haskell,
229 template-haskell,
159− test-framework,
230+ tasty,
160− test-framework-hunit,
231+ tasty-golden,
161− test-framework-quickcheck2,
232+ tasty-hunit,
233+ tasty-quickcheck,
162 text,
234 text,
163 time,
235 time,
164− time-locale-compat,
236+ time-compat,
165 unordered-containers,
237 unordered-containers,
238+ uuid-types,
166 vector,
239 vector,
167− quickcheck-instances >=0.3.12
240+ quickcheck-instances >= 0.3.21 && <0.4
241+
242+ if flag(bytestring-builder)
243+ build-depends: bytestring >= 0.9 && < 0.10.4,
244+ bytestring-builder >= 0.10.4 && < 1
245+ else
246+ build-depends: bytestring >= 0.10.4
168
247
169 if !impl(ghc >= 8.0)
248 if !impl(ghc >= 8.0)
170− build-depends: semigroups >= 0.16.1 && < 0.19
249+ build-depends:
250+ semigroups >= 0.18.2 && < 0.20,
251+ transformers >= 0.2.2.0,
252+ transformers-compat >= 0.3
171
253
172 if !impl(ghc >= 7.10)
254 if !impl(ghc >= 7.10)
173− build-depends: nats >=1 && <1.2
255+ build-depends: nats >=1 && <1.2,
256+ void >=0.7.2 && <0.8
257+
258+ if impl(ghc >= 7.8)
259+ build-depends: hashable-time >= 0.2 && <0.3
260+
261+ if flag(fast)
262+ ghc-options: -fno-enable-rewrite-rules
174
263
175source-repository head
264source-repository head
176 type: git
265 type: git
177 location: git://github.com/bos/aeson.git
266 location: git://github.com/bos/aeson.git
178−
179−source-repository head
180− type: mercurial
181− 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−231
@@ -1,231 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable, DeriveGeneric, TemplateHaskell #-}
2−
3−module Main where
4−
5−--------------------------------------------------------------------------------
6−
7−import Criterion.Main hiding (defaultOptions)
8−
9−import Control.DeepSeq (NFData, rnf, deepseq)
10−
11−import Data.Typeable (Typeable)
12−import Data.Data (Data)
13−import GHC.Generics (Generic)
14−
15−import Data.Aeson.Types
16−import Data.Aeson.TH
17−import qualified Data.Aeson.Generic as G (fromJSON, toJSON)
18−
19−import Data.Aeson.Encode
20−
21−import Options
22−
23−--------------------------------------------------------------------------------
24−
25−data D a = Nullary
26− | Unary Int
27− | Product String Char a
28− | Record { testOne :: Double
29− , testTwo :: Bool
30− , testThree :: D a
31− }
32− deriving (Show, Eq, Data, Typeable)
33−
34−deriveJSON opts ''D
35−
36−instance NFData a => NFData (D a) where
37− rnf Nullary = ()
38− rnf (Unary n) = rnf n
39− rnf (Product s c x) = s `deepseq` c `deepseq` rnf x
40− rnf (Record d b y) = d `deepseq` b `deepseq` rnf y
41−
42−type T = D (D (D ()))
43−
44−d :: T
45−d = Record
46− { testOne = 1234.56789
47− , testTwo = True
48− , testThree = Product "Hello World!" 'a' $
49− Record
50− { testOne = 9876.54321
51− , testTwo = False
52− , testThree = Product "Yeehaa!!!" '\n' Nullary
53− }
54− }
55−
56−--------------------------------------------------------------------------------
57−
58−data D' a = Nullary'
59− | Unary' Int
60− | Product' String Char a
61− | Record' { testOne' :: Double
62− , testTwo' :: Bool
63− , testThree' :: D' a
64− }
65− deriving (Show, Eq, Generic, Data, Typeable)
66−
67−instance ToJSON a => ToJSON (D' a) where
68− toJSON = genericToJSON opts
69−
70−instance FromJSON a => FromJSON (D' a) where
71− parseJSON = genericParseJSON opts
72−
73−instance NFData a => NFData (D' a) where
74− rnf Nullary' = ()
75− rnf (Unary' n) = rnf n
76− rnf (Product' s c x) = s `deepseq` c `deepseq` rnf x
77− rnf (Record' d b y) = d `deepseq` b `deepseq` rnf y
78−
79−type T' = D' (D' (D' ()))
80−
81−d' :: T'
82−d' = Record'
83− { testOne' = 1234.56789
84− , testTwo' = True
85− , testThree' = Product' "Hello World!" 'a' $
86− Record'
87− { testOne' = 9876.54321
88− , testTwo' = False
89− , testThree' = Product' "Yeehaa!!!" '\n' Nullary'
90− }
91− }
92−
93−--------------------------------------------------------------------------------
94−
95−data BigRecord = BigRecord
96− { field01 :: !Int, field02 :: !Int, field03 :: !Int, field04 :: !Int, field05 :: !Int
97− , field06 :: !Int, field07 :: !Int, field08 :: !Int, field09 :: !Int, field10 :: !Int
98− , field11 :: !Int, field12 :: !Int, field13 :: !Int, field14 :: !Int, field15 :: !Int
99− , field16 :: !Int, field17 :: !Int, field18 :: !Int, field19 :: !Int, field20 :: !Int
100− , field21 :: !Int, field22 :: !Int, field23 :: !Int, field24 :: !Int, field25 :: !Int
101− } deriving (Show, Eq, Generic, Data, Typeable)
102−
103−instance NFData BigRecord
104−
105−bigRecord = BigRecord 1 2 3 4 5
106− 6 7 8 9 10
107− 11 12 13 14 15
108− 16 17 18 19 20
109− 21 22 23 24 25
110−
111−gBigRecordToJSON :: BigRecord -> Value
112−gBigRecordToJSON = genericToJSON opts
113−
114−gBigRecordFromJSON :: Value -> Result BigRecord
115−gBigRecordFromJSON = parse $ genericParseJSON opts
116−
117−thBigRecordToJSON :: BigRecord -> Value
118−thBigRecordToJSON = $(mkToJSON opts ''BigRecord)
119−
120−thBigRecordFromJSON :: Value -> Result BigRecord
121−thBigRecordFromJSON = parse $(mkParseJSON opts ''BigRecord)
122−
123−--------------------------------------------------------------------------------
124−
125−data BigProduct = BigProduct
126− !Int !Int !Int !Int !Int
127− !Int !Int !Int !Int !Int
128− !Int !Int !Int !Int !Int
129− !Int !Int !Int !Int !Int
130− !Int !Int !Int !Int !Int
131− deriving (Show, Eq, Generic, Data, Typeable)
132−
133−instance NFData BigProduct
134−
135−bigProduct = BigProduct 1 2 3 4 5
136− 6 7 8 9 10
137− 11 12 13 14 15
138− 16 17 18 19 20
139− 21 22 23 24 25
140−
141−gBigProductToJSON :: BigProduct -> Value
142−gBigProductToJSON = genericToJSON opts
143−
144−gBigProductFromJSON :: Value -> Result BigProduct
145−gBigProductFromJSON = parse $ genericParseJSON opts
146−
147−thBigProductToJSON :: BigProduct -> Value
148−thBigProductToJSON = $(mkToJSON opts ''BigProduct)
149−
150−thBigProductFromJSON :: Value -> Result BigProduct
151−thBigProductFromJSON = parse $(mkParseJSON opts ''BigProduct)
152−
153−--------------------------------------------------------------------------------
154−
155−data BigSum = F01 | F02 | F03 | F04 | F05
156− | F06 | F07 | F08 | F09 | F10
157− | F11 | F12 | F13 | F14 | F15
158− | F16 | F17 | F18 | F19 | F20
159− | F21 | F22 | F23 | F24 | F25
160− deriving (Show, Eq, Generic, Data, Typeable)
161−
162−instance NFData BigSum
163−
164−bigSum = F25
165−
166−gBigSumToJSON :: BigSum -> Value
167−gBigSumToJSON = genericToJSON opts
168−
169−gBigSumFromJSON :: Value -> Result BigSum
170−gBigSumFromJSON = parse $ genericParseJSON opts
171−
172−thBigSumToJSON :: BigSum -> Value
173−thBigSumToJSON = $(mkToJSON opts ''BigSum)
174−
175−thBigSumFromJSON :: Value -> Result BigSum
176−thBigSumFromJSON = parse $(mkParseJSON opts ''BigSum)
177−
178−--------------------------------------------------------------------------------
179−
180−type FJ a = Value -> Result a
181−
182−main :: IO ()
183−main = defaultMain
184− [ let v = toJSON d
185− in (d, d', v) `deepseq`
186− bgroup "D"
187− [ group "toJSON" (nf toJSON d)
188− (nf G.toJSON d)
189− (nf toJSON d')
190− , group "fromJSON" (nf ( fromJSON :: FJ T ) v)
191− (nf (G.fromJSON :: FJ T ) v)
192− (nf ( fromJSON :: FJ T') v)
193− ]
194− , let v = thBigRecordToJSON bigRecord
195− in bigRecord `deepseq` v `deepseq`
196− bgroup "BigRecord"
197− [ group "toJSON" (nf thBigRecordToJSON bigRecord)
198− (nf G.toJSON bigRecord)
199− (nf gBigRecordToJSON bigRecord)
200− , group "fromJSON" (nf (thBigRecordFromJSON :: FJ BigRecord) v)
201− (nf (G.fromJSON :: FJ BigRecord) v)
202− (nf (gBigRecordFromJSON :: FJ BigRecord) v)
203− ]
204− , let v = thBigProductToJSON bigProduct
205− in bigProduct `deepseq` v `deepseq`
206− bgroup "BigProduct"
207− [ group "toJSON" (nf thBigProductToJSON bigProduct)
208− (nf G.toJSON bigProduct)
209− (nf gBigProductToJSON bigProduct)
210− , group "fromJSON" (nf (thBigProductFromJSON :: FJ BigProduct) v)
211− (nf (G.fromJSON :: FJ BigProduct) v)
212− (nf (gBigProductFromJSON :: FJ BigProduct) v)
213− ]
214− , let v = thBigSumToJSON bigSum
215− in bigSum `deepseq` v `deepseq`
216− bgroup "BigSum"
217− [ group "toJSON" (nf thBigSumToJSON bigSum)
218− (nf G.toJSON bigSum)
219− (nf gBigSumToJSON bigSum)
220− , group "fromJSON" (nf (thBigSumFromJSON :: FJ BigSum) v)
221− (nf (G.fromJSON :: FJ BigSum) v)
222− (nf (gBigSumFromJSON :: FJ BigSum) v)
223− ]
224− ]
225−
226−group n th syb gen = bcompare
227− [ bgroup n [ bench "th" th
228− , bench "syb" syb
229− , bench "generic" gen
230− ]
231− ]
benchmarks/AesonEncode.hs+12−6
@@ -1,7 +1,13 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE OverloadedStrings #-}
2
4
3−import Control.Exception
5+module Main (main) where
4−import Control.Monad
6+
7+import Prelude.Compat
8+
9+import Control.DeepSeq
10+import Control.Monad (forM_)
5import Data.Aeson
11import Data.Aeson
6import Data.Attoparsec.ByteString (IResult(..), parseWith)
12import Data.Attoparsec.ByteString (IResult(..), parseWith)
7import Data.Char (isDigit)
13import Data.Char (isDigit)
@@ -9,7 +15,6 @@ import Data.Time.Clock
9import System.Environment (getArgs)
15import System.Environment (getArgs)
10import System.IO
16import System.IO
11import qualified Data.ByteString as B
17import qualified Data.ByteString as B
12−import Control.DeepSeq
13
18
14main :: IO ()
19main :: IO ()
15main = do
20main = do
@@ -17,8 +22,9 @@ main = do
17 let (cnt,args) = case args0 of
22 let (cnt,args) = case args0 of
18 (i:c:a) | all isDigit i && all isDigit c -> (c,a)
23 (i:c:a) | all isDigit i && all isDigit c -> (c,a)
19 (c:a) -> (c,a)
24 (c:a) -> (c,a)
25+ [] -> error "Unexpected empty list"
20 let count = read cnt :: Int
26 let count = read cnt :: Int
21− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
27+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
22 putStrLn $ arg ++ ":"
28 putStrLn $ arg ++ ":"
23 let refill = B.hGet h 16384
29 let refill = B.hGet h 16384
24 result0 <- parseWith refill json =<< refill
30 result0 <- parseWith refill json =<< refill
@@ -28,7 +34,7 @@ main = do
28 start <- getCurrentTime
34 start <- getCurrentTime
29 let loop !n r
35 let loop !n r
30 | n >= count = return ()
36 | n >= count = return ()
31− | otherwise = {-# SCC "loop" #-} do
37+ | otherwise = {-# SCC "loop" #-}
32 rnf (encode r) `seq` loop (n+1) r
38 rnf (encode r) `seq` loop (n+1) r
33 loop 0 r0
39 loop 0 r0
34 delta <- flip diffUTCTime start `fmap` getCurrentTime
40 delta <- flip diffUTCTime start `fmap` getCurrentTime
benchmarks/AesonFoldable.hsnew file+86
@@ -0,0 +1,86 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+
4+module Main (main) where
5+
6+import Criterion.Main
7+
8+import Prelude.Compat
9+
10+import Data.Foldable (toList)
11+import qualified Data.Aeson as A
12+import qualified Data.Sequence as S
13+import qualified Data.Vector as V
14+import qualified Data.Vector.Unboxed as U
15+
16+-------------------------------------------------------------------------------
17+-- List
18+-------------------------------------------------------------------------------
19+
20+newtype L f = L { getL :: f Int }
21+
22+instance Foldable f => A.ToJSON (L f) where
23+ toJSON = error "do not use this"
24+ toEncoding = A.toEncoding . toList . getL
25+
26+-------------------------------------------------------------------------------
27+-- Foldable
28+-------------------------------------------------------------------------------
29+
30+newtype F f = F { getF :: f Int }
31+
32+instance Foldable f => A.ToJSON (F f) where
33+ toJSON = error "do not use this"
34+ toEncoding = A.foldable . getF
35+
36+-------------------------------------------------------------------------------
37+-- Values
38+-------------------------------------------------------------------------------
39+
40+valueList :: [Int]
41+valueList = [1..1000]
42+
43+valueSeq :: S.Seq Int
44+valueSeq = S.fromList valueList
45+
46+valueVector :: V.Vector Int
47+valueVector = V.fromList valueList
48+
49+valueUVector :: U.Vector Int
50+valueUVector = U.fromList valueList
51+
52+-------------------------------------------------------------------------------
53+-- Main
54+-------------------------------------------------------------------------------
55+
56+benchEncode
57+ :: A.ToJSON a
58+ => String
59+ -> a
60+ -> Benchmark
61+benchEncode name val
62+ = bench ("A " ++ name) $ nf A.encode val
63+
64+main :: IO ()
65+main = defaultMain
66+ [ bgroup "encode"
67+ [ bgroup "List"
68+ [ benchEncode "-" valueList
69+ , benchEncode "L" $ L valueList
70+ , benchEncode "F" $ F valueList
71+ ]
72+ , bgroup "Seq"
73+ [ benchEncode "-" valueSeq
74+ , benchEncode "L" $ L valueSeq
75+ , benchEncode "F" $ F valueSeq
76+ ]
77+ , bgroup "Vector"
78+ [ benchEncode "-" valueVector
79+ , benchEncode "L" $ L valueVector
80+ , benchEncode "F" $ F valueVector
81+ ]
82+ , bgroup "Vector.Unboxed"
83+ [ benchEncode "-" valueUVector
84+ ]
85+ ]
86+ ]
benchmarks/AesonMap.hs+28−17
@@ -1,16 +1,24 @@
1−{-# LANGUAGE OverloadedStrings, RankNTypes #-}
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE RankNTypes #-}
4+
5+{-# OPTIONS_GHC -fno-warn-orphans #-}
6+
7+module Main (main) where
8+
9+import Prelude.Compat
2
10
3import Control.DeepSeq
11import Control.DeepSeq
4import Criterion.Main
12import Criterion.Main
5−import Data.Aeson
6−import Data.Aeson.Types (fromJSONKeyCoerce)
7import Data.Hashable
13import Data.Hashable
8import Data.Proxy (Proxy (..))
14import Data.Proxy (Proxy (..))
9import Data.Tagged (Tagged (..))
15import Data.Tagged (Tagged (..))
16+import Data.Aeson
17+import Data.Aeson.Types (fromJSONKeyCoerce)
10import qualified Data.ByteString.Lazy as LBS
18import qualified Data.ByteString.Lazy as LBS
11−import qualified Data.Text as T
12−import qualified Data.Map as M
13import qualified Data.HashMap.Strict as HM
19import qualified Data.HashMap.Strict as HM
20+import qualified Data.Map as M
21+import qualified Data.Text as T
14
22
15value :: Int -> HM.HashMap T.Text T.Text
23value :: Int -> HM.HashMap T.Text T.Text
16value n = HM.fromList $ map f [1..n]
24value n = HM.fromList $ map f [1..n]
@@ -35,6 +43,7 @@ instance NFData T1 where
35 rnf (T1 t) = rnf t
43 rnf (T1 t) = rnf t
36instance Hashable T1 where
44instance Hashable T1 where
37 hashWithSalt salt (T1 t) = hashWithSalt salt t
45 hashWithSalt salt (T1 t) = hashWithSalt salt t
46+
38instance FromJSON T1 where
47instance FromJSON T1 where
39 parseJSON = withText "T1" $ pure . T1
48 parseJSON = withText "T1" $ pure . T1
40instance FromJSONKey T1 where
49instance FromJSONKey T1 where
@@ -51,6 +60,7 @@ instance NFData T2 where
51 rnf (T2 t) = rnf t
60 rnf (T2 t) = rnf t
52instance Hashable T2 where
61instance Hashable T2 where
53 hashWithSalt salt (T2 t) = hashWithSalt salt t
62 hashWithSalt salt (T2 t) = hashWithSalt salt t
63+
54instance FromJSON T2 where
64instance FromJSON T2 where
55 parseJSON = withText "T2" $ pure . T2
65 parseJSON = withText "T2" $ pure . T2
56instance FromJSONKey T2 where
66instance FromJSONKey T2 where
@@ -67,6 +77,7 @@ instance NFData T3 where
67 rnf (T3 t) = rnf t
77 rnf (T3 t) = rnf t
68instance Hashable T3 where
78instance Hashable T3 where
69 hashWithSalt salt (T3 t) = hashWithSalt salt t
79 hashWithSalt salt (T3 t) = hashWithSalt salt t
80+
70instance FromJSON T3 where
81instance FromJSON T3 where
71 parseJSON = withText "T3" $ pure . T3
82 parseJSON = withText "T3" $ pure . T3
72instance FromJSONKey T3 where
83instance FromJSONKey T3 where
@@ -83,28 +94,28 @@ value1000 = value 1000
83value10000 = value 10000
94value10000 = value 10000
84
95
85encodedValue10 :: LBS.ByteString
96encodedValue10 :: LBS.ByteString
86−encodedValue10 = encode $ value10
97+encodedValue10 = encode value10
87
98
88encodedValue100 :: LBS.ByteString
99encodedValue100 :: LBS.ByteString
89−encodedValue100 = encode $ value100
100+encodedValue100 = encode value100
90
101
91encodedValue1000 :: LBS.ByteString
102encodedValue1000 :: LBS.ByteString
92−encodedValue1000 = encode $ value1000
103+encodedValue1000 = encode value1000
93
104
94encodedValue10000 :: LBS.ByteString
105encodedValue10000 :: LBS.ByteString
95−encodedValue10000 = encode $ value10000
106+encodedValue10000 = encode value10000
96
107
97-------------------------------------------------------------------------------
108-------------------------------------------------------------------------------
98-- Helpers
109-- Helpers
99-------------------------------------------------------------------------------
110-------------------------------------------------------------------------------
100
111
101decodeHM
112decodeHM
102− :: (FromJSONKey k, Eq k, Hashable k)
113+ :: (FromJSON (HM.HashMap k T.Text), Eq k, Hashable k)
103 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
114 => Proxy k -> LBS.ByteString -> Maybe (HM.HashMap k T.Text)
104decodeHM _ = decode
115decodeHM _ = decode
105
116
106decodeMap
117decodeMap
107− :: (FromJSONKey k, Ord k)
118+ :: (FromJSON (M.Map k T.Text), Ord k)
108 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
119 => Proxy k -> LBS.ByteString -> Maybe (M.Map k T.Text)
109decodeMap _ = decode
120decodeMap _ = decode
110
121
@@ -147,10 +158,10 @@ benchDecodeMap
147 -> LBS.ByteString
158 -> LBS.ByteString
148 -> Benchmark
159 -> Benchmark
149benchDecodeMap name val = bgroup name
160benchDecodeMap name val = bgroup name
150− [ bench "Text" $ nf (decodeMap proxyText) val
161+ [ bench "Text" $ nf (decodeMap proxyText) val
151− , bench "Identity" $ nf (decodeMap proxyT1) val
162+ , bench "Identity" $ nf (decodeMap proxyT1) val
152− , bench "Coerce" $ nf (decodeMap proxyT2) val
163+ , bench "Coerce" $ nf (decodeMap proxyT2) val
153− , bench "Parser" $ nf (decodeMap proxyT3) val
164+ , bench "Parser" $ nf (decodeMap proxyT3) val
154 ]
165 ]
155
166
156benchEncodeHM
167benchEncodeHM
@@ -158,7 +169,7 @@ benchEncodeHM
158 -> HM.HashMap T.Text T.Text
169 -> HM.HashMap T.Text T.Text
159 -> Benchmark
170 -> Benchmark
160benchEncodeHM name val = bgroup name
171benchEncodeHM name val = bgroup name
161− [ bench "Text" $ nf encode val
172+ [ bench "Text" $ nf encode val
162 ]
173 ]
163
174
164benchEncodeMap
175benchEncodeMap
@@ -166,7 +177,7 @@ benchEncodeMap
166 -> HM.HashMap T.Text T.Text
177 -> HM.HashMap T.Text T.Text
167 -> Benchmark
178 -> Benchmark
168benchEncodeMap name val = bgroup name
179benchEncodeMap name val = bgroup name
169− [ bench "Text" $ nf encode val'
180+ [ bench "Text" $ nf encode val'
170 ]
181 ]
171 where
182 where
172 val' :: M.Map T.Text T.Text
183 val' :: M.Map T.Text T.Text
benchmarks/AesonParse.hs+9−4
@@ -1,8 +1,13 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+module Main (main) where
6+
7+import Prelude.Compat
2
8
3−import Control.Exception
4−import Control.Monad
5import Data.Aeson
9import Data.Aeson
10+import Control.Monad
6import Data.Attoparsec.ByteString (IResult(..), parseWith)
11import Data.Attoparsec.ByteString (IResult(..), parseWith)
7import Data.Time.Clock
12import Data.Time.Clock
8import System.Environment (getArgs)
13import System.Environment (getArgs)
@@ -14,7 +19,7 @@ main = do
14 (bs:cnt:args) <- getArgs
19 (bs:cnt:args) <- getArgs
15 let count = read cnt :: Int
20 let count = read cnt :: Int
16 blkSize = read bs
21 blkSize = read bs
17− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
22+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
18 putStrLn $ arg ++ ":"
23 putStrLn $ arg ++ ":"
19 start <- getCurrentTime
24 start <- getCurrentTime
20 let loop !good !bad
25 let loop !good !bad
benchmarks/AesonTuples.hs+5−3
@@ -1,9 +1,11 @@
1−module Main where
1+{-# LANGUAGE NoImplicitPrelude #-}
2
2
3−--------------------------------------------------------------------------------
3+module Main (main) where
4+
5+import Prelude.Compat
4
6
5−import Criterion.Main
6import Control.DeepSeq (deepseq)
7import Control.DeepSeq (deepseq)
8+import Criterion.Main
7import Data.Aeson
9import Data.Aeson
8
10
9--------------------------------------------------------------------------------
11--------------------------------------------------------------------------------
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+12−3
@@ -1,17 +1,24 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
2
4
3module Main (main) where
5module Main (main) where
4
6
7+import Prelude.Compat
8+
5import Compare.BufferBuilder ()
9import Compare.BufferBuilder ()
6−import Compare.JsonBuilder ()
7import Criterion.Main
10import Criterion.Main
8import Data.BufferBuilder.Json
11import Data.BufferBuilder.Json
9−import Data.Json.Builder
10import Twitter
12import Twitter
11import Twitter.Manual ()
13import Twitter.Manual ()
12import Typed.Common
14import Typed.Common
13−import qualified Compare.JsonBench as JsonBench
14import qualified Data.Aeson as Aeson
15import qualified Data.Aeson as Aeson
16+import qualified Compare.JsonBench as JsonBench
17+
18+#ifdef MIN_VERSION_json_builder
19+import Data.Json.Builder
20+import Compare.JsonBuilder ()
21+#endif
15
22
16main :: IO ()
23main :: IO ()
17main =
24main =
@@ -20,7 +27,9 @@ main =
20 bgroup "twitter" [
27 bgroup "twitter" [
21 bench "aeson" $ nf Aeson.encode twtr
28 bench "aeson" $ nf Aeson.encode twtr
22 , bench "buffer-builder" $ nf encodeJson twtr
29 , bench "buffer-builder" $ nf encodeJson twtr
30+#ifdef MIN_VERSION_json_builder
23 , bench "json-builder" $ nf toJsonLBS twtr
31 , bench "json-builder" $ nf toJsonLBS twtr
32+#endif
24 ]
33 ]
25 , JsonBench.benchmarks
34 , JsonBench.benchmarks
26 ]
35 ]
benchmarks/Compare/BufferBuilder.hs+9−4
@@ -1,12 +1,17 @@
1−{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
4+
5+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
3
7
4module Compare.BufferBuilder () where
8module Compare.BufferBuilder () where
5
9
10+import Prelude.Compat hiding ((<>))
11+
6import Data.BufferBuilder.Json
12import Data.BufferBuilder.Json
7−import Data.Monoid ((<>))
8import Data.Int (Int64)
13import Data.Int (Int64)
9−import Prelude hiding (id)
14+import Data.Monoid ((<>))
10import Twitter
15import Twitter
11import qualified Data.BufferBuilder.Utf8 as UB
16import qualified Data.BufferBuilder.Utf8 as UB
12
17
@@ -37,7 +42,7 @@ instance ToJson Story where
37 <> "metadata" .= metadata
42 <> "metadata" .= metadata
38 <> "to_user_id" .= to_user_id
43 <> "to_user_id" .= to_user_id
39 <> "text" .= text
44 <> "text" .= text
40− <> "id" .= id
45+ <> "id" .= id_
41 <> "from_user_id" .= from_user_id
46 <> "from_user_id" .= from_user_id
42 <> "geo" .= geo
47 <> "geo" .= geo
43 <> "iso_language_code" .= iso_language_code
48 <> "iso_language_code" .= iso_language_code
benchmarks/Compare/JsonBench.hs+21−7
@@ -1,22 +1,32 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE MagicHash #-}
4+{-# LANGUAGE NoImplicitPrelude #-}
5+{-# LANGUAGE OverloadedStrings #-}
6+{-# LANGUAGE RecordWildCards #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+
1-- Adapted from a buffer-builder benchmark:
9-- Adapted from a buffer-builder benchmark:
2--
10--
3-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
11-- https://github.com/chadaustin/buffer-builder/blob/master/test.json
4
12
5−{-# LANGUAGE BangPatterns, OverloadedStrings, RecordWildCards #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE MagicHash, ScopedTypeVariables #-}
8−
9module Compare.JsonBench (benchmarks) where
13module Compare.JsonBench (benchmarks) where
10
14
15+import Prelude.Compat hiding ((<>))
16+
11import Control.DeepSeq (NFData(..))
17import Control.DeepSeq (NFData(..))
12import Criterion
18import Criterion
13import Data.Aeson ((.:))
19import Data.Aeson ((.:))
14import Data.Monoid ((<>))
20import Data.Monoid ((<>))
15import Data.Text (Text)
21import Data.Text (Text)
16import Typed.Common (load)
22import Typed.Common (load)
23+import qualified Control.Monad.Fail as Fail
17import qualified Data.Aeson as Aeson
24import qualified Data.Aeson as Aeson
18import qualified Data.BufferBuilder.Json as Json
25import qualified Data.BufferBuilder.Json as Json
26+
27+#ifdef MIN_VERSION_json_builder
19import qualified Data.Json.Builder as JB
28import qualified Data.Json.Builder as JB
29+#endif
20
30
21data EyeColor = Green | Blue | Brown
31data EyeColor = Green | Blue | Brown
22 deriving (Eq, Show)
32 deriving (Eq, Show)
@@ -59,10 +69,10 @@ instance NFData Gender where rnf !_ = ()
59instance NFData Fruit where rnf !_ = ()
69instance NFData Fruit where rnf !_ = ()
60
70
61instance NFData Friend where
71instance NFData Friend where
62− rnf Friend {..} = (rnf fId) `seq` (rnf fName) `seq` ()
72+ rnf Friend {..} = rnf fId `seq` rnf fName
63
73
64instance NFData User where
74instance NFData User where
65− 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
66
76
67eyeColorTable :: [(Text, EyeColor)]
77eyeColorTable :: [(Text, EyeColor)]
68eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
78eyeColorTable = [("brown", Brown), ("green", Green), ("blue", Blue)]
@@ -73,7 +83,7 @@ genderTable = [("male", Male), ("female", Female)]
73fruitTable :: [(Text, Fruit)]
83fruitTable :: [(Text, Fruit)]
74fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
84fruitTable = [("apple", Apple), ("strawberry", Strawberry), ("banana", Banana)]
75
85
76−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
77enumFromJson enumName table extract v = do
87enumFromJson enumName table extract v = do
78 s <- extract v
88 s <- extract v
79 case lookup s table of
89 case lookup s table of
@@ -263,6 +273,7 @@ instance Json.ToJson User where
263 <> "greeting"# Json..=# uGreeting
273 <> "greeting"# Json..=# uGreeting
264 <> "favoriteFruit"# Json..=# uFavouriteFruit
274 <> "favoriteFruit"# Json..=# uFavouriteFruit
265
275
276+#ifdef MIN_VERSION_json_builder
266---- json-builder instances ----
277---- json-builder instances ----
267
278
268instance JB.Value EyeColor where
279instance JB.Value EyeColor where
@@ -314,6 +325,7 @@ instance JB.Value User where
314 <> t "friends" `JB.row` uFriends
325 <> t "friends" `JB.row` uFriends
315 <> t "greeting" `JB.row` uGreeting
326 <> t "greeting" `JB.row` uGreeting
316 <> t "favoriteFruit" `JB.row` uFavouriteFruit
327 <> t "favoriteFruit" `JB.row` uFavouriteFruit
328+#endif
317
329
318benchmarks :: Benchmark
330benchmarks :: Benchmark
319benchmarks = env (load "json-data/buffer-builder.json") $
331benchmarks = env (load "json-data/buffer-builder.json") $
@@ -321,5 +333,7 @@ benchmarks = env (load "json-data/buffer-builder.json") $
321 bgroup "json-bench" [
333 bgroup "json-bench" [
322 bench "aeson" $ nf Aeson.encode parsedUserList
334 bench "aeson" $ nf Aeson.encode parsedUserList
323 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
335 , bench "buffer-builder" $ nf Json.encodeJson parsedUserList
336+#ifdef MIN_VERSION_json_builder
324 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
337 , bench "json-builder" $ nf JB.toJsonLBS parsedUserList
338+#endif
325 ]
339 ]
benchmarks/Compare/JsonBuilder.hs+6−2
@@ -1,11 +1,15 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
1{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE RecordWildCards #-}
3+
4+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
3
6
4module Compare.JsonBuilder () where
7module Compare.JsonBuilder () where
5
8
9+import Prelude.Compat hiding ((<>))
10+
6import Data.Json.Builder
11import Data.Json.Builder
7import Data.Monoid ((<>))
12import Data.Monoid ((<>))
8−import Prelude hiding (id)
9import Twitter
13import Twitter
10
14
11instance JsObject Metadata where
15instance JsObject Metadata where
@@ -36,7 +40,7 @@ instance JsObject Story where
36 row "metadata" metadata <>
40 row "metadata" metadata <>
37 row "to_user_id" to_user_id <>
41 row "to_user_id" to_user_id <>
38 row "text" text <>
42 row "text" text <>
39− row "id" id <>
43+ row "id" id_ <>
40 row "from_user_id" from_user_id <>
44 row "from_user_id" from_user_id <>
41 row "geo" geo <>
45 row "geo" geo <>
42 row "iso_language_code" iso_language_code <>
46 row "iso_language_code" iso_language_code <>
benchmarks/CompareWithJSON.hs+39−22
@@ -1,16 +1,25 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1{-# OPTIONS_GHC -fno-warn-orphans #-}
3{-# OPTIONS_GHC -fno-warn-orphans #-}
2
4
5+module Main (main) where
6+
7+import Prelude.Compat
8+
3import Blaze.ByteString.Builder (toLazyByteString)
9import Blaze.ByteString.Builder (toLazyByteString)
4import Blaze.ByteString.Builder.Char.Utf8 (fromString)
10import Blaze.ByteString.Builder.Char.Utf8 (fromString)
5import Control.DeepSeq (NFData(rnf))
11import Control.DeepSeq (NFData(rnf))
6import Criterion.Main
12import Criterion.Main
7−import qualified Data.Aeson.Encode as A
13+import Data.Maybe (fromMaybe)
8import qualified Data.Aeson as A
14import qualified Data.Aeson as A
15+import qualified Data.Aeson.Text as A
16+import qualified Data.Aeson.Parser.Internal as I
17+import qualified Data.ByteString as BS
9import qualified Data.ByteString.Lazy as BL
18import qualified Data.ByteString.Lazy as BL
10−import qualified Text.JSON as J
11import qualified Data.Text.Lazy as TL
19import qualified Data.Text.Lazy as TL
12import qualified Data.Text.Lazy.Builder as TLB
20import qualified Data.Text.Lazy.Builder as TLB
13import qualified Data.Text.Lazy.Encoding as TLE
21import qualified Data.Text.Lazy.Encoding as TLE
22+import qualified Text.JSON as J
14
23
15instance (NFData v) => NFData (J.JSObject v) where
24instance (NFData v) => NFData (J.JSObject v) where
16 rnf o = rnf (J.fromJSObject o)
25 rnf o = rnf (J.fromJSObject o)
@@ -18,7 +27,7 @@ instance (NFData v) => NFData (J.JSObject v) where
18instance NFData J.JSValue where
27instance NFData J.JSValue where
19 rnf J.JSNull = ()
28 rnf J.JSNull = ()
20 rnf (J.JSBool b) = rnf b
29 rnf (J.JSBool b) = rnf b
21− rnf (J.JSRational a b) = rnf a `seq` rnf b `seq` ()
30+ rnf (J.JSRational a b) = rnf a `seq` rnf b
22 rnf (J.JSString s) = rnf (J.fromJSString s)
31 rnf (J.JSString s) = rnf (J.fromJSString s)
23 rnf (J.JSArray lst) = rnf lst
32 rnf (J.JSArray lst) = rnf lst
24 rnf (J.JSObject o) = rnf o
33 rnf (J.JSObject o) = rnf o
@@ -29,15 +38,18 @@ decodeJ s =
29 J.Ok v -> v
38 J.Ok v -> v
30 J.Error _ -> error "fail to parse via JSON"
39 J.Error _ -> error "fail to parse via JSON"
31
40
32−decodeA :: BL.ByteString -> A.Value
41+decode :: BL.ByteString -> A.Value
33−decodeA s = case A.decode s of
42+decode s = fromMaybe (error "fail to parse via Aeson") $ A.decode s
34− Just v -> v
43+
35− Nothing -> error "fail to parse via Aeson"
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
48+decodeS s = fromMaybe (error "fail to parse via Aeson") $ A.decodeStrict' s
36
49
37−decodeA' :: BL.ByteString -> A.Value
50+decodeIP :: BL.ByteString -> A.Value
38−decodeA' s = case A.decode' s of
51+decodeIP s = fromMaybe (error "fail to parse via Parser.decodeWith") $
39− Just v -> v
52+ I.decodeWith I.jsonEOF A.fromJSON s
40− Nothing -> error "fail to parse via Aeson"
41
53
42encodeJ :: J.JSValue -> BL.ByteString
54encodeJ :: J.JSValue -> BL.ByteString
43encodeJ = toLazyByteString . fromString . J.encode
55encodeJ = toLazyByteString . fromString . J.encode
@@ -53,32 +65,37 @@ main = do
53 let enFile = "json-data/twitter100.json"
65 let enFile = "json-data/twitter100.json"
54 jpFile = "json-data/jp100.json"
66 jpFile = "json-data/jp100.json"
55 enA <- BL.readFile enFile
67 enA <- BL.readFile enFile
68+ enS <- BS.readFile enFile
56 enJ <- readFile enFile
69 enJ <- readFile enFile
57 jpA <- BL.readFile jpFile
70 jpA <- BL.readFile jpFile
71+ jpS <- BS.readFile jpFile
58 jpJ <- readFile jpFile
72 jpJ <- readFile jpFile
59 defaultMain [
73 defaultMain [
60 bgroup "decode" [
74 bgroup "decode" [
61 bgroup "en" [
75 bgroup "en" [
62− bench "aeson/lazy" $ nf decodeA enA
76+ bench "aeson/lazy" $ nf decode enA
63− , bench "aeson/strict" $ nf decodeA' enA
77+ , bench "aeson/strict" $ nf decode' enA
64− , bench "json" $ nf decodeJ enJ
78+ , bench "aeson/stricter" $ nf decodeS enS
79+ , bench "aeson/parser" $ nf decodeIP enA
80+ , bench "json" $ nf decodeJ enJ
65 ]
81 ]
66 , bgroup "jp" [
82 , bgroup "jp" [
67− bench "aeson" $ nf decodeA jpA
83+ bench "aeson" $ nf decode jpA
68− , bench "json" $ nf decodeJ jpJ
84+ , bench "aeson/stricter" $ nf decodeS jpS
85+ , bench "json" $ nf decodeJ jpJ
69 ]
86 ]
70 ]
87 ]
71 , bgroup "encode" [
88 , bgroup "encode" [
72 bgroup "en" [
89 bgroup "en" [
73− bench "aeson-to-bytestring" $ nf A.encode (decodeA enA)
90+ bench "aeson-to-bytestring" $ nf A.encode (decode enA)
74− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA enA)
91+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode enA)
75− , bench "aeson-to-text" $ nf encodeToText (decodeA enA)
92+ , bench "aeson-to-text" $ nf encodeToText (decode enA)
76 , bench "json" $ nf encodeJ (decodeJ enJ)
93 , bench "json" $ nf encodeJ (decodeJ enJ)
77 ]
94 ]
78 , bgroup "jp" [
95 , bgroup "jp" [
79− bench "aeson-to-bytestring" $ nf A.encode (decodeA jpA)
96+ bench "aeson-to-bytestring" $ nf A.encode (decode jpA)
80− , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decodeA jpA)
97+ , bench "aeson-via-text-to-bytestring" $ nf encodeViaText (decode jpA)
81− , bench "aeson-to-text" $ nf encodeToText (decodeA jpA)
98+ , bench "aeson-to-text" $ nf encodeToText (decode jpA)
82 , bench "json" $ nf encodeJ (decodeJ jpJ)
99 , bench "json" $ nf encodeJ (decodeJ jpJ)
83 ]
100 ]
84 ]
101 ]
benchmarks/Dates.hs+14−9
@@ -1,4 +1,9 @@
1−import Control.Applicative ((<$>))
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+module Main (main) where
4+
5+import Prelude.Compat
6+
2import Criterion.Main
7import Criterion.Main
3import Data.Aeson (decode, encode)
8import Data.Aeson (decode, encode)
4import Data.Time.Clock (UTCTime)
9import Data.Time.Clock (UTCTime)
@@ -18,22 +23,22 @@ main = do
18 defaultMain [
23 defaultMain [
19 bgroup "decode" [
24 bgroup "decode" [
20 bgroup "UTCTime" [
25 bgroup "UTCTime" [
21− env file1 $ \ ~bs -> bench "whole" $ nf utcTime bs
26+ env file1 $ \bs -> bench "whole" $ nf utcTime bs
22− , env file2 $ \ ~bs -> bench "fractional" $ nf utcTime bs
27+ , env file2 $ \bs -> bench "fractional" $ nf utcTime bs
23 ]
28 ]
24 , bgroup "ZonedTime" [
29 , bgroup "ZonedTime" [
25− env file1 $ \ ~bs -> bench "whole" $ nf zTime bs
30+ env file1 $ \bs -> bench "whole" $ nf zTime bs
26− , env file2 $ \ ~bs -> bench "fractional" $ nf zTime bs
31+ , env file2 $ \bs -> bench "fractional" $ nf zTime bs
27 ]
32 ]
28 ]
33 ]
29 , bgroup "encode" [
34 , bgroup "encode" [
30 bgroup "UTCTime" [
35 bgroup "UTCTime" [
31− env (utcTime <$> file1) $ \ ~ts -> bench "whole" $ nf encode ts
36+ env (utcTime <$> file1) $ \ts -> bench "whole" $ nf encode ts
32− , env (utcTime <$> file2) $ \ ~ts -> bench "fractional" $ nf encode ts
37+ , env (utcTime <$> file2) $ \ts -> bench "fractional" $ nf encode ts
33 ]
38 ]
34 , bgroup "ZonedTime" [
39 , bgroup "ZonedTime" [
35− env (zTime <$> file1) $ \ ~ts -> bench "whole" $ nf encode ts
40+ env (zTime <$> file1) $ \ts -> bench "whole" $ nf encode ts
36− , env (zTime <$> file2) $ \ ~ts -> bench "fractional" $ nf encode ts
41+ , env (zTime <$> file2) $ \ts -> bench "fractional" $ nf encode ts
37 ]
42 ]
38 ]
43 ]
39 ]
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+10−3
@@ -1,16 +1,23 @@
1−{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+
2{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
3
6
7+module Main (main) where
8+
9+import Prelude.Compat
10+
4import Control.DeepSeq
11import Control.DeepSeq
5import Control.Monad
12import Control.Monad
6−import Text.JSON
7import Data.Time.Clock
13import Data.Time.Clock
8import System.Environment (getArgs)
14import System.Environment (getArgs)
15+import Text.JSON
9
16
10instance NFData JSValue where
17instance NFData JSValue where
11 rnf JSNull = ()
18 rnf JSNull = ()
12 rnf (JSBool b) = rnf b
19 rnf (JSBool b) = rnf b
13− rnf (JSRational b r) = rnf b `seq` rnf r `seq` ()
20+ rnf (JSRational b r) = rnf b `seq` rnf r
14 rnf (JSString s) = rnf (fromJSString s)
21 rnf (JSString s) = rnf (fromJSString s)
15 rnf (JSArray vs) = rnf vs
22 rnf (JSArray vs) = rnf vs
16 rnf (JSObject kvs) = rnf (fromJSObject kvs)
23 rnf (JSObject kvs) = rnf (fromJSObject kvs)
benchmarks/Micro.hs+10−6
@@ -1,18 +1,22 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Main (main) where
3module Main (main) where
2
4
5+import Prelude.Compat
6+
3import Criterion.Main
7import Criterion.Main
4−import qualified Data.Aeson.Encode.Builder as AB
5−import qualified Data.ByteString.Builder as B
6−import qualified Data.Text as T
7
8
9+-- Encoding is a newtype wrapper around Builder
10+import Data.Aeson.Encoding (text, string, encodingToLazyByteString)
11+import qualified Data.Text as T
8
12
9main :: IO ()
13main :: IO ()
10main = do
14main = do
11 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
15 let txt = "append (append b (primBounded w1 x1)) (primBounded w2 x2)"
12 defaultMain [
16 defaultMain [
13 bgroup "string" [
17 bgroup "string" [
14− bench "text" $ nf (B.toLazyByteString . AB.text) (T.pack txt)
18+ bench "text" $ nf (encodingToLazyByteString . text) (T.pack txt)
15− , bench "string direct" $ nf (B.toLazyByteString . AB.string) txt
19+ , bench "string direct" $ nf (encodingToLazyByteString . string) txt
16− , bench "string via text" $ nf (B.toLazyByteString . AB.text . T.pack) txt
20+ , bench "string via text" $ nf (encodingToLazyByteString . text . T.pack) txt
17 ]
21 ]
18 ]
22 ]
benchmarks/Options.hs+1−1
@@ -1,4 +1,4 @@
1−module Options where
1+module Options (opts) where
2
2
3import Data.Aeson.Types
3import Data.Aeson.Types
4
4
benchmarks/ReadFile.hs+8−2
@@ -1,4 +1,10 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+
5+module Main (main) where
6+
7+import Prelude.Compat
2
8
3import Control.DeepSeq
9import Control.DeepSeq
4import Control.Exception
10import Control.Exception
@@ -14,7 +20,7 @@ import qualified Data.ByteString as B
14main = do
20main = do
15 (cnt:args) <- getArgs
21 (cnt:args) <- getArgs
16 let count = read cnt :: Int
22 let count = read cnt :: Int
17− forM_ args $ \arg -> bracket (openFile arg ReadMode) hClose $ \h -> do
23+ forM_ args $ \arg -> withFile arg ReadMode $ \h -> do
18 putStrLn $ arg ++ ":"
24 putStrLn $ arg ++ ":"
19 start <- getCurrentTime
25 start <- getCurrentTime
20 let loop !n
26 let loop !n
benchmarks/Typed.hs+7
@@ -1,5 +1,9 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
1module Main (main) where
3module Main (main) where
2
4
5+import Prelude.Compat
6+
3import Criterion.Main
7import Criterion.Main
4import qualified Typed.Generic as Generic
8import qualified Typed.Generic as Generic
5import qualified Typed.Manual as Manual
9import qualified Typed.Manual as Manual
@@ -10,4 +14,7 @@ main = defaultMain [
10 Generic.benchmarks
14 Generic.benchmarks
11 , Manual.benchmarks
15 , Manual.benchmarks
12 , TH.benchmarks
16 , TH.benchmarks
17+ , Generic.decodeBenchmarks
18+ , Manual.decodeBenchmarks
19+ , TH.decodeBenchmarks
13 ]
20 ]
benchmarks/Typed/Common.hs+9−3
@@ -1,10 +1,16 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
4+
1module Typed.Common (load) where
5module Typed.Common (load) where
2
6
3−import Data.Aeson hiding (Result)
7+import Prelude.Compat
8+
4import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
5−import Control.Applicative
6−import System.IO
7import System.Exit
10import System.Exit
11+import System.IO
12+
13+import Data.Aeson hiding (Result)
8
14
9load :: FromJSON a => FilePath -> IO a
15load :: FromJSON a => FilePath -> IO a
10load fileName = do
16load fileName = do
benchmarks/Typed/Generic.hs+23−8
@@ -1,11 +1,13 @@
1−module Typed.Generic (benchmarks) where
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+module Typed.Generic (benchmarks, decodeBenchmarks) where
4+
5+import Prelude.Compat
2
6
3−import Control.Applicative
4−import Criterion
5import Data.Aeson hiding (Result)
7import Data.Aeson hiding (Result)
6−import Data.ByteString.Builder as B
8+import Criterion
7import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
8−import Twitter.TH
10+import Twitter.Generic
9import Typed.Common
11import Typed.Common
10
12
11encodeDirect :: Result -> L.ByteString
13encodeDirect :: Result -> L.ByteString
@@ -17,13 +19,26 @@ encodeViaValue = encode . toJSON
17benchmarks :: Benchmark
19benchmarks :: Benchmark
18benchmarks =
20benchmarks =
19 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) ->
20− bgroup "generic" [
22+ bgroup "encodeGeneric" [
21 bgroup "direct" [
23 bgroup "direct" [
22 bench "twitter100" $ nf encodeDirect twitter100
24 bench "twitter100" $ nf encodeDirect twitter100
23− , bench "jp100" $ nf encodeDirect jp100
25+ , bench "jp100" $ nf encodeDirect jp100
24 ]
26 ]
25 , bgroup "viaValue" [
27 , bgroup "viaValue" [
26 bench "twitter100" $ nf encodeViaValue twitter100
28 bench "twitter100" $ nf encodeViaValue twitter100
27− , bench "jp100" $ nf encodeViaValue jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
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
42+ , bench "jp100" $ nf decodeDirect jp100
28 ]
43 ]
29 ]
44 ]
benchmarks/Typed/Manual.hs+22−7
@@ -1,9 +1,11 @@
1−module Typed.Manual (benchmarks) where
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+module Typed.Manual (benchmarks, decodeBenchmarks) where
4+
5+import Prelude.Compat
2
6
3−import Control.Applicative
4−import Criterion
5import Data.Aeson hiding (Result)
7import Data.Aeson hiding (Result)
6−import Data.ByteString.Builder as B
8+import Criterion
7import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
8import Twitter.Manual
10import Twitter.Manual
9import Typed.Common
11import Typed.Common
@@ -17,13 +19,26 @@ encodeViaValue = encode . toJSON
17benchmarks :: Benchmark
19benchmarks :: Benchmark
18benchmarks =
20benchmarks =
19 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) ->
20− bgroup "manual" [
22+ bgroup "encodeManual" [
21 bgroup "direct" [
23 bgroup "direct" [
22 bench "twitter100" $ nf encodeDirect twitter100
24 bench "twitter100" $ nf encodeDirect twitter100
23− , bench "jp100" $ nf encodeDirect jp100
25+ , bench "jp100" $ nf encodeDirect jp100
24 ]
26 ]
25 , bgroup "viaValue" [
27 , bgroup "viaValue" [
26 bench "twitter100" $ nf encodeViaValue twitter100
28 bench "twitter100" $ nf encodeViaValue twitter100
27− , bench "jp100" $ nf encodeViaValue jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
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
42+ , bench "jp100" $ nf decodeDirect jp100
28 ]
43 ]
29 ]
44 ]
benchmarks/Typed/TH.hs+22−7
@@ -1,9 +1,11 @@
1−module Typed.TH (benchmarks) where
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+module Typed.TH (benchmarks, decodeBenchmarks) where
4+
5+import Prelude.Compat
2
6
3−import Control.Applicative
4−import Criterion
5import Data.Aeson hiding (Result)
7import Data.Aeson hiding (Result)
6−import Data.ByteString.Builder as B
8+import Criterion
7import Data.ByteString.Lazy as L
9import Data.ByteString.Lazy as L
8import Twitter.TH
10import Twitter.TH
9import Typed.Common
11import Typed.Common
@@ -17,13 +19,26 @@ encodeViaValue = encode . toJSON
17benchmarks :: Benchmark
19benchmarks :: Benchmark
18benchmarks =
20benchmarks =
19 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) ->
20− bgroup "th" [
22+ bgroup "encodeTH" [
21 bgroup "direct" [
23 bgroup "direct" [
22 bench "twitter100" $ nf encodeDirect twitter100
24 bench "twitter100" $ nf encodeDirect twitter100
23− , bench "jp100" $ nf encodeDirect jp100
25+ , bench "jp100" $ nf encodeDirect jp100
24 ]
26 ]
25 , bgroup "viaValue" [
27 , bgroup "viaValue" [
26 bench "twitter100" $ nf encodeViaValue twitter100
28 bench "twitter100" $ nf encodeViaValue twitter100
27− , bench "jp100" $ nf encodeViaValue jp100
29+ , bench "jp100" $ nf encodeViaValue jp100
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
42+ , bench "jp100" $ nf decodeDirect jp100
28 ]
43 ]
29 ]
44 ]
benchmarks/aeson-benchmarks.cabal+226−59
@@ -1,90 +1,193 @@
1name: aeson-benchmarks
1name: aeson-benchmarks
2version: 0
2version: 0
3build-type: Simple
3build-type: Simple
4+cabal-version: >=1.10
4
5
5−cabal-version: >=1.8
6+flag bytestring-builder
7+ description: Depend on the bytestring-builder package for backwards compatibility.
8+ default: False
9+ manual: False
10+
11+flag local-aeson
12+ description: Build the local version of aeson, to avoid rebuilding aeson's
13+ reverse dependencies for benchmarking (statistics, criterion).
14+ default: True
15+ manual: True
6
16
7library
17library
8− hs-source-dirs: .. .
18+ default-language: Haskell2010
9−
19+
10− exposed-modules:
20+ if flag(bytestring-builder)
11− Data.Aeson
21+ build-depends: bytestring >= 0.9 && < 0.10.4,
12− Data.Aeson.Encode
22+ bytestring-builder >= 0.10.4 && < 1
13− Data.Aeson.Encoding
23+ else
14− Data.Aeson.Encoding.Internal
24+ build-depends: bytestring >= 0.10.4
15− Data.Aeson.Internal
25+
16− Data.Aeson.Internal.Time
26+ if flag(local-aeson)
17− Data.Aeson.Parser
27+
18− Data.Aeson.Types
28+ hs-source-dirs: .. ../ffi ../pure ../attoparsec-iso8601
19− Data.Aeson.TH
29+ c-sources: ../cbits/unescape_string.c
20−
30+ build-depends:
21− Data.Aeson.Encode.Builder
31+ attoparsec,
22− Data.Aeson.Functions
32+ base,
23− Data.Aeson.Parser.Internal
33+ base-compat,
24− Data.Aeson.Parser.Time
34+ containers,
25− Data.Aeson.Types.FromJSON
35+ deepseq,
26− Data.Aeson.Types.Generic
36+ dlist,
27− Data.Aeson.Types.ToJSON
37+ fail,
28− Data.Aeson.Types.Class
38+ ghc-prim,
29− Data.Aeson.Types.Internal
39+ hashable,
40+ mtl,
41+ primitive,
42+ scientific,
43+ syb,
44+ tagged,
45+ template-haskell,
46+ text,
47+ th-abstraction,
48+ time-compat,
49+ time,
50+ transformers,
51+ unordered-containers,
52+ uuid-types,
53+ vector
54+
55+ if !impl(ghc >= 8.6)
56+ build-depends: contravariant
57+
58+ if !impl(ghc >= 8.0)
59+ -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
60+ build-depends: semigroups,
61+ transformers-compat
62+
63+ if !impl(ghc >= 7.10)
64+ -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
65+ build-depends: nats,
66+ void
67+
68+ include-dirs: ../include
69+
70+ ghc-options: -O2 -Wall
71+ cpp-options: -DGENERICS
72+
73+ exposed-modules:
74+ Data.Aeson
75+ Data.Aeson.Encoding
76+ Data.Aeson.Encoding.Builder
77+ Data.Aeson.Encoding.Internal
78+ Data.Aeson.Internal
79+ Data.Aeson.Internal.Functions
80+ Data.Aeson.Internal.Time
81+ Data.Aeson.Parser
82+ Data.Aeson.Parser.Internal
83+ Data.Aeson.Parser.Time
84+ Data.Aeson.Parser.Unescape
85+ Data.Aeson.Parser.UnescapeFFI
86+ Data.Aeson.Parser.UnescapePure
87+ Data.Aeson.TH
88+ Data.Aeson.Text
89+ Data.Aeson.Types
90+ Data.Aeson.Types.Class
91+ Data.Aeson.Types.FromJSON
92+ Data.Aeson.Types.Generic
93+ Data.Aeson.Types.Internal
94+ Data.Aeson.Types.ToJSON
95+ Data.Attoparsec.Time
96+ Data.Attoparsec.Time.Internal
97+
98+ else
30
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
31 build-depends:
125 build-depends:
32− attoparsec >= 0.13.0.1,
126+ aeson-benchmarks,
33− base == 4.*,
127+ base,
34− base-compat >= 0.9.1 && <0.10,
128+ criterion,
35− time-locale-compat >=0.1.1 && <0.2,
36− bytestring >= 0.10.4.0,
37− containers,
38 deepseq,
129 deepseq,
39− dlist >= 0.2,
130+ template-haskell
40− fail == 4.9.*,
131+
41− ghc-prim >= 0.2,
132+executable aeson-benchmark-escape
42− hashable >= 1.1.2.0,
133+ default-language: Haskell2010
43− mtl,
134+ main-is: Escape.hs
44− scientific >= 0.3.1 && < 0.4,
135+ hs-source-dirs: ../examples .
45− syb,
136+ ghc-options: -Wall -O2 -rtsopts
46− tagged >=0.8.3 && <0.9,
137+ if flag(local-aeson)
47− template-haskell >= 2.4,
138+ build-depends:
48− text >= 1.1.1.0,
139+ aeson-benchmarks,
49− time,
140+ base,
50− transformers,
141+ base-compat,
51− unordered-containers >= 0.2.3.0,
142+ bytestring,
52− vector >= 0.7.1
143+ criterion >= 1.0,
53−
144+ deepseq,
54− if impl(ghc >=7.8)
145+ ghc-prim,
55− cpp-options: -DHAS_COERCIBLE
146+ text
56−
147+ else
57− if !impl(ghc >= 8.0)
148+ -- Disabled because of inaccessible internals
58− -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
149+ buildable: False
59− build-depends: semigroups >= 0.16.1 && < 0.19
60−
61− include-dirs: ../include
62−
63− ghc-options: -O2 -Wall
64− cpp-options: -DGENERICS
65
150
66executable aeson-benchmark-compare
151executable aeson-benchmark-compare
152+ default-language: Haskell2010
67 main-is: Compare.hs
153 main-is: Compare.hs
68 hs-source-dirs: ../examples .
154 hs-source-dirs: ../examples .
69 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
70 build-depends:
162 build-depends:
71 aeson-benchmarks,
163 aeson-benchmarks,
72 base,
164 base,
165+ base-compat,
73 buffer-builder,
166 buffer-builder,
74 bytestring,
167 bytestring,
75 criterion >= 1.0,
168 criterion >= 1.0,
76 deepseq,
169 deepseq,
77 ghc-prim,
170 ghc-prim,
78− json-builder,
79 text
171 text
80
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+
81executable aeson-benchmark-micro
182executable aeson-benchmark-micro
183+ default-language: Haskell2010
82 main-is: Micro.hs
184 main-is: Micro.hs
83 hs-source-dirs: ../examples .
185 hs-source-dirs: ../examples .
84 ghc-options: -Wall -O2 -rtsopts
186 ghc-options: -Wall -O2 -rtsopts
85 build-depends:
187 build-depends:
86 aeson-benchmarks,
188 aeson-benchmarks,
87 base,
189 base,
190+ base-compat,
88 bytestring,
191 bytestring,
89 criterion >= 1.0,
192 criterion >= 1.0,
90 deepseq,
193 deepseq,
@@ -92,25 +195,46 @@ executable aeson-benchmark-micro
92 text
195 text
93
196
94executable aeson-benchmark-typed
197executable aeson-benchmark-typed
198+ default-language: Haskell2010
95 main-is: Typed.hs
199 main-is: Typed.hs
96 hs-source-dirs: ../examples .
200 hs-source-dirs: ../examples .
97 ghc-options: -Wall -O2 -rtsopts
201 ghc-options: -Wall -O2 -rtsopts
202+ other-modules:
203+ Twitter
204+ Twitter.Generic
205+ Twitter.Manual
206+ Twitter.Options
207+ Twitter.TH
208+ Typed.Common
209+ Typed.Generic
210+ Typed.Manual
211+ Typed.TH
98 build-depends:
212 build-depends:
99 aeson-benchmarks,
213 aeson-benchmarks,
100 base,
214 base,
101− bytestring,
215+ base-compat,
102 criterion >= 1.0,
216 criterion >= 1.0,
103 deepseq,
217 deepseq,
104 ghc-prim,
218 ghc-prim,
105 text,
219 text,
106 time
220 time
107
221
222+ if flag(bytestring-builder)
223+ build-depends: bytestring >= 0.9 && < 0.10.4,
224+ bytestring-builder >= 0.10.4 && < 1
225+ else
226+ build-depends: bytestring >= 0.10.4
227+ if impl(ghc < 8.0)
228+ build-depends: semigroups
229+
108executable aeson-benchmark-compare-with-json
230executable aeson-benchmark-compare-with-json
231+ default-language: Haskell2010
109 main-is: CompareWithJSON.hs
232 main-is: CompareWithJSON.hs
110 ghc-options: -Wall -O2 -rtsopts
233 ghc-options: -Wall -O2 -rtsopts
111 build-depends:
234 build-depends:
112 aeson-benchmarks,
235 aeson-benchmarks,
113 base,
236 base,
237+ base-compat,
114 blaze-builder,
238 blaze-builder,
115 bytestring,
239 bytestring,
116 criterion,
240 criterion,
@@ -119,53 +243,65 @@ executable aeson-benchmark-compare-with-json
119 text
243 text
120
244
121executable aeson-benchmark-aeson-encode
245executable aeson-benchmark-aeson-encode
246+ default-language: Haskell2010
122 main-is: AesonEncode.hs
247 main-is: AesonEncode.hs
123 ghc-options: -Wall -O2 -rtsopts
248 ghc-options: -Wall -O2 -rtsopts
124 build-depends:
249 build-depends:
125 aeson-benchmarks,
250 aeson-benchmarks,
126 attoparsec,
251 attoparsec,
127 base,
252 base,
253+ base-compat,
128 bytestring,
254 bytestring,
129 deepseq,
255 deepseq,
130 time
256 time
131
257
132executable aeson-benchmark-aeson-parse
258executable aeson-benchmark-aeson-parse
259+ default-language: Haskell2010
133 main-is: AesonParse.hs
260 main-is: AesonParse.hs
134 ghc-options: -Wall -O2 -rtsopts
261 ghc-options: -Wall -O2 -rtsopts
135 build-depends:
262 build-depends:
136 aeson-benchmarks,
263 aeson-benchmarks,
137 attoparsec,
264 attoparsec,
138 base,
265 base,
266+ base-compat,
139 bytestring,
267 bytestring,
140 time
268 time
141
269
142executable aeson-benchmark-json-parse
270executable aeson-benchmark-json-parse
271+ default-language: Haskell2010
143 main-is: JsonParse.hs
272 main-is: JsonParse.hs
144 ghc-options: -Wall -O2 -rtsopts
273 ghc-options: -Wall -O2 -rtsopts
145 build-depends:
274 build-depends:
146 base,
275 base,
276+ base-compat,
147 deepseq,
277 deepseq,
148 json,
278 json,
149 time
279 time
150
280
151executable aeson-benchmark-dates
281executable aeson-benchmark-dates
282+ default-language: Haskell2010
152 main-is: Dates.hs
283 main-is: Dates.hs
153 ghc-options: -Wall -O2 -rtsopts
284 ghc-options: -Wall -O2 -rtsopts
154 build-depends:
285 build-depends:
155 base,
286 base,
287+ base-compat,
156 bytestring,
288 bytestring,
157 criterion,
289 criterion,
158 deepseq,
290 deepseq,
159 aeson-benchmarks,
291 aeson-benchmarks,
160 text,
292 text,
161 time
293 time
294+ if impl(ghc >= 8.2)
295+ ghc-options: -Wno-simplifiable-class-constraints
162
296
163executable aeson-benchmark-map
297executable aeson-benchmark-map
298+ default-language: Haskell2010
164 main-is: AesonMap.hs
299 main-is: AesonMap.hs
165 ghc-options: -Wall -O2 -rtsopts
300 ghc-options: -Wall -O2 -rtsopts
166 build-depends:
301 build-depends:
167 aeson-benchmarks,
302 aeson-benchmarks,
168 base,
303 base,
304+ base-compat,
169 criterion >= 1.0,
305 criterion >= 1.0,
170 bytestring,
306 bytestring,
171 containers,
307 containers,
@@ -173,6 +309,37 @@ executable aeson-benchmark-map
173 hashable,
309 hashable,
174 tagged,
310 tagged,
175 text,
311 text,
176− transformers,
177− transformers-compat,
178 unordered-containers
312 unordered-containers
313+
314+executable aeson-benchmark-foldable
315+ default-language: Haskell2010
316+ main-is: AesonFoldable.hs
317+ ghc-options: -Wall -O2 -rtsopts
318+ build-depends:
319+ aeson-benchmarks,
320+ base,
321+ base-compat,
322+ criterion >= 1.0,
323+ bytestring,
324+ containers,
325+ deepseq,
326+ hashable,
327+ tagged,
328+ text,
329+ unordered-containers,
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+313−20
@@ -1,41 +1,334 @@
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−# 0.12.0.0
3+### upcoming
4
4
5−* Added list specific members to `ToJSON` and `FromJSON` classes. In the same
5+* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− way `Read` / `Show` handle lists specifically. This removes need for
6+ standard `{numerator=..., denumerator=...}` encoding.
7− overlapping instances to handle `String`.
8
7
9−* Added higher rank classes: `ToJSON1`, `ToJSON2`, `FromJSON1`, and `FromJSON2`.
8+### 1.4.6.0
10
9
11−* Modified instances for `Map` and `HashMap`. Introduced new `FromJSONKey` and
10+* Provide a clearer error message when a required tagKey for a constructor is missing, thanks to Guru Devanla.
12− `ToJSONKey` type classes.
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`
13
12
14−* Export `decodeWith`, `decodeStrictWith`, `eitherDecodeWith`, and
13+* Add `formatPath` and `formatRelativePath` functions to turn a `JSONPath` into a `String`, thanks to Robbie McMichael
15− `eitherDecodeStrictWith` from `Data.Aeson.Parser`. This allows
16− decoding using explicit parsers instead of using `FromJSON`
17− instances.
18
14
19−* Remove the `contents` field encoding with `allNullaryToStringTag = False`,
20− giving us `{ "tag" : "c1" }` instead of `{ "tag" : "c1", contents : [] }`.
21− The contents field is optional when parsing so this is only a breaking change for ToJSON.
22
15
23−* Fix a bug where `genericToEncoding` with `unwrapUnaryRecords = True`
16+### 1.4.5.0
24− would produce an invalid encoding: `"unwrap\":""`.
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+
257+# 1.0.0.0
258+
259+Major enhancements:
260+
261+* Introduced new `FromJSONKey` and `ToJSONKey` type classes that are
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.
265+
266+* Added higher rank classes: `ToJSON1`, `ToJSON2`, `FromJSON1`, and
267+ `FromJSON2`.
268+
269+* Added `Data.Aeson.Encoding` with functions to safely write `ToJSON`
270+ instances using `toEncoding`.
271+
272+Other enhancements:
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+
281+* Added list specific members to `ToJSON` and `FromJSON` classes. In
282+ the same way `Read` and `Show` handle lists specifically. This
283+ removes need for overlapping instances to handle `String`.
284+
285+* Added a new `sumEncoding` option `UntaggedValue` which prevents
286+ objects from being tagged with the constructor name.
287+
288+* JSONPaths are now tracked in instances derived with template-haskell
289+ and generics.
25
290
26* Get rid of redundancy of JSONPath error messages in nested records.
291* Get rid of redundancy of JSONPath error messages in nested records.
27
292
28 `eitherDecode "{\"x\":{\"a\": [1,2,true]}}" :: Either String Y`
293 `eitherDecode "{\"x\":{\"a\": [1,2,true]}}" :: Either String Y`
29− previously yielded `Error in $.x.a[2]: failed to parse field" x:
294+ previously yielded
30− failed to parse field a: expected Int, encountered Boolean` and now
295+ `Error in $.x.a[2]: failed to parse field" x: failed to parse field a: expected Int, encountered Boolean`
31− yields `Error in $.x.a[2]: expected Int, encountered Boolean"`.
296+ and now yields `Error in $.x.a[2]: expected Int, encountered Boolean"`.
32
297
33 Some users might prefer to insert `modifyFailure` themselves to
298 Some users might prefer to insert `modifyFailure` themselves to
34 customize error messages, which previously prevented the use of
299 customize error messages, which previously prevented the use of
35 `(.:)`.
300 `(.:)`.
36
301
302+* Backwards compatibility with `bytestring-0.9` using the
303+ `bytestring-builder` compatibility package.
304+
305+* Export `decodeWith`, `decodeStrictWith`, `eitherDecodeWith`, and
306+ `eitherDecodeStrictWith` from `Data.Aeson.Parser`. This allows
307+ decoding using explicit parsers instead of using `FromJSON`
308+ instances.
309+
310+* Un-orphan internal instances to render them in haddocks.
311+
312+Other changes:
313+
314+* Integral `FromJSON` instances now only accept integral
315+ values. E.g. parsing `3.14` to `Int` fails instead of succeeding
316+ with the value `3`.
317+
318+* Over/underflows are now caught for bounded numeric types.
319+
320+* Remove the `contents` field encoding with `allNullaryToStringTag = False`,
321+ giving us `{ "tag" : "c1" }` instead of `{ "tag" : "c1", contents : [] }`.
322+ The contents field is optional when parsing so this is only a breaking
323+ change for ToJSON instances.
324+
325+* Fix a bug where `genericToEncoding` with `unwrapUnaryRecords = True`
326+ would produce an invalid encoding: `"unwrap\":""`.
327+
37* `ToJSON` instances using `genericToEncoding` and `omitNothingFields`
328* `ToJSON` instances using `genericToEncoding` and `omitNothingFields`
38− no longer produces invalid JSON.
329+ no longer produce invalid JSON.
330+
331+* Added instances for `DList`, `Compose`, `Product`, `Sum`.
39
332
40### 0.11.2.0
333### 0.11.2.0
41
334
examples/Generic.hs+7−5
@@ -6,17 +6,19 @@
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−
9+{-# LANGUAGE NoImplicitPrelude #-}
10{-# LANGUAGE OverloadedStrings #-}
10{-# LANGUAGE OverloadedStrings #-}
11
11
12+module Main (main) where
13+
14+import Prelude.Compat
15+
12import Data.Aeson (FromJSON, ToJSON, decode, encode)
16import Data.Aeson (FromJSON, ToJSON, decode, encode)
13import qualified Data.ByteString.Lazy.Char8 as BL
17import qualified Data.ByteString.Lazy.Char8 as BL
18+import GHC.Generics (Generic)
14
19
15-- To decode or encode a value using the generic machinery, we must
20-- To decode or encode a value using the generic machinery, we must
16-- make the type an instance of the Generic class.
21-- make the type an instance of the Generic class.
17−
18−import GHC.Generics (Generic)
19−
20data Coord = Coord { x :: Double, y :: Double }
22data Coord = Coord { x :: Double, y :: Double }
21 deriving (Show, Generic)
23 deriving (Show, Generic)
22
24
@@ -31,5 +33,5 @@ main :: IO ()
31main = do
33main = do
32 let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
34 let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
33 print req
35 print req
34− let reply = Coord 123.4 20
36+ let reply = Coord { x = 123.4, y = 20 }
35 BL.putStrLn (encode reply)
37 BL.putStrLn (encode reply)
examples/Simplest.hs+8−2
@@ -1,6 +1,12 @@
1−{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE RecordWildCards #-}
2
4
3−import Control.Applicative ((<$>), (<*>), empty)
5+module Main (main) where
6+
7+import Prelude.Compat
8+
9+import Control.Applicative (empty)
4import Data.Aeson
10import Data.Aeson
5import Data.Monoid
11import Data.Monoid
6import qualified Data.ByteString.Lazy.Char8 as BL
12import qualified Data.ByteString.Lazy.Char8 as BL
examples/TemplateHaskell.hs+6−3
@@ -1,10 +1,13 @@
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−
4+{-# LANGUAGE NoImplicitPrelude #-}
5+{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
6
7
7−{-# LANGUAGE OverloadedStrings #-}
8+module Main (main) where
9+
10+import Prelude.Compat
8
11
9import Data.Aeson (decode, encode)
12import Data.Aeson (decode, encode)
10import Data.Aeson.TH (deriveJSON, defaultOptions)
13import Data.Aeson.TH (deriveJSON, defaultOptions)
@@ -21,5 +24,5 @@ main :: IO ()
21main = do
24main = do
22 let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
25 let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord
23 print req
26 print req
24− let reply = Coord 123.4 20
27+ let reply = Coord { x = 123.4, y = 20 }
25 BL.putStrLn (encode reply)
28 BL.putStrLn (encode reply)
examples/Twitter.hs+12−6
@@ -8,22 +8,28 @@
8-- renamed "type" to "type_" in the *.json files, to avoid overlap
8-- renamed "type" to "type_" in the *.json files, to avoid overlap
9-- with a Haskell reserved keyword.
9-- with a Haskell reserved keyword.
10
10
11−{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}
11+{-# LANGUAGE DeriveDataTypeable #-}
12+{-# LANGUAGE DeriveGeneric #-}
13+{-# LANGUAGE NoImplicitPrelude #-}
12
14
13−module Twitter (
15+module Twitter
16+ (
14 Metadata(..)
17 Metadata(..)
15 , Geo(..)
18 , Geo(..)
16 , Story(..)
19 , Story(..)
17 , Result(..)
20 , Result(..)
18 ) where
21 ) where
19
22
23+import Prelude.Compat
24+
25+import Control.DeepSeq
20import Data.Data (Typeable, Data)
26import Data.Data (Typeable, Data)
21import Data.Int (Int64)
27import Data.Int (Int64)
22import Data.Text (Text)
28import Data.Text (Text)
23−-- import Data.Time (ZonedTime)
24import GHC.Generics (Generic)
29import GHC.Generics (Generic)
25−import Prelude hiding (id)
30+
26−import Control.DeepSeq
31+{-# ANN module "Hlint: ignore Use camelCase" #-}
32+{-# ANN module "Hlint: ignore Use newtype instead of data" #-}
27
33
28data Metadata = Metadata {
34data Metadata = Metadata {
29 result_type :: Text
35 result_type :: Text
@@ -47,7 +53,7 @@ data Story = Story {
47 , metadata :: Metadata
53 , metadata :: Metadata
48 , to_user_id :: Maybe Int64
54 , to_user_id :: Maybe Int64
49 , text :: Text
55 , text :: Text
50− , id :: Int64
56+ , id_ :: Int64
51 , from_user_id :: Int64
57 , from_user_id :: Int64
52 , geo :: Maybe Geo
58 , geo :: Maybe Geo
53 , iso_language_code :: Text
59 , iso_language_code :: Text
examples/Twitter/Generic.hs+27−9
@@ -1,5 +1,7 @@
1-- Use GHC generics to automatically generate good instances.
1-- Use GHC generics to automatically generate good instances.
2
2
3+{-# LANGUAGE NoImplicitPrelude #-}
4+
3{-# OPTIONS_GHC -fno-warn-orphans #-}
5{-# OPTIONS_GHC -fno-warn-orphans #-}
4
6
5module Twitter.Generic
7module Twitter.Generic
@@ -10,17 +12,33 @@ module Twitter.Generic
10 , Result(..)
12 , Result(..)
11 ) where
13 ) where
12
14
13−import Data.Aeson (ToJSON, FromJSON)
15+import Prelude.Compat ()
16+
14import Twitter
17import Twitter
18+import Twitter.Options
19+
20+import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)
15
21
16−instance ToJSON Metadata
22+instance ToJSON Metadata where
17−instance FromJSON Metadata
23+ toJSON = genericToJSON twitterOptions
24+ toEncoding = genericToEncoding twitterOptions
25+instance FromJSON Metadata where
26+ parseJSON = genericParseJSON twitterOptions
18
27
19−instance ToJSON Geo
28+instance ToJSON Geo where
20−instance FromJSON Geo
29+ toJSON = genericToJSON twitterOptions
30+ toEncoding = genericToEncoding twitterOptions
31+instance FromJSON Geo where
32+ parseJSON = genericParseJSON twitterOptions
21
33
22−instance ToJSON Story
34+instance ToJSON Story where
23−instance FromJSON Story
35+ toJSON = genericToJSON twitterOptions
36+ toEncoding = genericToEncoding twitterOptions
37+instance FromJSON Story where
38+ parseJSON = genericParseJSON twitterOptions
24
39
25−instance ToJSON Result
40+instance ToJSON Result where
26−instance FromJSON Result
41+ toJSON = genericToJSON twitterOptions
42+ toEncoding = genericToEncoding twitterOptions
43+instance FromJSON Result where
44+ parseJSON = genericParseJSON twitterOptions
examples/Twitter/Manual.hs+11−6
@@ -1,6 +1,8 @@
1-- Manually write instances.
1-- Manually write instances.
2+{-# LANGUAGE NoImplicitPrelude #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE RecordWildCards #-}
2
5
3−{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
4{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5
7
6module Twitter.Manual
8module Twitter.Manual
@@ -11,13 +13,16 @@ module Twitter.Manual
11 , Result(..)
13 , Result(..)
12 ) where
14 ) where
13
15
16+import Prelude.Compat
17+
14import Control.Applicative
18import Control.Applicative
15−import Data.Aeson hiding (Result)
19+import Data.Semigroup ((<>))
16−import Data.Monoid ((<>))
17−import Prelude hiding (id)
18import Twitter
20import Twitter
19
21
22+import Data.Aeson hiding (Result)
23+
20instance ToJSON Metadata where
24instance ToJSON Metadata where
25+
21 toJSON Metadata{..} = object [
26 toJSON Metadata{..} = object [
22 "result_type" .= result_type
27 "result_type" .= result_type
23 ]
28 ]
@@ -55,7 +60,7 @@ instance ToJSON Story where
55 , "metadata" .= metadata
60 , "metadata" .= metadata
56 , "to_user_id" .= to_user_id
61 , "to_user_id" .= to_user_id
57 , "text" .= text
62 , "text" .= text
58− , "id" .= id
63+ , "id" .= id_
59 , "from_user_id" .= from_user_id
64 , "from_user_id" .= from_user_id
60 , "geo" .= geo
65 , "geo" .= geo
61 , "iso_language_code" .= iso_language_code
66 , "iso_language_code" .= iso_language_code
@@ -72,7 +77,7 @@ instance ToJSON Story where
72 <> "metadata" .= metadata
77 <> "metadata" .= metadata
73 <> "to_user_id" .= to_user_id
78 <> "to_user_id" .= to_user_id
74 <> "text" .= text
79 <> "text" .= text
75− <> "id" .= id
80+ <> "id" .= id_
76 <> "from_user_id" .= from_user_id
81 <> "from_user_id" .= from_user_id
77 <> "geo" .= geo
82 <> "geo" .= geo
78 <> "iso_language_code" .= iso_language_code
83 <> "iso_language_code" .= iso_language_code
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+10−5
@@ -1,6 +1,8 @@
1-- Use Template Haskell to generate good instances.
1-- Use Template Haskell to generate good instances.
2
2
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
5+
4{-# OPTIONS_GHC -fno-warn-orphans #-}
6{-# OPTIONS_GHC -fno-warn-orphans #-}
5
7
6module Twitter.TH
8module Twitter.TH
@@ -11,10 +13,13 @@ module Twitter.TH
11 , Result(..)
13 , Result(..)
12 ) where
14 ) where
13
15
14−import Data.Aeson.TH
16+
15import Twitter
17import Twitter
18+import Twitter.Options
19+
20+import Data.Aeson.TH
16
21
17−$(deriveJSON defaultOptions ''Metadata)
22+$(deriveJSON twitterOptions ''Metadata)
18−$(deriveJSON defaultOptions ''Geo)
23+$(deriveJSON twitterOptions ''Geo)
19−$(deriveJSON defaultOptions ''Story)
24+$(deriveJSON twitterOptions ''Story)
20−$(deriveJSON defaultOptions ''Result)
25+$(deriveJSON twitterOptions ''Result)
examples/aeson-examples.cabal+3
@@ -9,6 +9,7 @@ executable aeson-example-generic
9 build-depends:
9 build-depends:
10 aeson >= 0.10,
10 aeson >= 0.10,
11 base,
11 base,
12+ base-compat,
12 bytestring,
13 bytestring,
13 ghc-prim
14 ghc-prim
14
15
@@ -18,6 +19,7 @@ executable aeson-example-simplest
18 build-depends:
19 build-depends:
19 aeson,
20 aeson,
20 base,
21 base,
22+ base-compat,
21 bytestring
23 bytestring
22
24
23executable aeson-example-th
25executable aeson-example-th
@@ -26,4 +28,5 @@ executable aeson-example-th
26 build-depends:
28 build-depends:
27 aeson,
29 aeson,
28 base,
30 base,
31+ base-compat,
29 bytestring
32 bytestring
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
include/overlapping-compat.h−7
@@ -1,15 +1,8 @@
1#if __GLASGOW_HASKELL__ >= 710
1#if __GLASGOW_HASKELL__ >= 710
2#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
2#define OVERLAPPABLE_ {-# OVERLAPPABLE #-}
3#define OVERLAPPING_ {-# OVERLAPPING #-}
3#define OVERLAPPING_ {-# OVERLAPPING #-}
4−#ifdef NEEDS_INCOHERENT
5−#define INCOHERENT_ {-# INCOHERENT #-}
6−#endif
7#else
4#else
8{-# LANGUAGE OverlappingInstances #-}
5{-# LANGUAGE OverlappingInstances #-}
9#define OVERLAPPABLE_
6#define OVERLAPPABLE_
10#define OVERLAPPING_
7#define OVERLAPPING_
11−#ifdef NEEDS_INCOHERENT
12−{-# LANGUAGE IncoherentInstances #-}
13−#define INCOHERENT_
14−#endif
15#endif
8#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.yamlnew file+12
@@ -0,0 +1,12 @@
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
10+packages:
11+- benchmarks
12+extra-deps: []
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-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
stack.yamldeleted−7
@@ -1,7 +0,0 @@
1−resolver: lts-5.16
2−packages:
3−- '.'
4−- benchmarks
5−extra-deps:
6−- generic-deriving-1.10.4.1
7−flags: {}
tests/DataFamilies/Encoders.hs+5−2
@@ -1,13 +1,16 @@
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
9−import Options
10import DataFamilies.Types
12import DataFamilies.Types
13+import Options
11
14
12--------------------------------------------------------------------------------
15--------------------------------------------------------------------------------
13-- Nullary encoders/decoders
16-- Nullary encoders/decoders
tests/DataFamilies/Instances.hs+6−3
@@ -1,17 +1,19 @@
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
7−module DataFamilies.Instances where
9+module DataFamilies.Instances () where
10+
11+import Prelude.Compat
8
12
9−import Control.Applicative
10import Data.Aeson.TH
13import Data.Aeson.TH
11import Data.Aeson.Types (FromJSON(..))
14import Data.Aeson.Types (FromJSON(..))
12import DataFamilies.Types
15import DataFamilies.Types
13import Test.QuickCheck (Arbitrary(..), elements, oneof)
16import Test.QuickCheck (Arbitrary(..), elements, oneof)
14−import Prelude
15
17
16instance (Arbitrary a) => Arbitrary (Approx a) where
18instance (Arbitrary a) => Arbitrary (Approx a) where
17 arbitrary = Approx <$> arbitrary
19 arbitrary = Approx <$> arbitrary
@@ -24,6 +26,7 @@ instance Arbitrary a => Arbitrary (SomeType c () a) where
24 , Unary <$> arbitrary
26 , Unary <$> arbitrary
25 , Product <$> arbitrary <*> arbitrary <*> arbitrary
27 , Product <$> arbitrary <*> arbitrary <*> arbitrary
26 , Record <$> arbitrary <*> arbitrary <*> arbitrary
28 , Record <$> arbitrary <*> arbitrary <*> arbitrary
29+ , List <$> arbitrary
27 ]
30 ]
28
31
29instance Arbitrary (GADT String) where
32instance Arbitrary (GADT String) where
tests/DataFamilies/Properties.hs+7−4
@@ -1,18 +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 ()
10+import PropUtils
7
11
8−import Properties hiding (tests)
9
12
10−import Test.Framework (Test, testGroup)
13+import Test.Tasty (TestTree, testGroup)
11−import Test.Framework.Providers.QuickCheck2 (testProperty)
14+import Test.Tasty.QuickCheck (testProperty)
12
15
13--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
14
17
15−tests :: Test
18+tests :: TestTree
16tests = testGroup "data families" [
19tests = testGroup "data families" [
17 testGroup "template-haskell" [
20 testGroup "template-haskell" [
18 testGroup "toJSON" [
21 testGroup "toJSON" [
tests/DataFamilies/Types.hs+8−3
@@ -1,14 +1,17 @@
1-- DataKinds is needed for deriveAll0 calls on GHC 8
1-- DataKinds is needed for deriveAll0 calls on GHC 8
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE NoImplicitPrelude #-}
6{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE StandaloneDeriving #-}
7−{-# LANGUAGE DeriveGeneric #-}
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(..))
@@ -24,7 +27,9 @@ data instance SomeType c () a = Nullary
24 | Record { testOne :: Double
27 | Record { testOne :: Double
25 , testTwo :: Maybe Bool
28 , testTwo :: Maybe Bool
26 , testThree :: Maybe a
29 , testThree :: Maybe a
27− } deriving (Eq, Show)
30+ }
31+ | List [a]
32+ deriving (Eq, Show)
28
33
29data family Approx a
34data family Approx a
30newtype instance Approx a = Approx { fromApprox :: a }
35newtype instance Approx a = Approx { fromApprox :: a }
tests/Encoders.hs+232−7
@@ -1,7 +1,12 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE GADTs #-}
2{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
3
5
4−module Encoders where
6+module Encoders (module Encoders) where
7+
8+import Prelude.Compat
9+import Data.Text (Text)
5
10
6import Data.Aeson.TH
11import Data.Aeson.TH
7import Data.Aeson.Types
12import Data.Aeson.Types
@@ -92,20 +97,49 @@ gNullaryToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingle
92gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
97gNullaryParseJSONObjectWithSingleField :: Value -> Parser Nullary
93gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
98gNullaryParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
94
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"
95
112
96--------------------------------------------------------------------------------
113--------------------------------------------------------------------------------
97-- SomeType encoders/decoders
114-- SomeType encoders/decoders
98--------------------------------------------------------------------------------
115--------------------------------------------------------------------------------
99
116
117+-- Unary types
118+type LiftToJSON f a =
119+ (a -> Value) -> ([a] -> Value) -> f a -> Value
120+type LiftToEncoding f a =
121+ (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
122+type LiftParseJSON f a =
123+ (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)
124+
100thSomeTypeToJSON2ElemArray :: SomeType Int -> Value
125thSomeTypeToJSON2ElemArray :: SomeType Int -> Value
101thSomeTypeToJSON2ElemArray = $(mkToJSON opts2ElemArray ''SomeType)
126thSomeTypeToJSON2ElemArray = $(mkToJSON opts2ElemArray ''SomeType)
102
127
103thSomeTypeToEncoding2ElemArray :: SomeType Int -> Encoding
128thSomeTypeToEncoding2ElemArray :: SomeType Int -> Encoding
104thSomeTypeToEncoding2ElemArray = $(mkToEncoding opts2ElemArray ''SomeType)
129thSomeTypeToEncoding2ElemArray = $(mkToEncoding opts2ElemArray ''SomeType)
105
130
131+thSomeTypeLiftToJSON2ElemArray :: LiftToJSON SomeType a
132+thSomeTypeLiftToJSON2ElemArray = $(mkLiftToJSON opts2ElemArray ''SomeType)
133+
134+thSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
135+thSomeTypeLiftToEncoding2ElemArray = $(mkLiftToEncoding opts2ElemArray ''SomeType)
136+
106thSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
137thSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
107thSomeTypeParseJSON2ElemArray = $(mkParseJSON opts2ElemArray ''SomeType)
138thSomeTypeParseJSON2ElemArray = $(mkParseJSON opts2ElemArray ''SomeType)
108
139
140+thSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
141+thSomeTypeLiftParseJSON2ElemArray = $(mkLiftParseJSON opts2ElemArray ''SomeType)
142+
109
143
110thSomeTypeToJSONTaggedObject :: SomeType Int -> Value
144thSomeTypeToJSONTaggedObject :: SomeType Int -> Value
111thSomeTypeToJSONTaggedObject = $(mkToJSON optsTaggedObject ''SomeType)
145thSomeTypeToJSONTaggedObject = $(mkToJSON optsTaggedObject ''SomeType)
@@ -113,21 +147,36 @@ thSomeTypeToJSONTaggedObject = $(mkToJSON optsTaggedObject ''SomeType)
113thSomeTypeToEncodingTaggedObject :: SomeType Int -> Encoding
147thSomeTypeToEncodingTaggedObject :: SomeType Int -> Encoding
114thSomeTypeToEncodingTaggedObject = $(mkToEncoding optsTaggedObject ''SomeType)
148thSomeTypeToEncodingTaggedObject = $(mkToEncoding optsTaggedObject ''SomeType)
115
149
150+thSomeTypeLiftToJSONTaggedObject :: LiftToJSON SomeType a
151+thSomeTypeLiftToJSONTaggedObject = $(mkLiftToJSON optsTaggedObject ''SomeType)
152+
153+thSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
154+thSomeTypeLiftToEncodingTaggedObject = $(mkLiftToEncoding optsTaggedObject ''SomeType)
155+
116thSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
156thSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
117thSomeTypeParseJSONTaggedObject = $(mkParseJSON optsTaggedObject ''SomeType)
157thSomeTypeParseJSONTaggedObject = $(mkParseJSON optsTaggedObject ''SomeType)
118
158
159+thSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
160+thSomeTypeLiftParseJSONTaggedObject = $(mkLiftParseJSON optsTaggedObject ''SomeType)
161+
119
162
120thSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
163thSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
121−thSomeTypeToJSONObjectWithSingleField =
164+thSomeTypeToJSONObjectWithSingleField = $(mkToJSON optsObjectWithSingleField ''SomeType)
122− $(mkToJSON optsObjectWithSingleField ''SomeType)
123
165
124thSomeTypeToEncodingObjectWithSingleField :: SomeType Int -> Encoding
166thSomeTypeToEncodingObjectWithSingleField :: SomeType Int -> Encoding
125−thSomeTypeToEncodingObjectWithSingleField =
167+thSomeTypeToEncodingObjectWithSingleField = $(mkToEncoding optsObjectWithSingleField ''SomeType)
126− $(mkToEncoding optsObjectWithSingleField ''SomeType)
168+
169+thSomeTypeLiftToJSONObjectWithSingleField :: LiftToJSON SomeType a
170+thSomeTypeLiftToJSONObjectWithSingleField = $(mkLiftToJSON optsObjectWithSingleField ''SomeType)
171+
172+thSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
173+thSomeTypeLiftToEncodingObjectWithSingleField = $(mkLiftToEncoding optsObjectWithSingleField ''SomeType)
127
174
128thSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
175thSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
129−thSomeTypeParseJSONObjectWithSingleField =
176+thSomeTypeParseJSONObjectWithSingleField = $(mkParseJSON optsObjectWithSingleField ''SomeType)
130− $(mkParseJSON optsObjectWithSingleField ''SomeType)
177+
178+thSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
179+thSomeTypeLiftParseJSONObjectWithSingleField = $(mkLiftParseJSON optsObjectWithSingleField ''SomeType)
131
180
132
181
133gSomeTypeToJSON2ElemArray :: SomeType Int -> Value
182gSomeTypeToJSON2ElemArray :: SomeType Int -> Value
@@ -139,6 +188,15 @@ gSomeTypeToEncoding2ElemArray = genericToEncoding opts2ElemArray
139gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
188gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
140gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
189gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
141
190
191+gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
192+gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
193+
194+gSomeTypeLiftToJSON2ElemArray :: LiftToJSON SomeType a
195+gSomeTypeLiftToJSON2ElemArray = genericLiftToJSON opts2ElemArray
196+
197+gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
198+gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
199+
142
200
143gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
201gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
144gSomeTypeToJSONTaggedObject = genericToJSON optsTaggedObject
202gSomeTypeToJSONTaggedObject = genericToJSON optsTaggedObject
@@ -149,6 +207,15 @@ gSomeTypeToEncodingTaggedObject = genericToEncoding optsTaggedObject
149gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
207gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
150gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
208gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
151
209
210+gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
211+gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
212+
213+gSomeTypeLiftToJSONTaggedObject :: LiftToJSON SomeType a
214+gSomeTypeLiftToJSONTaggedObject = genericLiftToJSON optsTaggedObject
215+
216+gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
217+gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
218+
152
219
153gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
220gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
154gSomeTypeToJSONObjectWithSingleField = genericToJSON optsObjectWithSingleField
221gSomeTypeToJSONObjectWithSingleField = genericToJSON optsObjectWithSingleField
@@ -159,6 +226,15 @@ gSomeTypeToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingl
159gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
226gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
160gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
227gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
161
228
229+gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
230+gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
231+
232+gSomeTypeLiftToJSONObjectWithSingleField :: LiftToJSON SomeType a
233+gSomeTypeLiftToJSONObjectWithSingleField = genericLiftToJSON optsObjectWithSingleField
234+
235+gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
236+gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
237+
162
238
163gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
239gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
164gSomeTypeToJSONOmitNothingFields = genericToJSON optsOmitNothingFields
240gSomeTypeToJSONOmitNothingFields = genericToJSON optsOmitNothingFields
@@ -167,6 +243,104 @@ gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
167gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
243gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
168
244
169
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+
321+-------------------------------------------------------------------------------
322+-- EitherTextInt encoders/decodes
323+-------------------------------------------------------------------------------
324+
325+thEitherTextIntToJSONUntaggedValue :: EitherTextInt -> Value
326+thEitherTextIntToJSONUntaggedValue = $(mkToJSON optsUntaggedValue ''EitherTextInt)
327+
328+thEitherTextIntToEncodingUntaggedValue :: EitherTextInt -> Encoding
329+thEitherTextIntToEncodingUntaggedValue = $(mkToEncoding optsUntaggedValue ''EitherTextInt)
330+
331+thEitherTextIntParseJSONUntaggedValue :: Value -> Parser EitherTextInt
332+thEitherTextIntParseJSONUntaggedValue = $(mkParseJSON optsUntaggedValue ''EitherTextInt)
333+
334+
335+gEitherTextIntToJSONUntaggedValue :: EitherTextInt -> Value
336+gEitherTextIntToJSONUntaggedValue = genericToJSON optsUntaggedValue
337+
338+gEitherTextIntToEncodingUntaggedValue :: EitherTextInt -> Encoding
339+gEitherTextIntToEncodingUntaggedValue = genericToEncoding optsUntaggedValue
340+
341+gEitherTextIntParseJSONUntaggedValue :: Value -> Parser EitherTextInt
342+gEitherTextIntParseJSONUntaggedValue = genericParseJSON optsUntaggedValue
343+
170--------------------------------------------------------------------------------
344--------------------------------------------------------------------------------
171-- Approx encoders/decoders
345-- Approx encoders/decoders
172--------------------------------------------------------------------------------
346--------------------------------------------------------------------------------
@@ -231,3 +405,54 @@ thGADTToEncodingDefault = $(mkToEncoding defaultOptions ''GADT)
231
405
232thGADTParseJSONDefault :: Value -> Parser (GADT String)
406thGADTParseJSONDefault :: Value -> Parser (GADT String)
233thGADTParseJSONDefault = $(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+9−1
@@ -1,4 +1,12 @@
1−module Functions where
1+{-# LANGUAGE NoImplicitPrelude #-}
2+
3+module Functions
4+ (
5+ approxEq
6+ , approxEqWith
7+ ) where
8+
9+import Prelude.Compat
2
10
3approxEq :: (Fractional a, Ord a) => a -> a -> Bool
11approxEq :: (Fractional a, Ord a) => a -> a -> Bool
4approxEq = approxEqWith 1e-15 1e-15
12approxEq = approxEqWith 1e-15 1e-15
tests/Instances.hs+44−50
@@ -1,39 +1,35 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE RecordWildCards #-}
5{-# LANGUAGE RecordWildCards #-}
5−{-# LANGUAGE StandaloneDeriving #-}
6+
6{-# OPTIONS_GHC -fno-warn-orphans #-}
7{-# OPTIONS_GHC -fno-warn-orphans #-}
7
8
8−module Instances where
9+module Instances () where
9
10
10−import Types
11+import Prelude.Compat
11−import Data.Function (on)
12+
13+import Control.Applicative (empty)
12import Control.Monad
14import Control.Monad
13−import Test.QuickCheck (Arbitrary(..), getNonNegative, elements,
14− listOf1, oneof, resize)
15−import Data.Time.Clock (UTCTime(..))
16−import Data.Time (ZonedTime(..), TimeZone(..))
17−import Data.List.NonEmpty (NonEmpty(..))
18−import Data.Version
19import Data.Aeson.Types
15import Data.Aeson.Types
20−import Control.Applicative (Const(..))
16+import Data.Function (on)
21−import Data.Tagged (Tagged(..))
17+import Data.Time (ZonedTime(..), TimeZone(..))
22−import Data.Proxy (Proxy(..))
18+import Data.Time.Clock (UTCTime(..))
23−import Control.Applicative
24import Functions
19import Functions
20+import Test.QuickCheck (Arbitrary(..), elements, oneof)
21+import Types
22+import qualified Data.DList as DList
23+import qualified Data.HashMap.Strict as HM
25
24
26import Data.Orphans ()
25import Data.Orphans ()
27import Test.QuickCheck.Instances ()
26import Test.QuickCheck.Instances ()
28−
27+import Data.Hashable.Time ()
29−#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
30−import Numeric.Natural
31−#endif
32
28
33-- "System" types.
29-- "System" types.
34
30
35instance Arbitrary DotNetTime where
31instance Arbitrary DotNetTime where
36− arbitrary = DotNetTime `liftM` arbitrary
32+ arbitrary = DotNetTime `fmap` arbitrary
37 shrink = map DotNetTime . shrink . fromDotNetTime
33 shrink = map DotNetTime . shrink . fromDotNetTime
38
34
39-- | Compare timezone part only on 'timeZoneMinutes'
35-- | Compare timezone part only on 'timeZoneMinutes'
@@ -51,8 +47,20 @@ instance ApproxEq UTCTime where
51instance ApproxEq DotNetTime where
47instance ApproxEq DotNetTime where
52 (=~) = (=~) `on` fromDotNetTime
48 (=~) = (=~) `on` fromDotNetTime
53
49
50+instance ApproxEq Float where
51+ a =~ b
52+ | isNaN a && isNaN b = True
53+ | otherwise = approxEq a b
54+
54instance ApproxEq Double where
55instance ApproxEq Double where
55− (=~) = approxEq
56+ a =~ b
57+ | isNaN a && isNaN b = True
58+ | otherwise = approxEq a b
59+
60+instance (ApproxEq k, Eq v) => ApproxEq (HM.HashMap k v) where
61+ a =~ b = and $ zipWith eq (HM.toList a) (HM.toList b)
62+ where
63+ eq (x,y) (u,v) = x =~ u && y == v
56
64
57-- Test-related types.
65-- Test-related types.
58
66
@@ -131,43 +139,29 @@ instance Arbitrary a => Arbitrary (SomeType a) where
131 , Unary <$> arbitrary
139 , Unary <$> arbitrary
132 , Product <$> arbitrary <*> arbitrary <*> arbitrary
140 , Product <$> arbitrary <*> arbitrary <*> arbitrary
133 , Record <$> arbitrary <*> arbitrary <*> arbitrary
141 , Record <$> arbitrary <*> arbitrary <*> arbitrary
142+ , List <$> arbitrary
134 ]
143 ]
135
144
145+instance Arbitrary EitherTextInt where
146+ arbitrary = oneof
147+ [ LeftBool <$> arbitrary
148+ , RightInt <$> arbitrary
149+ , BothTextInt <$> arbitrary <*> arbitrary
150+ , pure NoneNullary
151+ ]
152+
136instance Arbitrary (GADT String) where
153instance Arbitrary (GADT String) where
137 arbitrary = GADT <$> arbitrary
154 arbitrary = GADT <$> arbitrary
138
155
156+instance Arbitrary OptionField where
157+ arbitrary = OptionField <$> arbitrary
158+
159+
139instance ApproxEq Char where
160instance ApproxEq Char where
140 (=~) = (==)
161 (=~) = (==)
141
162
142instance (ApproxEq a) => ApproxEq [a] where
163instance (ApproxEq a) => ApproxEq [a] where
143 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
164 a =~ b = length a == length b && all (uncurry (=~)) (zip a b)
144
165
145−instance Arbitrary Version where
166+instance Arbitrary a => Arbitrary (DList.DList a) where
146− arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
167+ arbitrary = DList.fromList <$> arbitrary
147−
148−instance Arbitrary a => Arbitrary (NonEmpty a) where
149− arbitrary = (:|) <$> arbitrary <*> arbitrary
150−
151−-- Version tags are deprecated, so we avoid using them in the Arbitrary
152−-- instance. However, the recommended constructor 'makeVersion' is not
153−-- exported by "Data.Version" until base-4.8.0.0. For previous versions,
154−-- a definition is given below.
155−
156−#if !MIN_VERSION_base(4,8,0)
157−makeVersion :: [Int] -> Version
158−makeVersion b = Version b []
159−#endif
160−
161−#if !MIN_VERSION_base(4,8,0) && !MIN_VERSION_QuickCheck(2,8,3)
162−instance Arbitrary Natural where
163− arbitrary = fromInteger . abs <$> arbitrary
164−#endif
165−
166−instance Arbitrary (Proxy a) where
167− arbitrary = pure Proxy
168−
169−instance Arbitrary b => Arbitrary (Tagged a b) where
170− arbitrary = Tagged <$> arbitrary
171−
172−instance Arbitrary a => Arbitrary (Const a b) where
173− arbitrary = Const <$> 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

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONEncodable.swiftnew file+83

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONEncodingDetector.swiftnew file+110

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONLiteralConvertible.swiftnew file+134

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONParser.swiftnew file+918

This diff is too big to show here.

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

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONSerializing.swiftnew file+54

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/JSONSubscripting.swiftnew file+613

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_20161018/test_Freddy/main.swiftnew file+40

This diff is too big to show here.

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

This diff is too big to show here.

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

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy.xcodeproj/project.xcworkspace/xcshareddata/test_Freddy.xcscmblueprintnew file+30

This diff is too big to show here.

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

This diff is too big to show here.

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

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSON.swiftnew file+95

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONDecodable.swiftnew file+151

This diff is too big to show here.

tests/JSONTestSuite/parsers/test_Freddy_2_1_0/test_Freddy/JSONEncodable.swiftnew file+83

This diff is too big to show here.

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+33−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+19−266

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+93−4

This diff is too big to show here.

tests/UnitTests.hs+504−94

This diff is too big to show here.

tests/UnitTests/NullaryConstructors.hs+14−3

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.