on e8a7590 help sign in
28 files changed+259−401
since splitsnapshots
unifiedsplit
.gitignore−1 .travis.yml+12−15 CONTRIBUTING.md−38 Data/Aeson/Compat.hs+16 Data/Aeson/TH.hs+38−24 Data/Aeson/Types.hs−3 Data/Aeson/Types/FromJSON.hs+63−76 Data/Aeson/Types/Internal.hs+2−22 Data/Aeson/Types/ToJSON.hs+42−3 aeson.cabal+13−7 attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+20 attoparsec-iso8601/attoparsec-iso8601.cabal+1−1 benchmarks/aeson-benchmarks.cabal+1 cabal.project−1 changelog.md−13 stack-bench.yaml+8−2 stack-ffi-unescape.yaml+7−2 stack-nightly.yaml+4−2 tests/Encoders.hs+6−25 tests/ErrorMessages.hs+1−27 tests/Instances.hs+4−1 tests/Options.hs−11 tests/PropertyGeneric.hs+8 tests/PropertyKeys.hs+3 tests/SerializationFormatSpec.hs+4 tests/Types.hs+2 tests/UnitTests.hs+3−118 tests/golden/generic.expected+1−9
.gitignore−1
@@ -5,7 +5,6 @@ dist-newstyle
55cabal.sandbox.config
66.stack-work/
77.stack-work-bench/
8−*.yaml.lock
98
109*.o
1110*.hi
.travis.yml+12−15
@@ -24,46 +24,43 @@ before_cache:
2424
2525matrix:
2626 include:
27−
2827 - env: BUILD=hlint CABALVER=2.2 GHCVER=8.4.4
2928 compiler: ": #GHC 8.4.4"
3029 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]}}
30+ - env: BUILD=stack CABALVER=2.2 GHCVER=8.4.4 STACK_YAML=stack-bench.yaml
31+ compiler: ": #GHC 8.4.4"
32+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
33+ - env: BUILD=stack CABALVER=2.2 GHCVER=8.4.4 STACK_YAML=stack-ffi-unescape.yaml
34+ compiler: ": #GHC 8.4.4"
35+ addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
3936
4037 - env: BUILD=cabal2 CABALVER=3.0 GHCVER=8.8.1
4138 compiler: ": #GHC 8.8.1"
4239 addons: {apt: {packages: [cabal-install-3.0,ghc-8.8.1], sources: [hvr-ghc]}}
43−
4440 - env: BUILD=cabal CABALVER=2.4 GHCVER=8.6.5
4541 compiler: ": #GHC 8.6.5"
4642 addons: {apt: {packages: [cabal-install-2.4,ghc-8.6.5], sources: [hvr-ghc]}}
47−
4843 - env: BUILD=cabal CABALVER=2.2 GHCVER=8.4.4
4944 compiler: ": #GHC 8.4.4"
5045 addons: {apt: {packages: [cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}}
51−
5246 - env: BUILD=cabal CABALVER=2.0 GHCVER=8.2.2
5347 compiler: ": #GHC 8.2.2"
5448 addons: {apt: {packages: [cabal-install-2.0,ghc-8.2.2], sources: [hvr-ghc]}}
55−
5649 - env: BUILD=cabal CABALVER=2.0 GHCVER=8.0.2
5750 compiler: ": #GHC 8.0.2"
5851 addons: {apt: {packages: [cabal-install-2.0,ghc-8.0.2], sources: [hvr-ghc]}}
59−
6052 - env: BUILD=cabal CABALVER=1.22 GHCVER=7.10.3
6153 compiler: ": #GHC 7.10.3"
6254 addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
63−
6455 - env: BUILD=cabal CABALVER=1.18 GHCVER=7.8.4
6556 compiler: ": #GHC 7.8.4"
6657 addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
58+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.6.3
59+ compiler: ": #GHC 7.6.3"
60+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.6.3], sources: [hvr-ghc]}}
61+ - env: BUILD=cabal CABALVER=1.18 GHCVER=7.4.2
62+ compiler: ": #GHC 7.4.2"
63+ addons: {apt: {packages: [cabal-install-1.18,ghc-7.4.2], sources: [hvr-ghc]}}
6764
6865before_install:
6966 - unset CC
CONTRIBUTING.md−38
@@ -58,41 +58,3 @@ import Foo as F
5858import Qux (Q(..), q)
5959import qualified A
6060```
61−
62−## Development Workflow (If you plan on using Stack)
63−
64−Here we outline a general workflow new contributors could adopt for a nice incremental development experience.
65−
66−The root folder consists of two stack-*.yaml config files.
67−
68−1. stack-nightly.yaml - Config file defines all the settings to build and test any changes.
69−2. stack-bench.yaml - Used to run benchmark tests.
70−3. stack-ffi-unescape.yaml - This is for replacing certain parts with a C-implementation.
71−
72−Therefore, a ghci development experience would be:
73−
74−1. `stack ghci --stack-yaml stack-nightly.yaml --test`
75−2. :r to recompile
76−3. `main` to run all tests, or `:main --pattern Foo` to run specific tests matched by pattern.
77−
78−Some discussion on this topic can also be found here [here](https://github.com/bos/aeson/issues/733)
79−
80−Of course before submitting a PR, the following steps are recommended:
81−
82−1. 'stack test --stack-yaml stack-nightly.yaml` - Run the entire suite of tests
83−2. `make hlint` - Run hlint on the source folders.
84−3. 'stack test --stack-yaml stack-bench.yaml` - Run the benchmark tests if you believe your changes could affect the benchmarks.
85−
86−## Development Workflow (If you plan on using Cabal)
87−
88−A ghci development experience would be:
89−
90−1. `cabal new-repl test:tests`
91−2. `:r` to recompile
92−3. `Main.main` to run all tests, or `:m Main; :main --pattern Foo` to run specific tests matched by pattern.
93−
94−Of course before submitting a PR, the following steps are recommended:
95−
96−1. `cabal new-test` - Run the entire suite of tests
97−2. `make hlint` - Run hlint on the source folders.
98−3. Uncomment the `benchmarks` line in `cabal.project` - Run the benchmark tests if you believe your changes could affect the benchmarks.
Data/Aeson/Compat.hsnew file+16
@@ -0,0 +1,16 @@
1+{-# LANGUAGE CPP #-}
2+
3+module Data.Aeson.Compat
4+ (
5+ fromStrict
6+ ) where
7+
8+import qualified Data.ByteString as S
9+import qualified Data.ByteString.Lazy as L
10+
11+fromStrict :: S.ByteString -> L.ByteString
12+#if MIN_VERSION_bytestring(0, 9, 2)
13+fromStrict = L.fromChunks . (:[])
14+#else
15+fromStrict = L.fromStrict
16+#endif
Data/Aeson/TH.hs+38−24
@@ -135,15 +135,22 @@ import Data.Map (Map)
135135import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
136136import qualified Data.Monoid as Monoid
137137import Data.Set (Set)
138+#if MIN_VERSION_template_haskell(2,8,0)
138139import Language.Haskell.TH hiding (Arity)
140+#else
141+import Language.Haskell.TH
142+#endif
139143import Language.Haskell.TH.Datatype
144+#if MIN_VERSION_template_haskell(2,7,0) && !(MIN_VERSION_template_haskell(2,8,0))
145+import Language.Haskell.TH.Lib (starK)
146+#endif
140147#if MIN_VERSION_template_haskell(2,8,0) && !(MIN_VERSION_template_haskell(2,10,0))
141148import Language.Haskell.TH.Syntax (mkNameG_tc)
142149#endif
143150import Text.Printf (printf)
144151import qualified Data.Aeson.Encoding.Internal as E
145152import qualified Data.Foldable as F (all)
146−import qualified Data.HashMap.Strict as H (difference, fromList, keys, lookup, toList)
153+import qualified Data.HashMap.Strict as H (lookup, toList)
147154import qualified Data.List.NonEmpty as NE (length, reverse)
148155import qualified Data.Map as M (fromList, keys, lookup , singleton, size)
149156import qualified Data.Semigroup as Semigroup (Option(..))
@@ -922,32 +929,12 @@ parseRecord :: JSONClass
922929 -> Name
923930 -> [Name]
924931 -> Name
925− -> Bool
926932 -> ExpQ
927−parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject =
928− (if rejectUnknownFields opts
929− then infixApp checkUnknownRecords [|(>>)|]
930− else id) $
933+parseRecord jc tvMap argTys opts tName conName fields obj =
931934 foldl' (\a b -> infixApp a [|(<*>)|] b)
932935 (infixApp (conE conName) [|(<$>)|] x)
933936 xs
934937 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− ]
951938 x:xs = [ [|lookupField|]
952939 `appE` dispatchParseJSON jc conName tvMap argTy
953940 `appE` litE (stringL $ show tName)
@@ -1022,7 +1009,7 @@ parseArgs jc tvMap tName opts
10221009 , constructorFields = argTys }
10231010 (Left (_, obj)) = do
10241011 argTys' <- mapM resolveTypeSynonyms argTys
1025− parseRecord jc tvMap argTys' opts tName conName fields obj True
1012+ parseRecord jc tvMap argTys' opts tName conName fields obj
10261013parseArgs jc tvMap tName opts
10271014 info@ConstructorInfo { constructorName = conName
10281015 , constructorVariant = RecordConstructor fields
@@ -1037,7 +1024,7 @@ parseArgs jc tvMap tName opts
10371024 argTys' <- mapM resolveTypeSynonyms argTys
10381025 caseE (varE valName)
10391026 [ match (conP 'Object [varP obj]) (normalB $
1040− parseRecord jc tvMap argTys' opts tName conName fields obj False) []
1027+ parseRecord jc tvMap argTys' opts tName conName fields obj) []
10411028 , matchFailed tName conName "Object"
10421029 ]
10431030
@@ -1603,13 +1590,21 @@ type TyVarMap = Map Name (Name, Name)
16031590-- | Returns True if a Type has kind *.
16041591hasKindStar :: Type -> Bool
16051592hasKindStar VarT{} = True
1593+#if MIN_VERSION_template_haskell(2,8,0)
16061594hasKindStar (SigT _ StarT) = True
1595+#else
1596+hasKindStar (SigT _ StarK) = True
1597+#endif
16071598hasKindStar _ = False
16081599
16091600-- Returns True is a kind is equal to *, or if it is a kind variable.
16101601isStarOrVar :: Kind -> Bool
1602+#if MIN_VERSION_template_haskell(2,8,0)
16111603isStarOrVar StarT = True
16121604isStarOrVar VarT{} = True
1605+#else
1606+isStarOrVar StarK = True
1607+#endif
16131608isStarOrVar _ = False
16141609
16151610-- Generate a list of fresh names with a common prefix, and numbered suffixes.
@@ -1667,7 +1662,9 @@ isTyFamily (ConT n) = do
16671662#else
16681663 FamilyI (FamilyD TypeFam _ _ _) _ -> True
16691664#endif
1665+#if MIN_VERSION_template_haskell(2,9,0)
16701666 FamilyI ClosedTypeFamilyD{} _ -> True
1667+#endif
16711668 _ -> False
16721669isTyFamily _ = return False
16731670
@@ -1695,7 +1692,9 @@ mentionsName = go
16951692 go :: Type -> [Name] -> Bool
16961693 go (AppT t1 t2) names = go t1 names || go t2 names
16971694 go (SigT t _k) names = go t names
1695+#if MIN_VERSION_template_haskell(2,8,0)
16981696 || go _k names
1697+#endif
16991698 go (VarT n) names = n `elem` names
17001699 go _ _ = False
17011700
@@ -1751,14 +1750,23 @@ uncurryTy t = ([], t :| [])
17511750
17521751-- | Like uncurryType, except on a kind level.
17531752uncurryKind :: Kind -> NonEmpty Kind
1753+#if MIN_VERSION_template_haskell(2,8,0)
17541754uncurryKind = snd . uncurryTy
1755+#else
1756+uncurryKind (ArrowK k1 k2) = k1 <| uncurryKind k2
1757+uncurryKind k = k :| []
1758+#endif
17551759
17561760createKindChain :: Int -> Kind
17571761createKindChain = go starK
17581762 where
17591763 go :: Kind -> Int -> Kind
17601764 go k 0 = k
1765+#if MIN_VERSION_template_haskell(2,8,0)
17611766 go k !n = go (AppT (AppT ArrowT StarT) k) (n - 1)
1767+#else
1768+ go k !n = go (ArrowK StarK k) (n - 1)
1769+#endif
17621770
17631771-- | Makes a string literal expression from a constructor's name.
17641772conNameExp :: Options -> ConstructorInfo -> Q Exp
@@ -1812,7 +1820,11 @@ canEtaReduce remaining dropped =
18121820-------------------------------------------------------------------------------
18131821
18141822applySubstitutionKind :: Map Name Kind -> Type -> Type
1823+#if MIN_VERSION_template_haskell(2,8,0)
18151824applySubstitutionKind = applySubstitution
1825+#else
1826+applySubstitutionKind _ t = t
1827+#endif
18161828
18171829substNameWithKind :: Name -> Kind -> Type -> Type
18181830substNameWithKind n k = applySubstitutionKind (M.singleton n k)
@@ -1976,7 +1988,9 @@ data StarKindStatus = NotKindStar
19761988canRealizeKindStar :: Type -> StarKindStatus
19771989canRealizeKindStar t = case t of
19781990 _ | hasKindStar t -> KindStar
1991+#if MIN_VERSION_template_haskell(2,8,0)
19791992 SigT _ (VarT k) -> IsKindVar k
1993+#endif
19801994 _ -> NotKindStar
19811995
19821996-- | Returns 'Just' the kind variable 'Name' of a 'StarKindStatus' if it exists.
Data/Aeson/Types.hs−3
@@ -124,7 +124,6 @@ module Data.Aeson.Types
124124 , sumEncoding
125125 , unwrapUnaryRecords
126126 , tagSingleConstructors
127− , rejectUnknownFields
128127
129128 -- ** Options utilities
130129 , SumEncoding(..)
@@ -142,8 +141,6 @@ module Data.Aeson.Types
142141 , (<?>)
143142 , JSONPath
144143 , JSONPathElement(..)
145− , formatPath
146− , formatRelativePath
147144 ) where
148145
149146import Prelude.Compat
Data/Aeson/Types/FromJSON.hs+63−76
@@ -7,14 +7,16 @@
77{-# LANGUAGE MultiParamTypeClasses #-}
88{-# LANGUAGE NoImplicitPrelude #-}
99{-# LANGUAGE OverloadedStrings #-}
10−{-# LANGUAGE PolyKinds #-}
1110{-# LANGUAGE RecordWildCards #-}
1211{-# LANGUAGE ScopedTypeVariables #-}
1312{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE TupleSections #-}
1513{-# LANGUAGE UndecidableInstances #-}
1614{-# LANGUAGE ViewPatterns #-}
1715
16+#if __GLASGOW_HASKELL__ >= 706
17+{-# LANGUAGE PolyKinds #-}
18+#endif
19+
1820#include "incoherent-compat.h"
1921#include "overlapping-compat.h"
2022
@@ -118,9 +120,9 @@ import GHC.Generics
118120import Numeric.Natural (Natural)
119121import Text.ParserCombinators.ReadP (readP_to_S)
120122import Unsafe.Coerce (unsafeCoerce)
123+import qualified Data.Aeson.Compat as Compat
121124import qualified Data.Aeson.Parser.Time as Time
122125import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123−import qualified Data.ByteString.Lazy as L
124126import qualified Data.DList as DList
125127import qualified Data.HashMap.Strict as H
126128import qualified Data.HashSet as HashSet
@@ -155,8 +157,18 @@ import qualified Data.Primitive.UnliftedArray as PM
155157import qualified Data.Primitive.PrimArray as PM
156158#endif
157159
160+#ifndef HAS_COERCIBLE
161+#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
162+#endif
158163
164+#if HAS_COERCIBLE
159165import Data.Coerce (Coercible, coerce)
166+coerce' :: Coercible a b => a -> b
167+coerce' = coerce
168+#else
169+coerce' :: a -> b
170+coerce' = unsafeCoerce
171+#endif
160172
161173parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
162174parseIndexedJSON p idx value = p value <?> Index idx
@@ -428,7 +440,11 @@ class FromJSONKey a where
428440-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
429441-- can be inspected, but cannot be constructed.
430442data CoerceText a where
443+#if HAS_COERCIBLE
431444 CoerceText :: Coercible Text a => CoerceText a
445+#else
446+ CoerceText :: CoerceText a
447+#endif
432448
433449-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
434450-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
@@ -448,7 +464,7 @@ data FromJSONKeyFunction a
448464
449465-- | Only law abiding up to interpretation
450466instance Functor FromJSONKeyFunction where
451− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
467+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
452468 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
453469 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
454470 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
@@ -462,7 +478,9 @@ instance Functor FromJSONKeyFunction where
462478--
463479-- On pre GHC 7.8 this is unconstrainted function.
464480fromJSONKeyCoerce ::
481+#if HAS_COERCIBLE
465482 Coercible Text a =>
483+#endif
466484 FromJSONKeyFunction a
467485fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
468486
@@ -470,18 +488,29 @@ fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
470488--
471489-- See note on 'fromJSONKeyCoerce'.
472490coerceFromJSONKeyFunction ::
491+#if HAS_COERCIBLE
473492 Coercible a b =>
493+#endif
474494 FromJSONKeyFunction a -> FromJSONKeyFunction b
495+#if HAS_COERCIBLE
475496coerceFromJSONKeyFunction = coerce
497+#else
498+coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
499+coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
500+coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
501+coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
502+#endif
476503
477504{-# RULES
478505 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
479506 fmap id x = x
480507 #-}
508+#if HAS_COERCIBLE
481509{-# RULES
482510 "FromJSONKeyCoerce: fmap coerce" forall x .
483511 fmap coerce x = coerceFromJSONKeyFunction x
484512 #-}
513+#endif
485514
486515-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
487516mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
@@ -768,13 +797,13 @@ withBoundedScientific' f v = withBoundedScientific_ id f v
768797-- | A variant of 'withBoundedScientific_' parameterized by a function to apply
769798-- to the 'Parser' in case of failure.
770799withBoundedScientific_ :: (Parser a -> Parser a) -> (Scientific -> Parser a) -> Value -> Parser a
771−withBoundedScientific_ whenFail f (Number scientific) =
772− if exp10 > 1024
800+withBoundedScientific_ whenFail f v@(Number scientific) =
801+ if exponent > 1024
773802 then whenFail (fail msg)
774803 else f scientific
775804 where
776− exp10 = base10Exponent scientific
777− msg = "found a number with exponent " ++ show exp10 ++ ", but it must not be greater than 1024"
805+ exponent = base10Exponent scientific
806+ msg = "found a number with exponent " ++ show exponent ++ ", but it must not be greater than 1024"
778807withBoundedScientific_ whenFail _ v =
779808 whenFail (typeMismatch "Number" v)
780809{-# INLINE withBoundedScientific_ #-}
@@ -794,7 +823,7 @@ withBool name _ v = prependContext name (typeMismatch "Boolean" v)
794823-- | Decode a nested JSON-encoded string.
795824withEmbeddedJSON :: String -> (Value -> Parser a) -> Value -> Parser a
796825withEmbeddedJSON _ innerParser (String txt) =
797− either fail innerParser $ eitherDecode (L.fromStrict $ T.encodeUtf8 txt)
826+ either fail innerParser $ eitherDecode (Compat.fromStrict $ T.encodeUtf8 txt)
798827 where
799828 eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON
800829 eitherFormatError = either (Left . uncurry formatError) Right
@@ -930,7 +959,7 @@ contextType = prependContext
930959contextTag :: Text -> [String] -> Parser a -> Parser a
931960contextTag tagKey cnames = prependFailure
932961 ("expected Object with key \"" ++ unpack tagKey ++ "\"" ++
933− " containing one of " ++ show cnames ++ ", ")
962+ " associated to one of " ++ show cnames ++ ", ")
934963
935964-- | Add the name of the constructor being parsed to a parser's error messages.
936965contextCons :: ConName -> TypeName -> Parser a -> Parser a
@@ -1201,9 +1230,9 @@ class FromTaggedObject' arity f isRecord where
12011230 :: String :* ConName :* TypeName :* Options :* FromArgs arity a
12021231 -> Object -> Tagged isRecord (Parser (f a))
12031232
1204−instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
1233+instance (RecordFromJSON arity f) => FromTaggedObject' arity f True where
12051234 -- Records are unpacked in the tagged object
1206− parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
1235+ parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON p
12071236
12081237instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
12091238 -- Nonnullary nonrecords are encoded in the contents field
@@ -1244,11 +1273,11 @@ instance OVERLAPPING_
12441273 ) => ConsFromJSON' arity (S1 s a) True where
12451274 consParseJSON' p@(cname :* tname :* opts :* fargs)
12461275 | unwrapUnaryRecords opts = Tagged . fmap M1 . gParseJSON opts fargs
1247− | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1276+ | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON p)
12481277
12491278instance RecordFromJSON arity f => ConsFromJSON' arity f True where
12501279 consParseJSON' p@(cname :* tname :* _) =
1251− Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1280+ Tagged . withObject (showCons cname tname) (recordParseJSON p)
12521281
12531282instance OVERLAPPING_
12541283 ConsFromJSON' arity U1 False where
@@ -1274,55 +1303,21 @@ instance (ProductFromJSON arity f, ProductSize f
12741303
12751304--------------------------------------------------------------------------------
12761305
1277−class FieldNames f where
1278− fieldNames :: f a -> [Text] -> [Text]
1279−
1280−instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where
1281− fieldNames _ =
1282− fieldNames (undefined :: a x) .
1283− fieldNames (undefined :: b y)
1284−
1285−instance (Selector s) => FieldNames (S1 s f) where
1286− fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)
1287−
12881306class RecordFromJSON arity f where
12891307 recordParseJSON
1290− :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
1291− -> Object -> Parser (f a)
1292−
1293−instance ( FieldNames f
1294− , RecordFromJSON' arity f
1295− ) => RecordFromJSON arity f where
1296− recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
1297− \obj -> checkUnknown obj >> recordParseJSON' p obj
1298− where
1299− knownFields :: H.HashMap Text ()
1300− knownFields = H.fromList $ map (,()) $
1301− fieldNames (undefined :: f a)
1302− [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]
1303− checkUnknown =
1304− if not (rejectUnknownFields opts)
1305− then \_ -> return ()
1306− else \obj -> case H.keys (H.difference obj knownFields) of
1307− [] -> return ()
1308− unknownFields -> contextCons cname tname $
1309− fail ("unknown fields: " ++ show unknownFields)
1310−
1311−class RecordFromJSON' arity f where
1312− recordParseJSON'
13131308 :: ConName :* TypeName :* Options :* FromArgs arity a
13141309 -> Object -> Parser (f a)
13151310
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
1311+instance ( RecordFromJSON arity a
1312+ , RecordFromJSON arity b
1313+ ) => RecordFromJSON arity (a :*: b) where
1314+ recordParseJSON p obj =
1315+ (:*:) <$> recordParseJSON p obj
1316+ <*> recordParseJSON p obj
13221317
13231318instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1324− RecordFromJSON' arity (S1 s a) where
1325− recordParseJSON' (cname :* tname :* opts :* fargs) obj = do
1319+ RecordFromJSON arity (S1 s a) where
1320+ recordParseJSON (cname :* tname :* opts :* fargs) obj = do
13261321 fv <- contextCons cname tname (obj .: label)
13271322 M1 <$> gParseJSON opts fargs fv <?> Key label
13281323 where
@@ -1330,16 +1325,16 @@ instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
13301325 sname = selName (undefined :: M1 _i s _f _p)
13311326
13321327instance INCOHERENT_ (Selector s, FromJSON a) =>
1333− RecordFromJSON' arity (S1 s (K1 i (Maybe a))) where
1334− recordParseJSON' (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1328+ RecordFromJSON arity (S1 s (K1 i (Maybe a))) where
1329+ recordParseJSON (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
13351330 where
13361331 label = fieldLabelModifier opts sname
13371332 sname = selName (undefined :: M1 _i s _f _p)
13381333
13391334-- Parse an Option like a Maybe.
13401335instance INCOHERENT_ (Selector s, FromJSON a) =>
1341− RecordFromJSON' arity (S1 s (K1 i (Semigroup.Option a))) where
1342− recordParseJSON' p obj = wrap <$> recordParseJSON' p obj
1336+ RecordFromJSON arity (S1 s (K1 i (Semigroup.Option a))) where
1337+ recordParseJSON p obj = wrap <$> recordParseJSON p obj
13431338 where
13441339 wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
13451340 wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
@@ -1576,22 +1571,12 @@ instance FromJSONKey Float where
15761571 _ -> Scientific.toRealFloat <$> parseScientificText t
15771572
15781573instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1579− parseJSON (Number x)
1580− | exp10 <= 1024
1581− , exp10 >= -1024 = return $! realToFrac x
1582− | otherwise = prependContext "Ratio" $ fail msg
1583− where
1584− exp10 = base10Exponent x
1585− msg = "found a number with exponent " ++ show exp10
1586− ++ ", but it must not be greater than 1024 or less than -1024"
1587− parseJSON o = objParser o
1588− where
1589− objParser = withObject "Rational" $ \obj -> do
1590− numerator <- obj .: "numerator"
1591− denominator <- obj .: "denominator"
1592− if denominator == 0
1593− then fail "Ratio denominator was 0"
1594− else pure $ numerator % denominator
1574+ parseJSON = withObject "Rational" $ \obj -> do
1575+ numerator <- obj .: "numerator"
1576+ denominator <- obj .: "denominator"
1577+ if denominator == 0
1578+ then fail "Ratio denominator was 0"
1579+ else pure $ numerator % denominator
15951580 {-# INLINE parseJSON #-}
15961581
15971582-- | This instance includes a bounds check to prevent maliciously
@@ -2006,6 +1991,7 @@ instance FromJSON DotNetTime where
20061991-- primitive
20071992-------------------------------------------------------------------------------
20081993
1994+#if MIN_VERSION_base(4,7,0)
20091995instance FromJSON a => FromJSON (PM.Array a) where
20101996 -- note: we could do better than this if vector exposed the data
20111997 -- constructor in Data.Vector.
@@ -2023,6 +2009,7 @@ instance (PM.PrimUnlifted a,FromJSON a) => FromJSON (PM.UnliftedArray a) where
20232009 parseJSON = fmap Exts.fromList . parseJSON
20242010#endif
20252011#endif
2012+#endif
20262013
20272014-------------------------------------------------------------------------------
20282015-- time
Data/Aeson/Types/Internal.hs+2−22
@@ -49,8 +49,6 @@ module Data.Aeson.Types.Internal
4949 , parserThrowError
5050 , parserCatchError
5151 , formatError
52− , formatPath
53− , formatRelativePath
5452 , (<?>)
5553 -- * Constructors and accessors
5654 , object
@@ -64,7 +62,6 @@ module Data.Aeson.Types.Internal
6462 , sumEncoding
6563 , unwrapUnaryRecords
6664 , tagSingleConstructors
67− , rejectUnknownFields
6865 )
6966
7067 , SumEncoding(..)
@@ -461,17 +458,7 @@ parseEither m v = runParser (m v) [] onError Right
461458-- | Annotate an error message with a
462459-- <http://goessner.net/articles/JsonPath/ JSONPath> error location.
463460formatError :: JSONPath -> String -> String
464−formatError path msg = "Error in " ++ formatPath path ++ ": " ++ msg
465−
466−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String',
467−-- representing the root object as @$@.
468−formatPath :: JSONPath -> String
469−formatPath path = "$" ++ formatRelativePath path
470−
471−-- | Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
472−-- which represents the path relative to some root object.
473−formatRelativePath :: JSONPath -> String
474−formatRelativePath path = format "" path
461+formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
475462 where
476463 format :: String -> JSONPath -> String
477464 format pfx [] = pfx
@@ -625,14 +612,10 @@ data Options = Options
625612 , tagSingleConstructors :: Bool
626613 -- ^ Encode types with a single constructor as sums,
627614 -- 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.
632615 }
633616
634617instance Show Options where
635− show (Options f c a o s u t r) =
618+ show (Options f c a o s u t) =
636619 "Options {"
637620 ++ intercalate ", "
638621 [ "fieldLabelModifier =~ " ++ show (f "exampleField")
@@ -642,7 +625,6 @@ instance Show Options where
642625 , "sumEncoding = " ++ show s
643626 , "unwrapUnaryRecords = " ++ show u
644627 , "tagSingleConstructors = " ++ show t
645− , "rejectUnknownFields = " ++ show r
646628 ]
647629 ++ "}"
648630
@@ -724,7 +706,6 @@ data JSONKeyOptions = JSONKeyOptions
724706-- , 'sumEncoding' = 'defaultTaggedObject'
725707-- , 'unwrapUnaryRecords' = False
726708-- , 'tagSingleConstructors' = False
727−-- , 'rejectUnknownFields' = False
728709-- }
729710-- @
730711defaultOptions :: Options
@@ -736,7 +717,6 @@ defaultOptions = Options
736717 , sumEncoding = defaultTaggedObject
737718 , unwrapUnaryRecords = False
738719 , tagSingleConstructors = False
739− , rejectUnknownFields = False
740720 }
741721
742722-- | Default 'TaggedObject' 'SumEncoding' options:
Data/Aeson/Types/ToJSON.hs+42−3
@@ -8,12 +8,15 @@
88{-# LANGUAGE NoImplicitPrelude #-}
99{-# LANGUAGE OverloadedStrings #-}
1010{-# LANGUAGE PatternGuards #-}
11−{-# LANGUAGE PolyKinds #-}
1211{-# LANGUAGE RecordWildCards #-}
1312{-# LANGUAGE ScopedTypeVariables #-}
1413{-# LANGUAGE TypeOperators #-}
1514{-# LANGUAGE UndecidableInstances #-}
1615
16+#if __GLASGOW_HASKELL__ >= 706
17+{-# LANGUAGE PolyKinds #-}
18+#endif
19+
1720#include "overlapping-compat.h"
1821#include "incoherent-compat.h"
1922
@@ -100,6 +103,7 @@ import GHC.Generics
100103import Numeric.Natural (Natural)
101104import qualified Data.Aeson.Encoding as E
102105import qualified Data.Aeson.Encoding.Internal as E (InArray, comma, econcat, retagEncoding)
106+import qualified Data.ByteString as S
103107import qualified Data.ByteString.Lazy as L
104108import qualified Data.DList as DList
105109import qualified Data.HashMap.Strict as H
@@ -140,6 +144,14 @@ import qualified Data.Primitive.UnliftedArray as PM
140144import qualified Data.Primitive.PrimArray as PM
141145#endif
142146
147+#if !(MIN_VERSION_bytestring(0,10,0))
148+import Foreign.ForeignPtr (withForeignPtr)
149+import Foreign.Marshal.Utils (copyBytes)
150+import Foreign.Ptr (plusPtr)
151+import qualified Data.ByteString.Internal as S
152+import qualified Data.ByteString.Lazy.Internal as L
153+#endif
154+
143155toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
144156toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
145157{-# INLINE toJSONPair #-}
@@ -503,7 +515,7 @@ toJSONKeyTextEnc e = ToJSONKeyText tot e
503515 -- TODO: dropAround is also used in stringEncoding, which is unfortunate atm
504516 tot = T.dropAround (== '"')
505517 . T.decodeLatin1
506− . L.toStrict
518+ . lazyToStrictByteString
507519 . E.encodingToLazyByteString
508520 . e
509521
@@ -1970,6 +1982,7 @@ formatMillis = take 3 . formatTime defaultTimeLocale "%q"
19701982-- primitive
19711983-------------------------------------------------------------------------------
19721984
1985+#if MIN_VERSION_base(4,7,0)
19731986instance ToJSON a => ToJSON (PM.Array a) where
19741987 -- note: we could do better than this if vector exposed the data
19751988 -- constructor in Data.Vector.
@@ -1991,6 +2004,7 @@ instance (PM.PrimUnlifted a,ToJSON a) => ToJSON (PM.UnliftedArray a) where
19912004 toEncoding = toEncoding . Exts.toList
19922005#endif
19932006#endif
2007+#endif
19942008
19952009-------------------------------------------------------------------------------
19962010-- time
@@ -2040,7 +2054,7 @@ stringEncoding :: Encoding' Text -> Value
20402054stringEncoding = String
20412055 . T.dropAround (== '"')
20422056 . T.decodeLatin1
2043− . L.toStrict
2057+ . lazyToStrictByteString
20442058 . E.encodingToLazyByteString
20452059{-# INLINE stringEncoding #-}
20462060
@@ -2847,6 +2861,31 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g,
28472861 toEncoding = toEncoding2
28482862 {-# INLINE toEncoding #-}
28492863
2864+-------------------------------------------------------------------------------
2865+-- pre-bytestring-0.10 compatibility
2866+-------------------------------------------------------------------------------
2867+
2868+{-# INLINE lazyToStrictByteString #-}
2869+lazyToStrictByteString :: L.ByteString -> S.ByteString
2870+#if MIN_VERSION_bytestring(0,10,0)
2871+lazyToStrictByteString = L.toStrict
2872+#else
2873+lazyToStrictByteString = packChunks
2874+
2875+-- packChunks is taken from the blaze-builder package.
2876+
2877+-- | Pack the chunks of a lazy bytestring into a single strict bytestring.
2878+packChunks :: L.ByteString -> S.ByteString
2879+packChunks lbs =
2880+ S.unsafeCreate (fromIntegral $ L.length lbs) (copyChunks lbs)
2881+ where
2882+ copyChunks L.Empty _pf = return ()
2883+ copyChunks (L.Chunk (S.PS fpbuf o l) lbs') pf = do
2884+ withForeignPtr fpbuf $ \pbuf ->
2885+ copyBytes pf (pbuf `plusPtr` o) l
2886+ copyChunks lbs' (pf `plusPtr` l)
2887+#endif
2888+
28502889--------------------------------------------------------------------------------
28512890
28522891-- | Wrap a list of pairs as an object.
aeson.cabal+13−7
@@ -1,5 +1,5 @@
11name: aeson
2−version: 1.4.6.0
2+version: 1.4.5.0
33license: BSD3
44license-file: LICENSE
55category: Text, Web, JSON
@@ -88,6 +88,7 @@ library
8888 Data.Aeson.Encode
8989
9090 other-modules:
91+ Data.Aeson.Compat
9192 Data.Aeson.Encoding.Builder
9293 Data.Aeson.Internal.Functions
9394 Data.Aeson.Parser.Unescape
@@ -102,12 +103,11 @@ library
102103
103104 -- GHC bundled libs
104105 build-depends:
105− base >= 4.7.0.0 && < 5,
106− bytestring >= 0.10.4.0 && < 0.11,
107− containers >= 0.5.5.1 && < 0.7,
106+ base >= 4.5.0.0 && < 5,
107+ containers >= 0.4.2.1 && < 0.7,
108108 deepseq >= 1.3.0.0 && < 1.5,
109109 ghc-prim >= 0.2 && < 0.6,
110− template-haskell >= 2.9.0.0 && < 2.16,
110+ template-haskell >= 2.7.0.0 && < 2.16,
111111 text >= 1.2.3.0 && < 1.3,
112112 time >= 1.4 && < 1.10
113113
@@ -119,6 +119,12 @@ library
119119 base-compat >= 0.9.1 && < 0.12,
120120 time-compat >= 1.9.2.2 && < 1.10
121121
122+ if flag(bytestring-builder)
123+ build-depends: bytestring >= 0.9.2.1 && < 0.10.4,
124+ bytestring-builder >= 0.10.4 && < 1
125+ else
126+ build-depends: bytestring >= 0.10.4 && < 0.11
127+
122128 if !impl(ghc >= 8.6)
123129 build-depends:
124130 contravariant >=1.4.1 && <1.6
@@ -219,9 +225,9 @@ test-suite tests
219225 containers,
220226 directory,
221227 dlist,
222− Diff >= 0.4 && < 0.5,
228+ Diff,
223229 filepath,
224− generic-deriving >= 1.10 && < 1.14,
230+ generic-deriving >= 1.10 && < 1.13,
225231 ghc-prim >= 0.2,
226232 hashable >= 1.2.4.0,
227233 scientific,
attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs+20
@@ -29,8 +29,16 @@ import Data.Time.Clock (diffTimeToPicoseconds)
2929
3030#endif
3131
32+#if MIN_VERSION_base(4,7,0)
33+
3234import Data.Fixed (Pico, Fixed(MkFixed))
3335
36+#else
37+
38+import Data.Fixed (Pico)
39+
40+#endif
41+
3442#if !MIN_VERSION_time(1,6,0)
3543
3644diffTimeToPicoseconds :: DiffTime -> Integer
@@ -38,12 +46,24 @@ diffTimeToPicoseconds = unsafeCoerce
3846
3947#endif
4048
49+#if MIN_VERSION_base(4,7,0)
50+
4151toPico :: Integer -> Pico
4252toPico = MkFixed
4353
4454fromPico :: Pico -> Integer
4555fromPico (MkFixed i) = i
4656
57+#else
58+
59+toPico :: Integer -> Pico
60+toPico = unsafeCoerce
61+
62+fromPico :: Pico -> Integer
63+fromPico = unsafeCoerce
64+
65+#endif
66+
4767-- | Like TimeOfDay, but using a fixed-width integer for seconds.
4868data TimeOfDay64 = TOD {-# UNPACK #-} !Int
4969 {-# UNPACK #-} !Int
attoparsec-iso8601/attoparsec-iso8601.cabal+1−1
@@ -36,7 +36,7 @@ library
3636 Data.Attoparsec.Time
3737 build-depends:
3838 attoparsec >= 0.13.0.1,
39− base >= 4.7 && < 5,
39+ base >= 4.5 && < 5,
4040 base-compat >= 0.9.1 && < 0.12,
4141 text >= 1.1.1.0,
4242 time >= 1.1.1.4
benchmarks/aeson-benchmarks.cabal+1
@@ -72,6 +72,7 @@ library
7272
7373 exposed-modules:
7474 Data.Aeson
75+ Data.Aeson.Compat
7576 Data.Aeson.Encoding
7677 Data.Aeson.Encoding.Builder
7778 Data.Aeson.Encoding.Internal
cabal.project−1
@@ -2,4 +2,3 @@ with-compiler: ghc
22packages: .
33packages: attoparsec-iso8601
44-- packages: benchmarks
5−tests: true
changelog.md−13
@@ -1,18 +1,5 @@
11For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md).
22
3−### upcoming
4−
5−* `FromJSON` instance of `Ratio a` now parses numbers in addtion to
6− standard `{numerator=..., denumerator=...}` encoding.
7−
8−### 1.4.6.0
9−
10−* Provide a clearer error message when a required tagKey for a constructor is missing, thanks to Guru Devanla.
11− The error message now looks like this: `Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found`
12−
13−* Add `formatPath` and `formatRelativePath` functions to turn a `JSONPath` into a `String`, thanks to Robbie McMichael
14−
15−
163### 1.4.5.0
174
185* Expose `(<?>)`, `JSONPath` and `JSONPathElement(..)` from `Data.Aeson.Types`. Previously only available through internal modules. Thanks to Luke Clifton.
stack-bench.yaml+8−2
@@ -1,4 +1,4 @@
1−resolver: nightly-2019-09-21
1+resolver: lts-12.26
22# We use aeson in the snapshot to
33# - avoid recompilation of criterion
44# - compare against it
@@ -9,4 +9,10 @@ resolver: nightly-2019-09-21
99work-dir: .stack-work-bench
1010packages:
1111- benchmarks
12−extra-deps: []
12+extra-deps:
13+- base-orphans-0.8.1
14+- hashable-time-0.2.0.1
15+- QuickCheck-2.13.1
16+- quickcheck-instances-0.3.21
17+- splitmix-0.0.2
18+- time-compat-1.9.2.2
stack-ffi-unescape.yaml+7−2
@@ -1,4 +1,4 @@
1−resolver: nightly-2019-09-21
1+resolver: lts-12.26
22packages:
33- '.'
44flags:
@@ -6,4 +6,9 @@ flags:
66 fast: true
77 cffi: true
88extra-deps:
9−- Diff-0.4.0
9+- base-orphans-0.8.1
10+- hashable-time-0.2.0.1
11+- QuickCheck-2.13.1
12+- quickcheck-instances-0.3.21
13+- splitmix-0.0.2
14+- time-compat-1.9.2.2
stack-nightly.yaml+4−2
@@ -1,4 +1,4 @@
1−resolver: nightly-2019-10-14
1+resolver: nightly-2019-05-13
22packages:
33- '.'
44- attoparsec-iso8601
@@ -8,4 +8,6 @@ flags:
88 attoparsec-iso8601:
99 fast: true
1010extra-deps:
11−- generic-deriving-1.12.4
11+- hlint-2.1.18
12+- time-compat-1.9.2.2
13+- quickcheck-instances-0.3.21
tests/Encoders.hs+6−25
@@ -188,6 +188,7 @@ gSomeTypeToEncoding2ElemArray = genericToEncoding opts2ElemArray
188188gSomeTypeParseJSON2ElemArray :: Value -> Parser (SomeType Int)
189189gSomeTypeParseJSON2ElemArray = genericParseJSON opts2ElemArray
190190
191+#if __GLASGOW_HASKELL__ >= 706
191192gSomeTypeLiftToEncoding2ElemArray :: LiftToEncoding SomeType a
192193gSomeTypeLiftToEncoding2ElemArray = genericLiftToEncoding opts2ElemArray
193194
@@ -196,6 +197,7 @@ gSomeTypeLiftToJSON2ElemArray = genericLiftToJSON opts2ElemArray
196197
197198gSomeTypeLiftParseJSON2ElemArray :: LiftParseJSON SomeType a
198199gSomeTypeLiftParseJSON2ElemArray = genericLiftParseJSON opts2ElemArray
200+#endif
199201
200202
201203gSomeTypeToJSONTaggedObject :: SomeType Int -> Value
@@ -207,6 +209,7 @@ gSomeTypeToEncodingTaggedObject = genericToEncoding optsTaggedObject
207209gSomeTypeParseJSONTaggedObject :: Value -> Parser (SomeType Int)
208210gSomeTypeParseJSONTaggedObject = genericParseJSON optsTaggedObject
209211
212+#if __GLASGOW_HASKELL__ >= 706
210213gSomeTypeLiftToEncodingTaggedObject :: LiftToEncoding SomeType a
211214gSomeTypeLiftToEncodingTaggedObject = genericLiftToEncoding optsTaggedObject
212215
@@ -215,6 +218,7 @@ gSomeTypeLiftToJSONTaggedObject = genericLiftToJSON optsTaggedObject
215218
216219gSomeTypeLiftParseJSONTaggedObject :: LiftParseJSON SomeType a
217220gSomeTypeLiftParseJSONTaggedObject = genericLiftParseJSON optsTaggedObject
221+#endif
218222
219223
220224gSomeTypeToJSONObjectWithSingleField :: SomeType Int -> Value
@@ -226,6 +230,7 @@ gSomeTypeToEncodingObjectWithSingleField = genericToEncoding optsObjectWithSingl
226230gSomeTypeParseJSONObjectWithSingleField :: Value -> Parser (SomeType Int)
227231gSomeTypeParseJSONObjectWithSingleField = genericParseJSON optsObjectWithSingleField
228232
233+#if __GLASGOW_HASKELL__ >= 706
229234gSomeTypeLiftToEncodingObjectWithSingleField :: LiftToEncoding SomeType a
230235gSomeTypeLiftToEncodingObjectWithSingleField = genericLiftToEncoding optsObjectWithSingleField
231236
@@ -234,6 +239,7 @@ gSomeTypeLiftToJSONObjectWithSingleField = genericLiftToJSON optsObjectWithSingl
234239
235240gSomeTypeLiftParseJSONObjectWithSingleField :: LiftParseJSON SomeType a
236241gSomeTypeLiftParseJSONObjectWithSingleField = genericLiftParseJSON optsObjectWithSingleField
242+#endif
237243
238244
239245gSomeTypeToJSONOmitNothingFields :: SomeType Int -> Value
@@ -243,31 +249,6 @@ gSomeTypeToEncodingOmitNothingFields :: SomeType Int -> Encoding
243249gSomeTypeToEncodingOmitNothingFields = genericToEncoding optsOmitNothingFields
244250
245251
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−
271252--------------------------------------------------------------------------------
272253-- Option fields
273254--------------------------------------------------------------------------------
tests/ErrorMessages.hs+1−27
@@ -13,7 +13,7 @@ import Data.Aeson (FromJSON(..), Value, json)
1313import Data.Aeson.Types (Parser)
1414import Data.Aeson.Parser (eitherDecodeWith)
1515import Data.Aeson.Internal (formatError, iparse)
16−import Data.Algorithm.Diff (PolyDiff (..), getGroupedDiff)
16+import Data.Algorithm.Diff (Diff(..), getGroupedDiff)
1717import Data.Proxy (Proxy(..))
1818import Data.Semigroup ((<>))
1919import Data.Sequence (Seq)
@@ -136,29 +136,6 @@ outputGeneric choice = concat
136136 , "[1,"
137137 ]
138138
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−
162139 , testWith "EitherTextInt"
163140 (select
164141 thEitherTextIntParseJSONUntaggedValue
@@ -219,6 +196,3 @@ testFor name _ = testWith name (parseJSON :: Value -> Parser a)
219196
220197testWithSomeType :: String -> (Value -> Parser (SomeType Int)) -> [L.ByteString] -> Output
221198testWithSomeType = testWith
222−
223−testWithFoo :: String -> (Value -> Parser Foo) -> [L.ByteString] -> Output
224−testWithFoo = testWith
tests/Instances.hs+4−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE NoImplicitPrelude #-}
@@ -24,12 +25,14 @@ import qualified Data.HashMap.Strict as HM
2425
2526import Data.Orphans ()
2627import Test.QuickCheck.Instances ()
28+#if MIN_VERSION_base(4,7,0)
2729import Data.Hashable.Time ()
30+#endif
2831
2932-- "System" types.
3033
3134instance Arbitrary DotNetTime where
32− arbitrary = DotNetTime `fmap` arbitrary
35+ arbitrary = DotNetTime `liftM` arbitrary
3336 shrink = map DotNetTime . shrink . fromDotNetTime
3437
3538-- | Compare timezone part only on 'timeZoneMinutes'
tests/Options.hs−11
@@ -55,14 +55,3 @@ optsOptionField = optsDefault
5555 { fieldLabelModifier = const "field"
5656 , omitNothingFields = True
5757 }
58−
59−optsRejectUnknownFields :: Options
60−optsRejectUnknownFields = optsDefault
61− { rejectUnknownFields = True
62− }
63−
64−optsRejectUnknownFieldsTagged :: Options
65−optsRejectUnknownFieldsTagged = optsDefault
66− { rejectUnknownFields = True
67− , tagSingleConstructors = True
68− }
tests/PropertyGeneric.hs+8
@@ -44,9 +44,11 @@ genericTests =
4444 , testProperty "TaggedObject" (toParseJSON gSomeTypeParseJSONTaggedObject gSomeTypeToJSONTaggedObject)
4545 , testProperty "ObjectWithSingleField" (toParseJSON gSomeTypeParseJSONObjectWithSingleField gSomeTypeToJSONObjectWithSingleField)
4646
47+#if __GLASGOW_HASKELL__ >= 706
4748 , testProperty "2ElemArray unary" (toParseJSON1 gSomeTypeLiftParseJSON2ElemArray gSomeTypeLiftToJSON2ElemArray)
4849 , testProperty "TaggedObject unary" (toParseJSON1 gSomeTypeLiftParseJSONTaggedObject gSomeTypeLiftToJSONTaggedObject)
4950 , testProperty "ObjectWithSingleField unary" (toParseJSON1 gSomeTypeLiftParseJSONObjectWithSingleField gSomeTypeLiftToJSONObjectWithSingleField)
51+#endif
5052 ]
5153 ]
5254 , testGroup "OneConstructor" [
@@ -83,24 +85,30 @@ genericTests =
8385
8486 , testProperty "SomeType2ElemArray" $
8587 gSomeTypeToJSON2ElemArray `sameAs` gSomeTypeToEncoding2ElemArray
88+#if __GLASGOW_HASKELL__ >= 706
8689 , testProperty "SomeType2ElemArray unary" $
8790 gSomeTypeLiftToJSON2ElemArray `sameAs1` gSomeTypeLiftToEncoding2ElemArray
8891 , testProperty "SomeType2ElemArray unary agree" $
8992 gSomeTypeToEncoding2ElemArray `sameAs1Agree` gSomeTypeLiftToEncoding2ElemArray
93+#endif
9094
9195 , testProperty "SomeTypeTaggedObject" $
9296 gSomeTypeToJSONTaggedObject `sameAs` gSomeTypeToEncodingTaggedObject
97+#if __GLASGOW_HASKELL__ >= 706
9398 , testProperty "SomeTypeTaggedObject unary" $
9499 gSomeTypeLiftToJSONTaggedObject `sameAs1` gSomeTypeLiftToEncodingTaggedObject
95100 , testProperty "SomeTypeTaggedObject unary agree" $
96101 gSomeTypeToEncodingTaggedObject `sameAs1Agree` gSomeTypeLiftToEncodingTaggedObject
102+#endif
97103
98104 , testProperty "SomeTypeObjectWithSingleField" $
99105 gSomeTypeToJSONObjectWithSingleField `sameAs` gSomeTypeToEncodingObjectWithSingleField
106+#if __GLASGOW_HASKELL__ >= 706
100107 , testProperty "SomeTypeObjectWithSingleField unary" $
101108 gSomeTypeLiftToJSONObjectWithSingleField `sameAs1` gSomeTypeLiftToEncodingObjectWithSingleField
102109 , testProperty "SomeTypeObjectWithSingleField unary agree" $
103110 gSomeTypeToEncodingObjectWithSingleField `sameAs1Agree` gSomeTypeLiftToEncodingObjectWithSingleField
111+#endif
104112
105113 , testProperty "SomeTypeOmitNothingFields" $
106114 gSomeTypeToJSONOmitNothingFields `sameAs` gSomeTypeToEncodingOmitNothingFields
tests/PropertyKeys.hs+3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE NoImplicitPrelude #-}
23
34module PropertyKeys ( keysTests ) where
@@ -30,10 +31,12 @@ keysTests =
3031 , testProperty "Natural" $ roundTripKey (undefined :: Natural)
3132 , testProperty "Float" $ roundTripKey (undefined :: Float)
3233 , testProperty "Double" $ roundTripKey (undefined :: Double)
34+#if MIN_VERSION_base(4,7,0)
3335 , testProperty "Day" $ roundTripKey (undefined :: Day)
3436 , testProperty "LocalTime" $ roundTripKey (undefined :: LocalTime)
3537 , testProperty "TimeOfDay" $ roundTripKey (undefined :: TimeOfDay)
3638 , testProperty "UTCTime" $ roundTripKey (undefined :: UTCTime)
39+#endif
3740 , testProperty "Version" $ roundTripKey (undefined :: Version)
3841 , testProperty "Lazy Text" $ roundTripKey (undefined :: LT.Text)
3942 , testProperty "UUID" $ roundTripKey UUID.nil
tests/SerializationFormatSpec.hs+4
@@ -3,7 +3,9 @@
33{-# LANGUAGE GADTs #-}
44{-# LANGUAGE NoImplicitPrelude #-}
55{-# LANGUAGE OverloadedStrings #-}
6+#if __GLASGOW_HASKELL__ >= 708
67{-# LANGUAGE DataKinds #-}
8+#endif
79
810------------------------------------------------------------------------------
911-- These tests assert that the JSON serialization doesn't change by accident.
@@ -75,8 +77,10 @@ jsonExamples =
7577 , example "Just" "1" (Just 1 :: Maybe Int)
7678 , example "Proxy Int" "null" (Proxy :: Proxy Int)
7779 , example "Tagged Char Int" "1" (Tagged 1 :: Tagged Char Int)
80+#if __GLASGOW_HASKELL__ >= 708
7881 -- Test Tagged instance is polykinded
7982 , example "Tagged 123 Int" "1" (Tagged 1 :: Tagged 123 Int)
83+#endif
8084 , example "Const Char Int" "\"c\"" (Const 'c' :: Const Char Int)
8185 , example "Tuple" "[1,2]" ((1, 2) :: (Int, Int))
8286 , example "NonEmpty" "[1,2,3]" (1 :| [2, 3] :: NonEmpty Int)
tests/Types.hs+2
@@ -119,7 +119,9 @@ deriving instance Generic (Sum4 a b c d)
119119deriving instance Generic (Approx a)
120120deriving instance Generic Nullary
121121deriving instance Generic (SomeType a)
122+#if __GLASGOW_HASKELL__ >= 706
122123deriving instance Generic1 SomeType
124+#endif
123125deriving instance Generic OptionField
124126deriving instance Generic EitherTextInt
125127
tests/UnitTests.hs+3−118
@@ -33,15 +33,14 @@ import Data.Aeson.Parser
3333 ( json, jsonLast, jsonAccum, jsonNoDup
3434 , json', jsonLast', jsonAccum', jsonNoDup')
3535import Data.Aeson.Types
36− ( Options(..), Result(Success, Error), ToJSON(..)
37− , Value(Array, Bool, Null, Number, Object, String), camelTo, camelTo2
38− , defaultOptions, formatPath, formatRelativePath, omitNothingFields, parse)
36+ ( Options(..), Result(Success), ToJSON(..), Value(Array, Bool, Null, Object)
37+ , camelTo, camelTo2, defaultOptions, omitNothingFields, parse)
3938import Data.Attoparsec.ByteString (Parser, parseOnly)
4039import Data.Char (toUpper)
4140import Data.Either.Compat (isLeft, isRight)
4241import Data.Hashable (hash)
4342import Data.HashMap.Strict (HashMap)
44−import Data.List (sort, isSuffixOf)
43+import Data.List (sort)
4544import Data.Maybe (fromMaybe)
4645import Data.Scientific (Scientific, scientific)
4746import Data.Tagged (Tagged(..))
@@ -112,12 +111,8 @@ tests = testGroup "unit" [
112111 , testGroup "SingleMaybeField" singleMaybeField
113112 , testCase "withEmbeddedJSON" withEmbeddedJSONTest
114113 , testCase "SingleFieldCon" singleFieldCon
115− , testGroup "UnknownFields" unknownFields
116114 , testGroup "Ordering of object keys" keyOrdering
117115 , testCase "Ratio with denominator 0" ratioDenominator0
118− , testCase "Rational parses number" rationalNumber
119− , testCase "Big rational" bigRationalDecoding
120− , testCase "Small rational" smallRationalDecoding
121116 , testCase "Big scientific exponent" bigScientificExponent
122117 , testCase "Big integer decoding" bigIntegerDecoding
123118 , testCase "Big natural decading" bigNaturalDecoding
@@ -258,18 +253,6 @@ formatErrorExample =
258253 lhs = "Error in $[0].foo.bar['a.b.c']['']['\\'\\\\'].end: error msg"
259254 in assertEqual "formatError example" lhs rhs
260255
261−formatPathExample :: Assertion
262−formatPathExample =
263− let rhs = formatPath [Key "x", Index 0]
264− lhs = "$.x[0]"
265− in assertEqual "formatPath example" lhs rhs
266−
267−formatRelativePathExample :: Assertion
268−formatRelativePathExample =
269− let rhs = formatPath [Key "x", Index 0]
270− lhs = ".x[0]"
271− in assertEqual "formatRelativePath example" lhs rhs
272−
273256------------------------------------------------------------------------------
274257-- Comparison (.:?) and (.:!)
275258------------------------------------------------------------------------------
@@ -540,7 +523,6 @@ showOptions =
540523 ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}"
541524 ++ ", unwrapUnaryRecords = False"
542525 ++ ", tagSingleConstructors = False"
543− ++ ", rejectUnknownFields = False"
544526 ++ "}")
545527 (show defaultOptions)
546528
@@ -585,79 +567,6 @@ singleFieldCon :: Assertion
585567singleFieldCon =
586568 assertEqual "fromJSON" (Right (SingleFieldCon 0)) (eitherDecode "0")
587569
588−newtype UnknownFields = UnknownFields { knownField :: Int }
589− deriving (Eq, Show, Generic)
590−newtype UnknownFieldsTag = UnknownFieldsTag { tag :: Int }
591− deriving (Eq, Show, Generic)
592−newtype UnknownFieldsUnaryTagged = UnknownFieldsUnaryTagged { knownFieldUnaryTagged :: Int }
593− deriving (Eq, Show, Generic)
594−data UnknownFieldsSum
595− = UnknownFields1 { knownField1 :: Int }
596− | UnknownFields2 { knownField2 :: Int }
597− deriving (Eq, Show, Generic)
598−
599−unknownFields :: [TestTree]
600−unknownFields = concat
601− [ testsUnary
602− "unary-unknown"
603− (object [("knownField", Number 1), ("unknownField", Number 1)])
604− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFields)
605− , testsUnary
606− "unary-unknown-tag"
607− (object [("knownField", Number 1), ("tag", String "UnknownFields")])
608− (Error "nknown fields: [\"tag\"]" :: Result UnknownFields)
609− , testsUnaryTag
610− "unary-explicit-tag"
611− (object [("tag", Number 1)])
612− (Success $ UnknownFieldsTag 1)
613− , testsSum
614− "sum-tag"
615− (object [("knownField1", Number 1), ("tag", String "UnknownFields1")])
616− (Success $ UnknownFields1 1)
617− , testsSum
618− "sum-unknown-in-branch"
619− (object [("knownField1", Number 1), ("knownField2", Number 1), ("tag", String "UnknownFields1")])
620− (Error "nknown fields: [\"knownField2\"]" :: Result UnknownFieldsSum)
621− , testsSum
622− "sum-unknown"
623− (object [("knownField1", Number 1), ("unknownField", Number 1), ("tag", String "UnknownFields1")])
624− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsSum)
625− , testsTagged
626− "unary-tagged"
627− (object [("knownFieldUnaryTagged", Number 1), ("tag", String "UnknownFieldsUnaryTagged")])
628− (Success $ UnknownFieldsUnaryTagged 1)
629− , -- Just a case to verify that the tag isn't optional, this is likely already tested by other unit tests
630− testsTagged
631− "unary-tagged-notag"
632− (object [("knownFieldUnaryTagged", Number 1)])
633− (Error "key \"tag\" not found" :: Result UnknownFieldsUnaryTagged)
634− , testsTagged
635− "unary-tagged-unknown"
636− (object [ ("knownFieldUnaryTagged", Number 1), ("unknownField", Number 1)
637− , ("tag", String "UnknownFieldsUnaryTagged")])
638− (Error "nknown fields: [\"unknownField\"]" :: Result UnknownFieldsUnaryTagged)
639− ]
640− where
641− opts = defaultOptions{rejectUnknownFields=True}
642− taggedOpts = opts{tagSingleConstructors=True}
643− assertApprox :: (Show a, Eq a) => Result a -> Result a -> IO ()
644− assertApprox (Error expected) (Error actual) | expected `isSuffixOf` actual = return ()
645− assertApprox expected actual = assertEqual "fromJSON" expected actual
646− testsBase :: (Show a, Eq a) => (Value -> Result a) -> (Value -> Result a)
647− -> String -> Value -> Result a -> [TestTree]
648− testsBase th g name value expected =
649− [ testCase (name ++ "-th") $ assertApprox expected (th value)
650− , testCase (name ++ "-generic") $ assertApprox expected (g value)
651− ]
652− testsUnary :: String -> Value -> Result UnknownFields -> [TestTree]
653− testsUnary = testsBase fromJSON (parse (genericParseJSON opts))
654− testsUnaryTag :: String -> Value -> Result UnknownFieldsTag -> [TestTree]
655− testsUnaryTag = testsBase fromJSON (parse (genericParseJSON opts))
656− testsSum :: String -> Value -> Result UnknownFieldsSum -> [TestTree]
657− testsSum = testsBase fromJSON (parse (genericParseJSON opts))
658− testsTagged :: String -> Value -> Result UnknownFieldsUnaryTagged -> [TestTree]
659− testsTagged = testsBase fromJSON (parse (genericParseJSON taggedOpts))
660−
661570testParser :: (Eq a, Show a)
662571 => String -> Parser a -> S.ByteString -> Either String a -> TestTree
663572testParser name json_ s expected =
@@ -698,25 +607,6 @@ ratioDenominator0 =
698607 (Left "Error in $: Ratio denominator was 0")
699608 (eitherDecode "{ \"numerator\": 1, \"denominator\": 0 }" :: Either String Rational)
700609
701−rationalNumber :: Assertion
702−rationalNumber =
703− assertEqual "Ratio with denominator 0"
704− (Right 1.37)
705− (eitherDecode "1.37" :: Either String Rational)
706−
707−bigRationalDecoding :: Assertion
708−bigRationalDecoding =
709− assertEqual "Decoding an Integer with a large exponent should fail"
710− (Left "Error in $: parsing Ratio failed, found a number with exponent 2000, but it must not be greater than 1024 or less than -1024")
711− ((eitherDecode :: L.ByteString -> Either String Rational) "1e2000")
712−
713−smallRationalDecoding :: Assertion
714−smallRationalDecoding =
715− assertEqual "Decoding an Integer with a large exponent should fail"
716− (Left "Error in $: parsing Ratio failed, found a number with exponent -2000, but it must not be greater than 1024 or less than -1024")
717− ((eitherDecode :: L.ByteString -> Either String Rational) "1e-2000")
718−
719−
720610bigScientificExponent :: Assertion
721611bigScientificExponent =
722612 assertEqual "Encoding an integral scientific with a large exponent should normalize it"
@@ -753,8 +643,3 @@ deriveToJSON defaultOptions ''Foo
753643deriveToJSON1 defaultOptions ''Foo
754644
755645deriveJSON defaultOptions{omitNothingFields=True,unwrapUnaryRecords=True} ''SingleMaybeField
756−
757−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFields
758−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsTag
759−deriveJSON defaultOptions{tagSingleConstructors=True,rejectUnknownFields=True} ''UnknownFieldsUnaryTagged
760−deriveJSON defaultOptions{rejectUnknownFields=True} ''UnknownFieldsSum
tests/golden/generic.expected+1−9
@@ -10,7 +10,7 @@ Error in $: parsing Types.SomeType(Unary) failed, key "contents" not found
1010Error in $: parsing Types.SomeType(Record) failed, key "testone" not found
1111Error in $.testone: parsing Double failed, unexpected Boolean
1212Error in $.tag: parsing Types.SomeType failed, expected tag field to be one of ["nullary","unary","product","record","list"], but found tag "X"
13−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
13+Error in $: parsing Types.SomeType failed, expected Object with key "tag" associated to one of ["nullary","unary","product","record","list"], key "tag" not found
1414Error in $: parsing Types.SomeType failed, expected Object, but encountered Array
1515SomeType (single-field)
1616Error in $.unary: parsing Int failed, expected Number, but encountered Object
@@ -31,14 +31,6 @@ Error in $: parsing Types.SomeType failed, expected a 2-element Array, but encou
3131Error in $: parsing Types.SomeType failed, expected Array, but encountered Object
3232Error in $: not enough input. Expecting ',' or ']'
3333Error in $: not enough input. Expecting json list value
34−SomeType (reject unknown fields)
35−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testZero"]
36−Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found
37−Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testtwo","testone","testthree"]
38−Foo (reject unknown fields)
39−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["tag"]
40−Foo (reject unknown fields, tagged single)
41−Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["unknownField"]
4234EitherTextInt
4335Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected tag "nonenullary", but found tag "X"
4436Error in $: parsing Types.EitherTextInt(NoneNullary) failed, expected String, but encountered Array