aeson 1.4.7.1 → 1.5.0.0
raw patch · 26 files changed
+2025/−809 lines, 26 filesdep +assocdep ~QuickCheckdep ~attoparsecdep ~base-compat-batteriesPVP ok
version bump matches the API change (PVP)
Dependencies added: assoc
Dependency ranges changed: QuickCheck, attoparsec, base-compat-batteries, transformers-compat
API changes (from Hackage documentation)
- Data.Aeson: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson: [From1Args] :: (Value -> Parser a) -> (Value -> Parser [a]) -> FromArgs One a
- Data.Aeson: [NoFromArgs] :: FromArgs Zero a
- Data.Aeson: [NoToArgs] :: ToArgs res Zero a
- Data.Aeson: [To1Args] :: (a -> res) -> ([a] -> res) -> ToArgs res One a
- Data.Aeson: gParseJSON :: GFromJSON arity f => Options -> FromArgs arity a -> Value -> Parser (f a)
- Data.Aeson.Types: FromJSONKeyCoerce :: !CoerceText a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyText :: !Text -> a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyTextParser :: !Text -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: FromJSONKeyValue :: !Value -> Parser a -> FromJSONKeyFunction a
- Data.Aeson.Types: [From1Args] :: (Value -> Parser a) -> (Value -> Parser [a]) -> FromArgs One a
- Data.Aeson.Types: [NoFromArgs] :: FromArgs Zero a
- Data.Aeson.Types: [NoToArgs] :: ToArgs res Zero a
- Data.Aeson.Types: [To1Args] :: (a -> res) -> ([a] -> res) -> ToArgs res One a
- Data.Aeson.Types: gParseJSON :: GFromJSON arity f => Options -> FromArgs arity a -> Value -> Parser (f a)
+ Data.Aeson: [FromJSONKeyCoerce] :: Coercible Text a => FromJSONKeyFunction a
+ Data.Aeson: [FromJSONKeyTextParser] :: !Text -> Parser a -> FromJSONKeyFunction a
+ Data.Aeson: [FromJSONKeyText] :: !Text -> a -> FromJSONKeyFunction a
+ Data.Aeson: [FromJSONKeyValue] :: !Value -> Parser a -> FromJSONKeyFunction a
+ Data.Aeson: class GToJSON' enc arity f
+ Data.Aeson.Types: [FromJSONKeyCoerce] :: Coercible Text a => FromJSONKeyFunction a
+ Data.Aeson.Types: [FromJSONKeyTextParser] :: !Text -> Parser a -> FromJSONKeyFunction a
+ Data.Aeson.Types: [FromJSONKeyText] :: !Text -> a -> FromJSONKeyFunction a
+ Data.Aeson.Types: [FromJSONKeyValue] :: !Value -> Parser a -> FromJSONKeyFunction a
+ Data.Aeson.Types: class GToJSON' enc arity f
- Data.Aeson: genericLiftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f)) => Options -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
+ Data.Aeson: genericLiftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) => Options -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
- Data.Aeson: genericLiftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f)) => Options -> (a -> Value) -> ([a] -> Value) -> f a -> Value
+ Data.Aeson: genericLiftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) => Options -> (a -> Value) -> ([a] -> Value) -> f a -> Value
- Data.Aeson: genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a)) => Options -> a -> Encoding
+ Data.Aeson: genericToEncoding :: (Generic a, GToJSON' Encoding Zero (Rep a)) => Options -> a -> Encoding
- Data.Aeson: genericToJSON :: (Generic a, GToJSON Value Zero (Rep a)) => Options -> a -> Value
+ Data.Aeson: genericToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => Options -> a -> Value
- Data.Aeson: liftToEncoding :: (ToJSON1 f, Generic1 f, GToJSON Encoding One (Rep1 f)) => (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
+ Data.Aeson: liftToEncoding :: (ToJSON1 f, Generic1 f, GToJSON' Encoding One (Rep1 f)) => (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
- Data.Aeson: liftToJSON :: (ToJSON1 f, Generic1 f, GToJSON Value One (Rep1 f)) => (a -> Value) -> ([a] -> Value) -> f a -> Value
+ Data.Aeson: liftToJSON :: (ToJSON1 f, Generic1 f, GToJSON' Value One (Rep1 f)) => (a -> Value) -> ([a] -> Value) -> f a -> Value
- Data.Aeson: toJSON :: (ToJSON a, Generic a, GToJSON Value Zero (Rep a)) => a -> Value
+ Data.Aeson: toJSON :: (ToJSON a, Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
- Data.Aeson: type GToEncoding = GToJSON Encoding
+ Data.Aeson: type GToEncoding = GToJSON' Encoding
- Data.Aeson: type GToJSON = GToJSON Value
+ Data.Aeson: type GToJSON = GToJSON' Value
- Data.Aeson.Types: genericLiftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f)) => Options -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
+ Data.Aeson.Types: genericLiftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) => Options -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
- Data.Aeson.Types: genericLiftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f)) => Options -> (a -> Value) -> ([a] -> Value) -> f a -> Value
+ Data.Aeson.Types: genericLiftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) => Options -> (a -> Value) -> ([a] -> Value) -> f a -> Value
- Data.Aeson.Types: genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a)) => Options -> a -> Encoding
+ Data.Aeson.Types: genericToEncoding :: (Generic a, GToJSON' Encoding Zero (Rep a)) => Options -> a -> Encoding
- Data.Aeson.Types: genericToJSON :: (Generic a, GToJSON Value Zero (Rep a)) => Options -> a -> Value
+ Data.Aeson.Types: genericToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => Options -> a -> Value
- Data.Aeson.Types: liftToEncoding :: (ToJSON1 f, Generic1 f, GToJSON Encoding One (Rep1 f)) => (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
+ Data.Aeson.Types: liftToEncoding :: (ToJSON1 f, Generic1 f, GToJSON' Encoding One (Rep1 f)) => (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding
- Data.Aeson.Types: liftToJSON :: (ToJSON1 f, Generic1 f, GToJSON Value One (Rep1 f)) => (a -> Value) -> ([a] -> Value) -> f a -> Value
+ Data.Aeson.Types: liftToJSON :: (ToJSON1 f, Generic1 f, GToJSON' Value One (Rep1 f)) => (a -> Value) -> ([a] -> Value) -> f a -> Value
- Data.Aeson.Types: toJSON :: (ToJSON a, Generic a, GToJSON Value Zero (Rep a)) => a -> Value
+ Data.Aeson.Types: toJSON :: (ToJSON a, Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
- Data.Aeson.Types: type GToEncoding = GToJSON Encoding
+ Data.Aeson.Types: type GToEncoding = GToJSON' Encoding
- Data.Aeson.Types: type GToJSON = GToJSON Value
+ Data.Aeson.Types: type GToJSON = GToJSON' Value
Files
- Data/Aeson.hs +4/−3
- Data/Aeson/Parser/Internal.hs +8/−6
- Data/Aeson/TH.hs +1/−1
- Data/Aeson/Types.hs +4/−3
- Data/Aeson/Types/Class.hs +4/−4
- Data/Aeson/Types/FromJSON.hs +19/−32
- Data/Aeson/Types/ToJSON.hs +39/−39
- LICENSE +1/−1
- aeson.cabal +7/−9
- benchmarks/Suite.hs +74/−0
- benchmarks/aeson-benchmarks.cabal +255/−258
- benchmarks/json-data/github-issues.json +1540/−0
- changelog.md +11/−0
- examples/Generic.hs +0/−37
- examples/Simplest.hs +0/−41
- examples/TemplateHaskell.hs +0/−28
- examples/Twitter.hs +0/−80
- examples/Twitter/Generic.hs +0/−44
- examples/Twitter/Manual.hs +0/−146
- examples/Twitter/Options.hs +0/−10
- examples/Twitter/TH.hs +0/−25
- examples/aeson-examples.cabal +0/−32
- tests/ErrorMessages.hs +2/−1
- tests/UnitTests.hs +7/−8
- tests/golden/generic.expected +1/−1
- tests/golden/th.expected +48/−0
Data/Aeson.hs view
@@ -87,11 +87,12 @@ , toJSON2 , toEncoding2 -- ** Generic JSON classes and options- , GFromJSON(..)- , FromArgs(..)+ , GFromJSON+ , FromArgs , GToJSON , GToEncoding- , ToArgs(..)+ , GToJSON'+ , ToArgs , Zero , One , genericToJSON
Data/Aeson/Parser/Internal.hs view
@@ -55,7 +55,6 @@ import Data.Attoparsec.ByteString.Char8 (Parser, char, decimal, endOfInput, isDigit_w8, signed, string) import Data.Function (fix) import Data.Functor.Compat (($>))-import Data.Bits (testBit) import Data.Scientific (Scientific) import Data.Text (Text) import qualified Data.Text.Encoding as TE@@ -320,13 +319,16 @@ jstring_ :: Parser Text {-# INLINE jstring_ #-} jstring_ = do- s <- A.takeWhile (\w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && not (testBit w 7))+ -- not sure whether >= or bit hackery is faster+ -- perfectly, we shouldn't care, it's compiler job.+ s <- A.takeWhile (\w -> w /= DOUBLE_QUOTE && w /= BACKSLASH && w >= 0x20 && w < 0x80) let txt = TE.decodeUtf8 s- w <- A.peekWord8- case w of- Nothing -> fail "string without end"+ mw <- A.peekWord8+ case mw of+ Nothing -> fail "string without end" Just DOUBLE_QUOTE -> A.anyWord8 $> txt- _ -> jstringSlow s+ Just w | w < 0x20 -> fail "unescaped control character"+ _ -> jstringSlow s jstringSlow :: B.ByteString -> Parser Text {-# INLINE jstringSlow #-}
Data/Aeson/TH.hs view
@@ -939,7 +939,7 @@ if inTaggedObject then (tagFieldName (sumEncoding opts) :) else id knownFields = appE [|H.fromList|] $ listE $ map (\knownName -> tupE [appE [|T.pack|] $ litE $ stringL knownName, [|()|]]) $- tagFieldNameAppender $ map nameBase fields+ tagFieldNameAppender $ map (fieldLabel opts) fields checkUnknownRecords = caseE (appE [|H.keys|] $ infixApp (varE obj) [|H.difference|] knownFields) [ match (listP []) (normalB [|return ()|]) []
Data/Aeson/Types.hs view
@@ -73,11 +73,12 @@ , toEncoding2 -- ** Generic JSON classes- , GFromJSON(..)- , FromArgs(..)+ , GFromJSON+ , FromArgs , GToJSON , GToEncoding- , ToArgs(..)+ , GToJSON'+ , ToArgs , Zero , One , genericToJSON
Data/Aeson/Types/Class.hs view
@@ -39,6 +39,7 @@ , FromArgs(..) , GToJSON , GToEncoding+ , GToJSON' , ToArgs(..) , Zero , One@@ -100,10 +101,9 @@ import Data.Aeson.Types.FromJSON import Data.Aeson.Types.Generic (One, Zero)-import Data.Aeson.Types.ToJSON hiding (GToJSON)-import qualified Data.Aeson.Types.ToJSON as ToJSON+import Data.Aeson.Types.ToJSON import Data.Aeson.Types.Internal (Value) import Data.Aeson.Encoding (Encoding) -type GToJSON = ToJSON.GToJSON Value-type GToEncoding = ToJSON.GToJSON Encoding+type GToJSON = GToJSON' Value+type GToEncoding = GToJSON' Encoding
Data/Aeson/Types/FromJSON.hs view
@@ -419,38 +419,28 @@ default fromJSONKeyList :: FromJSON a => FromJSONKeyFunction [a] fromJSONKeyList = FromJSONKeyValue parseJSON --- | With GHC 7.8+ we carry around @'Coercible' 'Text' a@ dictionary,--- to give us an assurance that the program will not segfault.--- Unfortunately we cannot enforce that the 'Eq' instances or the--- 'Hashable' instances for 'Text' and @a@ agree.------ At the moment this type is intentionally not exported. 'FromJSONKeyFunction'--- can be inspected, but cannot be constructed.-data CoerceText a where- CoerceText :: Coercible Text a => CoerceText a- -- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the -- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey' -- instance. The other three data constructors for this type all correspond to -- 'ToJSONKeyText'. Strictly speaking, 'FromJSONKeyTextParser' is more powerful than -- 'FromJSONKeyText', which is in turn more powerful than 'FromJSONKeyCoerce'. -- For performance reasons, these exist as three options instead of one.-data FromJSONKeyFunction a- = FromJSONKeyCoerce !(CoerceText a)- -- ^ uses 'coerce' ('unsafeCoerce' in older GHCs)- | FromJSONKeyText !(Text -> a)+data FromJSONKeyFunction a where+ FromJSONKeyCoerce :: Coercible Text a => FromJSONKeyFunction a+ -- ^ uses 'coerce'+ FromJSONKeyText :: !(Text -> a) -> FromJSONKeyFunction a -- ^ conversion from 'Text' that always succeeds- | FromJSONKeyTextParser !(Text -> Parser a)+ FromJSONKeyTextParser :: !(Text -> Parser a) -> FromJSONKeyFunction a -- ^ conversion from 'Text' that may fail- | FromJSONKeyValue !(Value -> Parser a)+ FromJSONKeyValue :: !(Value -> Parser a) -> FromJSONKeyFunction a -- ^ conversion for non-textual keys -- | Only law abiding up to interpretation instance Functor FromJSONKeyFunction where- fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)- fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)- fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)- fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)+ fmap h FromJSONKeyCoerce = FromJSONKeyText (h . coerce)+ fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)+ fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)+ fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f) -- | Construct 'FromJSONKeyFunction' for types coercible from 'Text'. This -- conversion is still unsafe, as 'Hashable' and 'Eq' instances of @a@ should be@@ -463,7 +453,7 @@ fromJSONKeyCoerce :: Coercible Text a => FromJSONKeyFunction a-fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText+fromJSONKeyCoerce = FromJSONKeyCoerce -- | Semantically the same as @coerceFromJSONKeyFunction = fmap coerce = coerce@. --@@ -474,10 +464,6 @@ coerceFromJSONKeyFunction = coerce {-# RULES- "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).- fmap id x = x- #-}-{-# RULES "FromJSONKeyCoerce: fmap coerce" forall x . fmap coerce x = coerceFromJSONKeyFunction x #-}@@ -1274,7 +1260,7 @@ -------------------------------------------------------------------------------- class FieldNames f where- fieldNames :: f a -> [Text] -> [Text]+ fieldNames :: f a -> [String] -> [String] instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where fieldNames _ =@@ -1282,7 +1268,7 @@ fieldNames (undefined :: b y) instance (Selector s) => FieldNames (S1 s f) where- fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)+ fieldNames _ = (selName (undefined :: M1 _i s _f _p) :) class RecordFromJSON arity f where recordParseJSON@@ -1296,9 +1282,10 @@ \obj -> checkUnknown obj >> recordParseJSON' p obj where knownFields :: H.HashMap Text ()- knownFields = H.fromList $ map (,()) $- fieldNames (undefined :: f a)- [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]+ knownFields = H.fromList $ map ((,()) . pack) $+ [tagFieldName (sumEncoding opts) | fromTaggedSum] <>+ (fieldLabelModifier opts <$> fieldNames (undefined :: f a) [])+ checkUnknown = if not (rejectUnknownFields opts) then \_ -> return ()@@ -1887,7 +1874,7 @@ instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where liftParseJSON p _ = case fromJSONKey of- FromJSONKeyCoerce _ -> withObject "Map" $+ FromJSONKeyCoerce -> withObject "Map" $ fmap (H.foldrWithKey (M.insert . unsafeCoerce) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k) FromJSONKeyText f -> withObject "Map" $ fmap (H.foldrWithKey (M.insert . f) M.empty) . H.traverseWithKey (\k v -> p v <?> Key k)@@ -1967,7 +1954,7 @@ instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where liftParseJSON p _ = case fromJSONKey of- FromJSONKeyCoerce _ -> withObject "HashMap ~Text" $+ FromJSONKeyCoerce -> withObject "HashMap ~Text" $ uc . H.traverseWithKey (\k v -> p v <?> Key k) FromJSONKeyText f -> withObject "HashMap" $ fmap (mapKey f) . H.traverseWithKey (\k v -> p v <?> Key k)
Data/Aeson/Types/ToJSON.hs view
@@ -32,7 +32,7 @@ , toJSON2 , toEncoding2 -- * Generic JSON classes- , GToJSON(..)+ , GToJSON'(..) , ToArgs(..) , genericToJSON , genericToEncoding@@ -155,7 +155,7 @@ -- | Class of generic representation types that can be converted to -- JSON.-class GToJSON enc arity f where+class GToJSON' enc arity f where -- | This method (applied to 'defaultOptions') is used as the -- default generic implementation of 'toJSON' -- (with @enc ~ 'Value'@ and @arity ~ 'Zero'@)@@ -176,14 +176,14 @@ -- | A configurable generic JSON creator. This function applied to -- 'defaultOptions' is used as the default for 'toJSON' when the type -- is an instance of 'Generic'.-genericToJSON :: (Generic a, GToJSON Value Zero (Rep a))+genericToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => Options -> a -> Value genericToJSON opts = gToJSON opts NoToArgs . from -- | A configurable generic JSON creator. This function applied to -- 'defaultOptions' is used as the default for 'liftToJSON' when the type -- is an instance of 'Generic1'.-genericLiftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))+genericLiftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) => Options -> (a -> Value) -> ([a] -> Value) -> f a -> Value genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1@@ -191,14 +191,14 @@ -- | A configurable generic JSON encoder. This function applied to -- 'defaultOptions' is used as the default for 'toEncoding' when the type -- is an instance of 'Generic'.-genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a))+genericToEncoding :: (Generic a, GToJSON' Encoding Zero (Rep a)) => Options -> a -> Encoding genericToEncoding opts = gToJSON opts NoToArgs . from -- | A configurable generic JSON encoder. This function applied to -- 'defaultOptions' is used as the default for 'liftToEncoding' when the type -- is an instance of 'Generic1'.-genericLiftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))+genericLiftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) => Options -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1@@ -287,7 +287,7 @@ -- | Convert a Haskell value to a JSON-friendly intermediate type. toJSON :: a -> Value - default toJSON :: (Generic a, GToJSON Value Zero (Rep a)) => a -> Value+ default toJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value toJSON = genericToJSON defaultOptions -- | Encode a Haskell value as JSON.@@ -590,7 +590,7 @@ class ToJSON1 f where liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value - default liftToJSON :: (Generic1 f, GToJSON Value One (Rep1 f))+ default liftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) => (a -> Value) -> ([a] -> Value) -> f a -> Value liftToJSON = genericLiftToJSON defaultOptions @@ -599,7 +599,7 @@ liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding - default liftToEncoding :: (Generic1 f, GToJSON Encoding One (Rep1 f))+ default liftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) => (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding liftToEncoding = genericLiftToEncoding defaultOptions@@ -699,12 +699,12 @@ -- Generic toJSON / toEncoding ------------------------------------------------------------------------------- -instance OVERLAPPABLE_ (GToJSON enc arity a) => GToJSON enc arity (M1 i c a) where+instance OVERLAPPABLE_ (GToJSON' enc arity a) => GToJSON' enc arity (M1 i c a) where -- Meta-information, which is not handled elsewhere, is ignored: gToJSON opts targs = gToJSON opts targs . unM1 {-# INLINE gToJSON #-} -instance GToJSON enc One Par1 where+instance GToJSON' enc One Par1 where -- Direct occurrences of the last type parameter are encoded with the -- function passed in as an argument: gToJSON _opts (To1Args tj _) = tj . unPar1@@ -713,7 +713,7 @@ instance ( ConsToJSON enc arity a , AllNullary (C1 c a) allNullary , SumToJSON enc arity (C1 c a) allNullary- ) => GToJSON enc arity (D1 d (C1 c a)) where+ ) => GToJSON' enc arity (D1 d (C1 c a)) where -- The option 'tagSingleConstructors' determines whether to wrap -- a single-constructor type. gToJSON opts targs@@ -723,7 +723,7 @@ | otherwise = consToJSON opts targs . unM1 . unM1 {-# INLINE gToJSON #-} -instance (ConsToJSON enc arity a) => GToJSON enc arity (C1 c a) where+instance (ConsToJSON enc arity a) => GToJSON' enc arity (C1 c a) where -- Constructors need to be encoded differently depending on whether they're -- a record or not. This distinction is made by 'consToJSON': gToJSON opts targs = consToJSON opts targs . unM1@@ -731,7 +731,7 @@ instance ( AllNullary (a :+: b) allNullary , SumToJSON enc arity (a :+: b) allNullary- ) => GToJSON enc arity (a :+: b)+ ) => GToJSON' enc arity (a :+: b) where -- If all constructors of a sum datatype are nullary and the -- 'allNullaryToStringTag' option is set they are encoded to@@ -747,31 +747,31 @@ -- possible but makes error messages a bit harder to understand for missing -- instances. -instance GToJSON Value arity V1 where+instance GToJSON' Value arity V1 where -- Empty values do not exist, which makes the job of formatting them -- rather easy: gToJSON _ _ x = x `seq` error "case: V1" {-# INLINE gToJSON #-} -instance ToJSON a => GToJSON Value arity (K1 i a) where+instance ToJSON a => GToJSON' Value arity (K1 i a) where -- Constant values are encoded using their ToJSON instance: gToJSON _opts _ = toJSON . unK1 {-# INLINE gToJSON #-} -instance ToJSON1 f => GToJSON Value One (Rec1 f) where+instance ToJSON1 f => GToJSON' Value One (Rec1 f) where -- Recursive occurrences of the last type parameter are encoded using their -- ToJSON1 instance: gToJSON _opts (To1Args tj tjl) = liftToJSON tj tjl . unRec1 {-# INLINE gToJSON #-} -instance GToJSON Value arity U1 where+instance GToJSON' Value arity U1 where -- Empty constructors are encoded to an empty array: gToJSON _opts _ _ = emptyArray {-# INLINE gToJSON #-} instance ( WriteProduct arity a, WriteProduct arity b , ProductSize a, ProductSize b- ) => GToJSON Value arity (a :*: b)+ ) => GToJSON' Value arity (a :*: b) where -- Products are encoded to an array. Here we allocate a mutable vector of -- the same size as the product and write the product's elements to it using@@ -787,8 +787,8 @@ {-# INLINE gToJSON #-} instance ( ToJSON1 f- , GToJSON Value One g- ) => GToJSON Value One (f :.: g)+ , GToJSON' Value One g+ ) => GToJSON' Value One (f :.: g) where -- If an occurrence of the last type parameter is nested inside two -- composed types, it is encoded by using the outermost type's ToJSON1@@ -801,25 +801,25 @@ -------------------------------------------------------------------------------- -- Generic toEncoding -instance ToJSON a => GToJSON Encoding arity (K1 i a) where+instance ToJSON a => GToJSON' Encoding arity (K1 i a) where -- Constant values are encoded using their ToJSON instance: gToJSON _opts _ = toEncoding . unK1 {-# INLINE gToJSON #-} -instance ToJSON1 f => GToJSON Encoding One (Rec1 f) where+instance ToJSON1 f => GToJSON' Encoding One (Rec1 f) where -- Recursive occurrences of the last type parameter are encoded using their -- ToEncoding1 instance: gToJSON _opts (To1Args te tel) = liftToEncoding te tel . unRec1 {-# INLINE gToJSON #-} -instance GToJSON Encoding arity U1 where+instance GToJSON' Encoding arity U1 where -- Empty constructors are encoded to an empty array: gToJSON _opts _ _ = E.emptyArray_ {-# INLINE gToJSON #-} instance ( EncodeProduct arity a , EncodeProduct arity b- ) => GToJSON Encoding arity (a :*: b)+ ) => GToJSON' Encoding arity (a :*: b) where -- Products are encoded to an array. Here we allocate a mutable vector of -- the same size as the product and write the product's elements to it using@@ -828,8 +828,8 @@ {-# INLINE gToJSON #-} instance ( ToJSON1 f- , GToJSON Encoding One g- ) => GToJSON Encoding One (f :.: g)+ , GToJSON' Encoding One g+ ) => GToJSON' Encoding One (f :.: g) where -- If an occurrence of the last type parameter is nested inside two -- composed types, it is encoded by using the outermost type's ToJSON1@@ -939,7 +939,7 @@ taggedObject' :: Options -> ToArgs enc arity a -> String -> f a -> Tagged isRecord pairs -instance ( GToJSON enc arity f+instance ( GToJSON' enc arity f , KeyValuePair enc pairs ) => TaggedObject' enc pairs arity f False where@@ -994,7 +994,7 @@ -------------------------------------------------------------------------------- -instance ( GToJSON Value arity a+instance ( GToJSON' Value arity a , ConsToJSON Value arity a , Constructor c ) => SumToJSON' TwoElemArray Value arity (C1 c a) where@@ -1007,7 +1007,7 @@ -------------------------------------------------------------------------------- -instance ( GToJSON Encoding arity a+instance ( GToJSON' Encoding arity a , ConsToJSON Encoding arity a , Constructor c ) => SumToJSON' TwoElemArray Encoding arity (C1 c a)@@ -1039,7 +1039,7 @@ instance OVERLAPPING_ ( RecordToPairs enc pairs arity (S1 s f) , FromPairs enc pairs- , GToJSON enc arity f+ , GToJSON' enc arity f ) => ConsToJSON' enc arity (S1 s f) True where consToJSON' opts targs@@ -1054,7 +1054,7 @@ consToJSON' opts targs = Tagged . fromPairs . recordToPairs opts targs {-# INLINE consToJSON' #-} -instance GToJSON enc arity f => ConsToJSON' enc arity f False where+instance GToJSON' enc arity f => ConsToJSON' enc arity f False where consToJSON' opts targs = Tagged . gToJSON opts targs {-# INLINE consToJSON' #-} @@ -1080,7 +1080,7 @@ {-# INLINE recordToPairs #-} instance ( Selector s- , GToJSON enc arity a+ , GToJSON' enc arity a , KeyValuePair enc pairs ) => RecordToPairs enc pairs arity (S1 s a) where@@ -1089,7 +1089,7 @@ instance INCOHERENT_ ( Selector s- , GToJSON enc arity (K1 i (Maybe a))+ , GToJSON' enc arity (K1 i (Maybe a)) , KeyValuePair enc pairs , Monoid pairs ) => RecordToPairs enc pairs arity (S1 s (K1 i (Maybe a)))@@ -1101,7 +1101,7 @@ instance INCOHERENT_ ( Selector s- , GToJSON enc arity (K1 i (Maybe a))+ , GToJSON' enc arity (K1 i (Maybe a)) , KeyValuePair enc pairs , Monoid pairs ) => RecordToPairs enc pairs arity (S1 s (K1 i (Semigroup.Option a)))@@ -1113,7 +1113,7 @@ {-# INLINE recordToPairs #-} fieldToPair :: (Selector s- , GToJSON enc arity a+ , GToJSON' enc arity a , KeyValuePair enc pairs) => Options -> ToArgs enc arity p -> S1 s a p -> pairs@@ -1146,7 +1146,7 @@ ixR = ix + lenL {-# INLINE writeProduct #-} -instance OVERLAPPABLE_ (GToJSON Value arity a) => WriteProduct arity a where+instance OVERLAPPABLE_ (GToJSON' Value arity a) => WriteProduct arity a where writeProduct opts targs mv ix _ = VM.unsafeWrite mv ix . gToJSON opts targs {-# INLINE writeProduct #-}@@ -1169,13 +1169,13 @@ encodeProduct opts targs b {-# INLINE encodeProduct #-} -instance OVERLAPPABLE_ (GToJSON Encoding arity a) => EncodeProduct arity a where+instance OVERLAPPABLE_ (GToJSON' Encoding arity a) => EncodeProduct arity a where encodeProduct opts targs a = E.retagEncoding $ gToJSON opts targs a {-# INLINE encodeProduct #-} -------------------------------------------------------------------------------- -instance ( GToJSON enc arity a+instance ( GToJSON' enc arity a , ConsToJSON enc arity a , FromPairs enc pairs , KeyValuePair enc pairs
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011, MailRank, Inc.+Copyright (c) 2011, MailRank, Inc. 2014-2020 Aeson project contributors All rights reserved.
aeson.cabal view
@@ -1,5 +1,5 @@ name: aeson-version: 1.4.7.1+version: 1.5.0.0 license: BSD3 license-file: LICENSE category: Text, Web, JSON@@ -8,7 +8,7 @@ author: Bryan O'Sullivan <bos@serpentine.com> maintainer: Adam Bergmark <adam@bergmark.nl> stability: experimental-tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1+tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.3, GHC == 8.10.1 synopsis: Fast JSON parsing and encoding cabal-version: >= 1.10 homepage: https://github.com/bos/aeson@@ -35,9 +35,6 @@ benchmarks/json-data/*.json cbits/*.c changelog.md- examples/*.cabal- examples/*.hs- examples/Twitter/*.hs ffi/Data/Aeson/Parser/*.hs include/*.h tests/JSONTestSuite/test_parsing/*.json@@ -109,7 +106,7 @@ ghc-prim >= 0.2 && < 0.7, template-haskell >= 2.9.0.0 && < 2.17, text >= 1.2.3.0 && < 1.3,- time >= 1.4 && < 1.10+ time >= 1.4 && < 1.11 if impl(ghc >= 8.0) build-depends: bytestring >= 0.10.8.1@@ -152,7 +149,8 @@ scientific >= 0.3.6.2 && < 0.4, th-abstraction >= 0.2.8.0 && < 0.4, uuid-types >= 1.0.3 && < 1.1,- vector >= 0.12.0.1 && < 0.13+ vector >= 0.12.0.1 && < 0.13,+ assoc ghc-options: -Wall @@ -175,7 +173,7 @@ hs-source-dirs: ffi other-modules: Data.Aeson.Parser.UnescapeFFI -test-suite tests+test-suite aeson-tests default-language: Haskell2010 type: exitcode-stdio-1.0 hs-source-dirs: tests ffi pure@@ -208,7 +206,7 @@ UnitTests.NullaryConstructors build-depends:- QuickCheck >= 2.10.0.1 && < 2.14,+ QuickCheck >= 2.10.0.1 && < 2.15, aeson, integer-logarithms >= 1 && <1.1, attoparsec,
+ benchmarks/Suite.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Main (main) where++import Data.Aeson+import Prelude.Compat++import Control.DeepSeq (NFData)+import Criterion.Main (Benchmark, bench, bgroup, defaultMain,+ env, nf)+import Data.Maybe (fromMaybe)+import Data.Proxy (Proxy (..))+import Data.Vector (Vector)+import System.Environment (lookupEnv)+import System.FilePath ((</>))++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS++import qualified Twitter+import qualified Twitter.Manual ()++import qualified GitHub++-------------------------------------------------------------------------------+-- Decode bench+-------------------------------------------------------------------------------++decodeBench+ :: forall a. (FromJSON a, NFData a)+ => String -- ^ name+ -> FilePath -- ^ input file+ -> Proxy a -- ^ what type+ -> Benchmark+decodeBench name fp _ = bgroup name+ [ env (readL fp) $ \contents -> bench "lazy" $ nf decL contents+ , env (readS fp) $ \contents -> bench "strict" $ nf decS contents+ ]+ where+ decL :: LBS.ByteString -> Maybe a+ decL = decode++ decS :: BS.ByteString -> Maybe a+ decS = decodeStrict++-------------------------------------------------------------------------------+-- Helpers+-------------------------------------------------------------------------------++readS :: FilePath -> IO BS.ByteString+readS fp = do+ dataDir <- lookupEnv "AESON_BENCH_DATADIR"+ BS.readFile $ maybe id (</>) dataDir fp++readL :: FilePath -> IO LBS.ByteString+readL fp = do+ dataDir <- lookupEnv "AESON_BENCH_DATADIR"+ LBS.readFile $ fromMaybe "json-data" dataDir </> fp++-------------------------------------------------------------------------------+-- Main+-------------------------------------------------------------------------------++main :: IO ()+main = defaultMain+ [ bgroup "Examples"+ [ bgroup "decode"+ [ decodeBench "twitter100" "twitter100.json" (Proxy :: Proxy Twitter.Result)+ , decodeBench "jp100" "jp100.json" (Proxy :: Proxy Twitter.Result)+ , decodeBench "github-issues" "github-issues.json" (Proxy :: Proxy (Vector GitHub.Issue))+ ]+ ]+ ]
benchmarks/aeson-benchmarks.cabal view
@@ -1,204 +1,197 @@-name: aeson-benchmarks-version: 0-build-type: Simple-cabal-version: >=1.10--flag bytestring-builder- description: Depend on the bytestring-builder package for backwards compatibility.- default: False- manual: False--flag local-aeson- description: Build the local version of aeson, to avoid rebuilding aeson's- reverse dependencies for benchmarking (statistics, criterion).- default: True- manual: True+cabal-version: >=1.10+name: aeson-benchmarks+version: 0+build-type: Simple library default-language: Haskell2010-- if flag(bytestring-builder)- build-depends: bytestring >= 0.9 && < 0.10.4,- bytestring-builder >= 0.10.4 && < 1- else- build-depends: bytestring >= 0.10.4+ hs-source-dirs: .. ../ffi ../pure ../attoparsec-iso8601+ c-sources: ../cbits/unescape_string.c+ build-depends:+ attoparsec+ , base+ , base-compat-batteries+ , bytestring >=0.10.4+ , containers+ , deepseq+ , dlist+ , ghc-prim+ , hashable+ , mtl+ , primitive+ , scientific+ , syb+ , tagged+ , template-haskell+ , text+ , th-abstraction+ , time+ , time-compat+ , transformers+ , unordered-containers+ , uuid-types+ , vector - if flag(local-aeson)+ if !impl(ghc >=8.6)+ build-depends: contravariant - hs-source-dirs: .. ../ffi ../pure ../attoparsec-iso8601- c-sources: ../cbits/unescape_string.c+ if !impl(ghc >=8.0)+ -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9 build-depends:- attoparsec,- base,- base-compat,- containers,- deepseq,- dlist,- fail,- ghc-prim,- hashable,- mtl,- primitive,- scientific,- syb,- tagged,- template-haskell,- text,- th-abstraction,- time-compat,- time,- transformers,- unordered-containers,- uuid-types,- vector-- if !impl(ghc >= 8.6)- build-depends: contravariant-- if !impl(ghc >= 8.0)- -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9- build-depends: semigroups,- transformers-compat-- if !impl(ghc >= 7.10)- -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8- build-depends: nats,- void-- include-dirs: ../include+ fail+ , semigroups+ , transformers-compat - ghc-options: -O2 -Wall- cpp-options: -DGENERICS+ if !impl(ghc >=7.10)+ -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8+ build-depends:+ nats+ , void - exposed-modules:- Data.Aeson- Data.Aeson.Encoding- Data.Aeson.Encoding.Builder- Data.Aeson.Encoding.Internal- Data.Aeson.Internal- Data.Aeson.Internal.Functions- Data.Aeson.Internal.Time- Data.Aeson.Parser- Data.Aeson.Parser.Internal- Data.Aeson.Parser.Time- Data.Aeson.Parser.Unescape- Data.Aeson.Parser.UnescapeFFI- Data.Aeson.Parser.UnescapePure- Data.Aeson.TH- Data.Aeson.Text- Data.Aeson.Types- Data.Aeson.Types.Class- Data.Aeson.Types.FromJSON- Data.Aeson.Types.Generic- Data.Aeson.Types.Internal- Data.Aeson.Types.ToJSON- Data.Attoparsec.Time- Data.Attoparsec.Time.Internal+ include-dirs: ../include+ ghc-options: -O2 -Wall+ cpp-options: -DGENERICS+ exposed-modules:+ Data.Aeson+ Data.Aeson.Encoding+ Data.Aeson.Encoding.Builder+ Data.Aeson.Encoding.Internal+ Data.Aeson.Internal+ Data.Aeson.Internal.Functions+ Data.Aeson.Internal.Time+ Data.Aeson.Parser+ Data.Aeson.Parser.Internal+ Data.Aeson.Parser.Time+ Data.Aeson.Parser.Unescape+ Data.Aeson.Parser.UnescapeFFI+ Data.Aeson.Parser.UnescapePure+ Data.Aeson.Text+ Data.Aeson.TH+ Data.Aeson.Types+ Data.Aeson.Types.Class+ Data.Aeson.Types.FromJSON+ Data.Aeson.Types.Generic+ Data.Aeson.Types.Internal+ Data.Aeson.Types.ToJSON+ Data.Attoparsec.Time+ Data.Attoparsec.Time.Internal - else+-- Idea is to fold as many benchmarks into this as possible.+executable aeson-benchmark-suite+ default-language: Haskell2010+ main-is: Suite.hs+ hs-source-dirs: . ../examples/src+ ghc-options: -Wall -O2 -rtsopts+ build-depends:+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , criterion+ , deepseq+ , filepath+ , template-haskell+ , text+ , time+ , vector - build-depends: aeson+ other-modules:+ GitHub+ Twitter+ Twitter.Manual - reexported-modules:- Data.Aeson,- Data.Aeson.Encoding,- Data.Aeson.Parser.Internal,- Data.Aeson.Text,- Data.Aeson.TH,- Data.Aeson.Types+-- Benchmarks below are haven't been worked out yet executable aeson-benchmark-auto-compare default-language: Haskell2010- main-is: AutoCompare.hs- hs-source-dirs: .- ghc-options: -Wall -O2 -rtsopts+ main-is: AutoCompare.hs+ hs-source-dirs: .+ ghc-options: -Wall -O2 -rtsopts other-modules:- Auto.T.D- Auto.T.BigRecord- Auto.T.BigProduct- Auto.T.BigSum- Auto.G.D- Auto.G.BigRecord Auto.G.BigProduct+ Auto.G.BigRecord Auto.G.BigSum+ Auto.G.D+ Auto.T.BigProduct+ Auto.T.BigRecord+ Auto.T.BigSum+ Auto.T.D Options+ build-depends:- aeson-benchmarks,- base,- criterion,- deepseq,- template-haskell+ aeson-benchmarks+ , base+ , criterion+ , deepseq+ , template-haskell executable aeson-benchmark-escape default-language: Haskell2010- main-is: Escape.hs- hs-source-dirs: ../examples .- ghc-options: -Wall -O2 -rtsopts- if flag(local-aeson)- build-depends:- aeson-benchmarks,- base,- base-compat,- bytestring,- criterion >= 1.0,- deepseq,- ghc-prim,- text- else- -- Disabled because of inaccessible internals- buildable: False+ main-is: Escape.hs+ hs-source-dirs: ../examples/src .+ ghc-options: -Wall -O2 -rtsopts+ build-depends:+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , criterion >=1.0+ , deepseq+ , ghc-prim+ , text executable aeson-benchmark-compare default-language: Haskell2010- main-is: Compare.hs- hs-source-dirs: ../examples .- ghc-options: -Wall -O2 -rtsopts+ main-is: Compare.hs+ hs-source-dirs: ../examples/src .+ ghc-options: -Wall -O2 -rtsopts other-modules: Compare.BufferBuilder Compare.JsonBench Twitter Twitter.Manual Typed.Common+ build-depends:- aeson-benchmarks,- base,- base-compat,- buffer-builder,- bytestring,- criterion >= 1.0,- deepseq,- ghc-prim,- text+ aeson-benchmarks+ , base+ , base-compat-batteries+ , buffer-builder+ , bytestring+ , criterion >=1.0+ , deepseq+ , ghc-prim+ , text - if impl(ghc < 8.4)- other-modules:- Compare.JsonBuilder- build-depends:- json-builder- if impl(ghc < 8.0)- build-depends:- semigroups+ if impl(ghc <8.4)+ other-modules: Compare.JsonBuilder+ build-depends: json-builder + if !impl(ghc >=8.0)+ build-depends:+ fail+ , semigroups+ executable aeson-benchmark-micro default-language: Haskell2010- main-is: Micro.hs- hs-source-dirs: ../examples .- ghc-options: -Wall -O2 -rtsopts+ main-is: Micro.hs+ hs-source-dirs: ../examples/src .+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- base,- base-compat,- bytestring,- criterion >= 1.0,- deepseq,- ghc-prim,- text+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , criterion >=1.0+ , deepseq+ , ghc-prim+ , text executable aeson-benchmark-typed default-language: Haskell2010- main-is: Typed.hs- hs-source-dirs: ../examples .- ghc-options: -Wall -O2 -rtsopts+ main-is: Typed.hs+ hs-source-dirs: ../examples/src .+ ghc-options: -Wall -O2 -rtsopts other-modules: Twitter Twitter.Generic@@ -209,137 +202,141 @@ Typed.Generic Typed.Manual Typed.TH+ build-depends:- aeson-benchmarks,- base,- base-compat,- criterion >= 1.0,- deepseq,- ghc-prim,- text,- time+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , criterion >=1.0+ , deepseq+ , ghc-prim+ , text+ , time - if flag(bytestring-builder)- build-depends: bytestring >= 0.9 && < 0.10.4,- bytestring-builder >= 0.10.4 && < 1- else- build-depends: bytestring >= 0.10.4- if impl(ghc < 8.0)+ if impl(ghc <8.0) build-depends: semigroups executable aeson-benchmark-compare-with-json default-language: Haskell2010- main-is: CompareWithJSON.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: CompareWithJSON.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- base,- base-compat,- blaze-builder,- bytestring,- criterion,- deepseq,- json,- text+ aeson-benchmarks+ , base+ , base-compat-batteries+ , blaze-builder+ , bytestring+ , criterion+ , deepseq+ , json+ , text executable aeson-benchmark-aeson-encode default-language: Haskell2010- main-is: AesonEncode.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: AesonEncode.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- attoparsec,- base,- base-compat,- bytestring,- deepseq,- time+ aeson-benchmarks+ , attoparsec+ , base+ , base-compat-batteries+ , bytestring+ , deepseq+ , time executable aeson-benchmark-aeson-parse default-language: Haskell2010- main-is: AesonParse.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: AesonParse.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- attoparsec,- base,- base-compat,- bytestring,- time+ aeson-benchmarks+ , attoparsec+ , base+ , base-compat-batteries+ , bytestring+ , time + if !impl(ghc >=8.0)+ build-depends:+ fail+ , semigroups+ executable aeson-benchmark-json-parse default-language: Haskell2010- main-is: JsonParse.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: JsonParse.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- base,- base-compat,- deepseq,- json,- time+ base+ , base-compat-batteries+ , deepseq+ , json+ , time executable aeson-benchmark-dates default-language: Haskell2010- main-is: Dates.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: Dates.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- base,- base-compat,- bytestring,- criterion,- deepseq,- aeson-benchmarks,- text,- time- if impl(ghc >= 8.2)+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , criterion+ , deepseq+ , text+ , time++ if impl(ghc >=8.2) ghc-options: -Wno-simplifiable-class-constraints executable aeson-benchmark-map default-language: Haskell2010- main-is: AesonMap.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: AesonMap.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- base,- base-compat,- criterion >= 1.0,- bytestring,- containers,- deepseq,- hashable,- tagged,- text,- unordered-containers+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , containers+ , criterion >=1.0+ , deepseq+ , hashable+ , tagged+ , text+ , unordered-containers executable aeson-benchmark-foldable default-language: Haskell2010- main-is: AesonFoldable.hs- ghc-options: -Wall -O2 -rtsopts+ main-is: AesonFoldable.hs+ ghc-options: -Wall -O2 -rtsopts build-depends:- aeson-benchmarks,- base,- base-compat,- criterion >= 1.0,- bytestring,- containers,- deepseq,- hashable,- tagged,- text,- unordered-containers,- vector+ aeson-benchmarks+ , base+ , base-compat-batteries+ , bytestring+ , containers+ , criterion >=1.0+ , deepseq+ , hashable+ , tagged+ , text+ , unordered-containers+ , vector executable aeson-issue-673 default-language: Haskell2010- main-is: Issue673.hs- ghc-options: -Wall -O1 -rtsopts+ main-is: Issue673.hs+ ghc-options: -Wall -O1 -rtsopts build-depends:- aeson-benchmarks,- attoparsec,- base,- bytestring,- scientific,- base-compat,- criterion >= 1.0- if impl(ghc < 8.0)+ aeson-benchmarks+ , attoparsec+ , base+ , base-compat-batteries+ , bytestring+ , criterion >=1.0+ , scientific++ if impl(ghc <8.0) build-depends: semigroups
+ benchmarks/json-data/github-issues.json view
@@ -0,0 +1,1540 @@+[+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/772",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/772/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/772/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/772/events",+ "html_url": "https://github.com/bos/aeson/pull/772",+ "id": 593935498,+ "node_id": "MDExOlB1bGxSZXF1ZXN0Mzk4NjI4OTIx",+ "number": 772,+ "title": "WIP: Add cabal.bench.project for running benchmarks",+ "user": {+ "login": "phadej",+ "id": 51087,+ "node_id": "MDQ6VXNlcjUxMDg3",+ "avatar_url": "https://avatars2.githubusercontent.com/u/51087?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/phadej",+ "html_url": "https://github.com/phadej",+ "followers_url": "https://api.github.com/users/phadej/followers",+ "following_url": "https://api.github.com/users/phadej/following{/other_user}",+ "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",+ "organizations_url": "https://api.github.com/users/phadej/orgs",+ "repos_url": "https://api.github.com/users/phadej/repos",+ "events_url": "https://api.github.com/users/phadej/events{/privacy}",+ "received_events_url": "https://api.github.com/users/phadej/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2020-04-04T15:53:53Z",+ "updated_at": "2020-04-07T11:10:42Z",+ "closed_at": null,+ "author_association": "CONTRIBUTOR",+ "pull_request": {+ "url": "https://api.github.com/repos/bos/aeson/pulls/772",+ "html_url": "https://github.com/bos/aeson/pull/772",+ "diff_url": "https://github.com/bos/aeson/pull/772.diff",+ "patch_url": "https://github.com/bos/aeson/pull/772.patch"+ },+ "body": ""+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/768",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/768/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/768/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/768/events",+ "html_url": "https://github.com/bos/aeson/pull/768",+ "id": 593798540,+ "node_id": "MDExOlB1bGxSZXF1ZXN0Mzk4NTA2NDAy",+ "number": 768,+ "title": "Implement JSON parser using scanner",+ "user": {+ "login": "Yuras",+ "id": 692251,+ "node_id": "MDQ6VXNlcjY5MjI1MQ==",+ "avatar_url": "https://avatars0.githubusercontent.com/u/692251?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/Yuras",+ "html_url": "https://github.com/Yuras",+ "followers_url": "https://api.github.com/users/Yuras/followers",+ "following_url": "https://api.github.com/users/Yuras/following{/other_user}",+ "gists_url": "https://api.github.com/users/Yuras/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/Yuras/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/Yuras/subscriptions",+ "organizations_url": "https://api.github.com/users/Yuras/orgs",+ "repos_url": "https://api.github.com/users/Yuras/repos",+ "events_url": "https://api.github.com/users/Yuras/events{/privacy}",+ "received_events_url": "https://api.github.com/users/Yuras/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 4,+ "created_at": "2020-04-04T11:05:26Z",+ "updated_at": "2020-04-06T11:04:41Z",+ "closed_at": null,+ "author_association": "CONTRIBUTOR",+ "pull_request": {+ "url": "https://api.github.com/repos/bos/aeson/pulls/768",+ "html_url": "https://github.com/bos/aeson/pull/768",+ "diff_url": "https://github.com/bos/aeson/pull/768.diff",+ "patch_url": "https://github.com/bos/aeson/pull/768.patch"+ },+ "body": "This PR replaces `attoparsec` with non-backtracking parser `scanner`, which improves performance by 20%-40% (see the benchmark results below.) It's WIP yet, I'd like to hear people's opinion on that.\r\n\r\n# Issues\r\n\r\nThere are two main issues with this change:\r\n\r\n## Backward compatibility\r\n\r\nSince `aeson` exposes `attoparsec` parses from `Data.Aeson.Parser` module, replacing implementation will affect the API.\r\n\r\nThe possible solution is to convert parser based on `scanner` to one based on `attoparsec` using `toAtto` combinator from [here](https://github.com/Yuras/scanner-attoparsec/commit/f7fff690f2e565d94005c26320bcb742d4efc22b#diff-61024a57a5b895c8501edc2950c22cf7R30). It requires [this PR](https://github.com/bos/attoparsec/pull/165) to be merged though. Note that `toAtto` preserves the speedup, i.e. parser implemented using `scanner` and then converted to `attoparsec` is still faster then one written directly in `attoparsec`.\r\n\r\nAlternatively we can expose both implementations for some period of time.\r\n\r\n## Error messages\r\n\r\nThe error messages from the new parser lacks context. Here is a fragment of the output from the `ErrorMessages` test case:\r\n\r\n```\r\n --- Error in $: not enough input. Expecting ':'\r\n --- Error in $: not enough input. Expecting object value\r\n --- Error in $: not enough input. Expecting ',' or '}'\r\n +++ Error in $: No more input\r\n +++ Error in $: unexpected end of input\r\n +++ Error in $: No more input\r\n```\r\n\r\nThe reason is that `scanner` doesn't have a combinator similar to `<?>` from `attoparsec`. It's possible to add it, but it affects performance negatively.\r\n\r\nNote that the error messages from `attoparsec` doesn't contain the error location, it makes them less useful. With `scanner` with [can add error location](https://github.com/Yuras/scanner/pull/12) to the error messages, it will make errors more useful.\r\n\r\n# Benchmarks\r\n\r\nI used the bench-parse.py for benchmarking. Here are the results on my computer. Before:\r\n\r\n```\r\n json-data/twitter1.json :: 60000 times\r\n 0.626 seconds, 95806 parses/sec, 78.120 MB/sec\r\n json-data/twitter1.json :: 60000 times\r\n 0.599 seconds, 100216 parses/sec, 81.716 MB/sec\r\n json-data/twitter1.json :: 60000 times\r\n 0.614 seconds, 97689 parses/sec, 79.655 MB/sec\r\n0.8 KB: 100217 msg\\/sec (81.7 MB\\/sec)\r\n json-data/twitter10.json :: 13000 times\r\n 0.636 seconds, 20430 parses/sec, 128.477 MB/sec\r\n json-data/twitter10.json :: 13000 times\r\n 0.624 seconds, 20830 parses/sec, 130.995 MB/sec\r\n json-data/twitter10.json :: 13000 times\r\n 0.637 seconds, 20412 parses/sec, 128.364 MB/sec\r\n6.4 KB: 20831 msg\\/sec (131.0 MB\\/sec)\r\n json-data/twitter20.json :: 7500 times\r\n 0.763 seconds, 9823 parses/sec, 113.058 MB/sec\r\n json-data/twitter20.json :: 7500 times\r\n 0.742 seconds, 10105 parses/sec, 116.303 MB/sec\r\n json-data/twitter20.json :: 7500 times\r\n 0.742 seconds, 10103 parses/sec, 116.280 MB/sec\r\n11.8 KB: 10105 msg\\/sec (116.3 MB\\/sec)\r\n json-data/twitter50.json :: 2500 times\r\n 0.699 seconds, 3575 parses/sec, 108.937 MB/sec\r\n json-data/twitter50.json :: 2500 times\r\n 0.703 seconds, 3554 parses/sec, 108.313 MB/sec\r\n json-data/twitter50.json :: 2500 times\r\n 0.706 seconds, 3540 parses/sec, 107.879 MB/sec\r\n31.2 KB: 3575 msg\\/sec (108.9 MB\\/sec)\r\n json-data/twitter100.json :: 1000 times\r\n 0.662 seconds, 1509 parses/sec, 90.703 MB/sec\r\n json-data/twitter100.json :: 1000 times\r\n 0.679 seconds, 1473 parses/sec, 88.514 MB/sec\r\n json-data/twitter100.json :: 1000 times\r\n 0.653 seconds, 1530 parses/sec, 91.961 MB/sec\r\n61.5 KB: 1531 msg\\/sec (92.0 MB\\/sec)\r\n json-data/jp10.json :: 4000 times\r\n 0.592 seconds, 6756 parses/sec, 96.536 MB/sec\r\n json-data/jp10.json :: 4000 times\r\n 0.600 seconds, 6661 parses/sec, 95.192 MB/sec\r\n json-data/jp10.json :: 4000 times\r\n 0.597 seconds, 6702 parses/sec, 95.772 MB/sec\r\n14.6 KB: 6756 msg\\/sec (96.5 MB\\/sec)\r\n json-data/jp50.json :: 1200 times\r\n 0.644 seconds, 1862 parses/sec, 80.163 MB/sec\r\n json-data/jp50.json :: 1200 times\r\n 0.627 seconds, 1914 parses/sec, 82.409 MB/sec\r\n json-data/jp50.json :: 1200 times\r\n 0.646 seconds, 1858 parses/sec, 80.009 MB/sec\r\n44.1 KB: 1914 msg\\/sec (82.4 MB\\/sec)\r\n json-data/jp100.json :: 700 times\r\n 0.743 seconds, 941 parses/sec, 76.231 MB/sec\r\n json-data/jp100.json :: 700 times\r\n 0.745 seconds, 939 parses/sec, 76.063 MB/sec\r\n json-data/jp100.json :: 700 times\r\n 0.747 seconds, 937 parses/sec, 75.874 MB/sec\r\n82.9 KB: 942 msg\\/sec (76.2 MB\\/sec)\r\n```\r\n\r\nAfter:\r\n\r\n```\r\n json-data/twitter1.json :: 60000 times\r\n 0.488 seconds, 122874 parses/sec, 100.190 MB/sec\r\n json-data/twitter1.json :: 60000 times\r\n 0.480 seconds, 125054 parses/sec, 101.969 MB/sec\r\n json-data/twitter1.json :: 60000 times\r\n 0.478 seconds, 125445 parses/sec, 102.287 MB/sec\r\n0.8 KB: 125445 msg\\/sec (102.3 MB\\/sec)\r\n json-data/twitter10.json :: 13000 times\r\n 0.459 seconds, 28311 parses/sec, 178.035 MB/sec\r\n json-data/twitter10.json :: 13000 times\r\n 0.466 seconds, 27877 parses/sec, 175.307 MB/sec\r\n json-data/twitter10.json :: 13000 times\r\n 0.462 seconds, 28159 parses/sec, 177.084 MB/sec\r\n6.4 KB: 28311 msg\\/sec (178.0 MB\\/sec)\r\n json-data/twitter20.json :: 7500 times\r\n 0.533 seconds, 14079 parses/sec, 162.038 MB/sec\r\n json-data/twitter20.json :: 7500 times\r\n 0.527 seconds, 14239 parses/sec, 163.882 MB/sec\r\n json-data/twitter20.json :: 7500 times\r\n 0.526 seconds, 14248 parses/sec, 163.981 MB/sec\r\n11.8 KB: 14248 msg\\/sec (164.0 MB\\/sec)\r\n json-data/twitter50.json :: 2500 times\r\n 0.498 seconds, 5019 parses/sec, 152.932 MB/sec\r\n json-data/twitter50.json :: 2500 times\r\n 0.492 seconds, 5076 parses/sec, 154.690 MB/sec\r\n json-data/twitter50.json :: 2500 times\r\n 0.496 seconds, 5035 parses/sec, 153.441 MB/sec\r\n31.2 KB: 5077 msg\\/sec (154.7 MB\\/sec)\r\n json-data/twitter100.json :: 1000 times\r\n 0.464 seconds, 2153 parses/sec, 129.369 MB/sec\r\n json-data/twitter100.json :: 1000 times\r\n 0.463 seconds, 2158 parses/sec, 129.672 MB/sec\r\n json-data/twitter100.json :: 1000 times\r\n 0.463 seconds, 2158 parses/sec, 129.690 MB/sec\r\n61.5 KB: 2159 msg\\/sec (129.7 MB\\/sec)\r\n json-data/jp10.json :: 4000 times\r\n 0.512 seconds, 7811 parses/sec, 111.625 MB/sec\r\n json-data/jp10.json :: 4000 times\r\n 0.521 seconds, 7677 parses/sec, 109.707 MB/sec\r\n json-data/jp10.json :: 4000 times\r\n 0.509 seconds, 7855 parses/sec, 112.243 MB/sec\r\n14.6 KB: 7855 msg\\/sec (112.2 MB\\/sec)\r\n json-data/jp50.json :: 1200 times\r\n 0.519 seconds, 2313 parses/sec, 99.590 MB/sec\r\n json-data/jp50.json :: 1200 times\r\n 0.540 seconds, 2222 parses/sec, 95.687 MB/sec\r\n json-data/jp50.json :: 1200 times\r\n 0.522 seconds, 2299 parses/sec, 98.975 MB/sec\r\n44.1 KB: 2313 msg\\/sec (99.6 MB\\/sec)\r\n json-data/jp100.json :: 700 times\r\n 0.623 seconds, 1123 parses/sec, 90.915 MB/sec\r\n json-data/jp100.json :: 700 times\r\n 0.605 seconds, 1156 parses/sec, 93.613 MB/sec\r\n json-data/jp100.json :: 700 times\r\n 0.600 seconds, 1166 parses/sec, 94.408 MB/sec\r\n82.9 KB: 1166 msg\\/sec (94.4 MB\\/sec)\r\n```\r\n\r\n"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/766",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/766/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/766/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/766/events",+ "html_url": "https://github.com/bos/aeson/issues/766",+ "id": 593293818,+ "node_id": "MDU6SXNzdWU1OTMyOTM4MTg=",+ "number": 766,+ "title": "Cleanup benchmarks",+ "user": {+ "login": "phadej",+ "id": 51087,+ "node_id": "MDQ6VXNlcjUxMDg3",+ "avatar_url": "https://avatars2.githubusercontent.com/u/51087?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/phadej",+ "html_url": "https://github.com/phadej",+ "followers_url": "https://api.github.com/users/phadej/followers",+ "following_url": "https://api.github.com/users/phadej/following{/other_user}",+ "gists_url": "https://api.github.com/users/phadej/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/phadej/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/phadej/subscriptions",+ "organizations_url": "https://api.github.com/users/phadej/orgs",+ "repos_url": "https://api.github.com/users/phadej/repos",+ "events_url": "https://api.github.com/users/phadej/events{/privacy}",+ "received_events_url": "https://api.github.com/users/phadej/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 0,+ "created_at": "2020-04-03T10:49:30Z",+ "updated_at": "2020-04-03T10:49:30Z",+ "closed_at": null,+ "author_association": "CONTRIBUTOR",+ "body": "- Make `cabal.benchmarks.project` without `aeson` library. This would allow to not recompile `criterion` all the time.\r\n- remove old python scripts\r\n- rather make a script which would allow comparing benchmarks using https://hackage.haskell.org/package/criterion-compare\r\n - that script could run all benchmarks and collect their results, we have plenty of benchmarks for special cases\r\n- Add more examples than just Twitter. for example\r\n - GitHub issues listing\r\n - https://openweathermap.org/api\r\n - Some NPM package's `package.json` and or Cabal's `plan.json`"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/748",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/748/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/748/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/748/events",+ "html_url": "https://github.com/bos/aeson/issues/748",+ "id": 555921820,+ "node_id": "MDU6SXNzdWU1NTU5MjE4MjA=",+ "number": 748,+ "title": "how to parse json with field of optional and variant type in Haskell?",+ "user": {+ "login": "chakpongchung",+ "id": 8110904,+ "node_id": "MDQ6VXNlcjgxMTA5MDQ=",+ "avatar_url": "https://avatars0.githubusercontent.com/u/8110904?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/chakpongchung",+ "html_url": "https://github.com/chakpongchung",+ "followers_url": "https://api.github.com/users/chakpongchung/followers",+ "following_url": "https://api.github.com/users/chakpongchung/following{/other_user}",+ "gists_url": "https://api.github.com/users/chakpongchung/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/chakpongchung/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/chakpongchung/subscriptions",+ "organizations_url": "https://api.github.com/users/chakpongchung/orgs",+ "repos_url": "https://api.github.com/users/chakpongchung/repos",+ "events_url": "https://api.github.com/users/chakpongchung/events{/privacy}",+ "received_events_url": "https://api.github.com/users/chakpongchung/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 0,+ "created_at": "2020-01-28T00:52:43Z",+ "updated_at": "2020-01-28T00:52:43Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "https://stackoverflow.com/questions/59903779/how-to-parse-json-with-field-of-optional-and-variant-type-in-haskell\r\n\r\nI looked at the Twitter example in this repo. Is it a working example? If so, where can I find its corresponding json? Though I am not sure that twitter example cover the features needed to parse the full example(not the minimal) shown in the link above.\r\n\r\n"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/747",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/747/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/747/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/747/events",+ "html_url": "https://github.com/bos/aeson/issues/747",+ "id": 552518278,+ "node_id": "MDU6SXNzdWU1NTI1MTgyNzg=",+ "number": 747,+ "title": "Add a data type and `FromJSON` instance that retains the original JSON",+ "user": {+ "login": "TomMD",+ "id": 137806,+ "node_id": "MDQ6VXNlcjEzNzgwNg==",+ "avatar_url": "https://avatars2.githubusercontent.com/u/137806?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/TomMD",+ "html_url": "https://github.com/TomMD",+ "followers_url": "https://api.github.com/users/TomMD/followers",+ "following_url": "https://api.github.com/users/TomMD/following{/other_user}",+ "gists_url": "https://api.github.com/users/TomMD/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/TomMD/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/TomMD/subscriptions",+ "organizations_url": "https://api.github.com/users/TomMD/orgs",+ "repos_url": "https://api.github.com/users/TomMD/repos",+ "events_url": "https://api.github.com/users/TomMD/events{/privacy}",+ "received_events_url": "https://api.github.com/users/TomMD/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 2,+ "created_at": "2020-01-20T21:34:42Z",+ "updated_at": "2020-01-21T10:17:08Z",+ "closed_at": null,+ "author_association": "CONTRIBUTOR",+ "body": "With many APIs the Haskell types and the real world get out of sync. Updating the types is time consuming and often requires human interaction (because of separate packages, maintainers). For example, the GitHub API has lots of nooks that aren't reflected in the quite-rich (and appreciated) github package. For these cases I find it helpful to have access to the raw JSON both for debugging and for supporting functionality until the next release.\r\n\r\nTo restate, having a type decoded from an Aeson Value does not negate the utility of having the original Aeson Value around. This is a common situation and to support it I propose we add a type and `FromJSON` instance of:\r\n\r\n```\r\nmodule M (RetainedJSON, retainedJSON, decodedJSON) where\r\ndata RetainedJSON a = RetainedJSON\r\n { retainedJSON :: Value\r\n , decodedJSON :: a\r\n }\r\n\r\ninstance FromJSON a => FromJSON (RetainedJSON a) where\r\n parseJSON x = RetainedJSON x <$> parseJSON x\r\n```\r\n\r\nAnd either omit the `ToJSON` instance or use the admittedly-controversial instance of:\r\n\r\n```\r\ninstance ToJSON (RetainedJSON a) where\r\n toJSON = pure . retainedJSON\r\n```\r\n\r\nI'm checking for strong opinions against this proposal before I make a PR. Anyone have thoughts?"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/744",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/744/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/744/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/744/events",+ "html_url": "https://github.com/bos/aeson/issues/744",+ "id": 536970276,+ "node_id": "MDU6SXNzdWU1MzY5NzAyNzY=",+ "number": 744,+ "title": "Decode null to ()",+ "user": {+ "login": "domenkozar",+ "id": 126339,+ "node_id": "MDQ6VXNlcjEyNjMzOQ==",+ "avatar_url": "https://avatars2.githubusercontent.com/u/126339?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/domenkozar",+ "html_url": "https://github.com/domenkozar",+ "followers_url": "https://api.github.com/users/domenkozar/followers",+ "following_url": "https://api.github.com/users/domenkozar/following{/other_user}",+ "gists_url": "https://api.github.com/users/domenkozar/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/domenkozar/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/domenkozar/subscriptions",+ "organizations_url": "https://api.github.com/users/domenkozar/orgs",+ "repos_url": "https://api.github.com/users/domenkozar/repos",+ "events_url": "https://api.github.com/users/domenkozar/events{/privacy}",+ "received_events_url": "https://api.github.com/users/domenkozar/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 3,+ "created_at": "2019-12-12T13:13:33Z",+ "updated_at": "2019-12-12T13:32:44Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Sometimes you always expect `null` in a certain field and it would be great if that could get decoded to `()`."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/733",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/733/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/733/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/733/events",+ "html_url": "https://github.com/bos/aeson/issues/733",+ "id": 506387744,+ "node_id": "MDU6SXNzdWU1MDYzODc3NDQ=",+ "number": 733,+ "title": "Development Workflow",+ "user": {+ "login": "gdevanla",+ "id": 1274371,+ "node_id": "MDQ6VXNlcjEyNzQzNzE=",+ "avatar_url": "https://avatars0.githubusercontent.com/u/1274371?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/gdevanla",+ "html_url": "https://github.com/gdevanla",+ "followers_url": "https://api.github.com/users/gdevanla/followers",+ "following_url": "https://api.github.com/users/gdevanla/following{/other_user}",+ "gists_url": "https://api.github.com/users/gdevanla/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/gdevanla/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/gdevanla/subscriptions",+ "organizations_url": "https://api.github.com/users/gdevanla/orgs",+ "repos_url": "https://api.github.com/users/gdevanla/repos",+ "events_url": "https://api.github.com/users/gdevanla/events{/privacy}",+ "received_events_url": "https://api.github.com/users/gdevanla/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 10,+ "created_at": "2019-10-13T21:56:41Z",+ "updated_at": "2019-10-16T13:35:35Z",+ "closed_at": null,+ "author_association": "CONTRIBUTOR",+ "body": "One challenge I have had while making incremental changes to the code base was that running `stack build` or `stack test` takes a while. What is the workflow other contributors follow in these scenarios? \r\n\r\nAt the least, when I run `stack test`, I would like to have a way to just run specific tests or just tests within a particular scope. Currently, when I run `stack test`,a lot of time is spent in building libraries like `criterion` and 'statistics`. Is there a way to turn them off as needed.\r\n\r\nOverall, I would like to contribute to a document (assuming this is not there already) that describes a development workflow which other contributors could use. Can someone point me to any resources or share their workflow. I can create a document by compiling those."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/724",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/724/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/724/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/724/events",+ "html_url": "https://github.com/bos/aeson/issues/724",+ "id": 482230210,+ "node_id": "MDU6SXNzdWU0ODIyMzAyMTA=",+ "number": 724,+ "title": "Add default wrapper",+ "user": {+ "login": "treeowl",+ "id": 1893117,+ "node_id": "MDQ6VXNlcjE4OTMxMTc=",+ "avatar_url": "https://avatars2.githubusercontent.com/u/1893117?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/treeowl",+ "html_url": "https://github.com/treeowl",+ "followers_url": "https://api.github.com/users/treeowl/followers",+ "following_url": "https://api.github.com/users/treeowl/following{/other_user}",+ "gists_url": "https://api.github.com/users/treeowl/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/treeowl/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/treeowl/subscriptions",+ "organizations_url": "https://api.github.com/users/treeowl/orgs",+ "repos_url": "https://api.github.com/users/treeowl/repos",+ "events_url": "https://api.github.com/users/treeowl/events{/privacy}",+ "received_events_url": "https://api.github.com/users/treeowl/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 2,+ "created_at": "2019-08-19T10:27:18Z",+ "updated_at": "2019-09-04T19:54:59Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "```haskell\r\nnewtype DefaultJSON a = DefaultJSON a\r\n\r\ninstance (Generic a, GToJSON Zero (Rep a), GToEncoding Zero (Rep a))\r\n => ToJSON (DefaultJSON a) where\r\n toJSON (DefaultJSON a) = genericToJSON defaultOptions a\r\n toEncoding (DefaultJSON a) = genericToEncoding defaultOptions a\r\n\r\ninstance (Generic a, GFromJSON Zero (Rep a)) => FromJSON (DefaultJSON a) where\r\n parseJSON x = DefaultJSON <$> genericParseJSON defaultOptions x\r\n```\r\n\r\nThis can be used with `DerivingVia`:\r\n\r\n```haskell\r\ndata Message val = Message {val :: val, teamId :: Int, messageType :: Char}\r\n deriving Generic\r\n deriving (ToJSON, FromJSON) via DefaultJSON (Message val)\r\n```\r\n\r\nThis is almost as easy as writing empty instances, but it includes the `toEncoding` optimization for `ToJSON`."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/720",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/720/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/720/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/720/events",+ "html_url": "https://github.com/bos/aeson/issues/720",+ "id": 473768871,+ "node_id": "MDU6SXNzdWU0NzM3Njg4NzE=",+ "number": 720,+ "title": "Unexpected behaviour with Maybe typed fields and UntaggedValue",+ "user": {+ "login": "edwardb96",+ "id": 8405233,+ "node_id": "MDQ6VXNlcjg0MDUyMzM=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/8405233?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/edwardb96",+ "html_url": "https://github.com/edwardb96",+ "followers_url": "https://api.github.com/users/edwardb96/followers",+ "following_url": "https://api.github.com/users/edwardb96/following{/other_user}",+ "gists_url": "https://api.github.com/users/edwardb96/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/edwardb96/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/edwardb96/subscriptions",+ "organizations_url": "https://api.github.com/users/edwardb96/orgs",+ "repos_url": "https://api.github.com/users/edwardb96/repos",+ "events_url": "https://api.github.com/users/edwardb96/events{/privacy}",+ "received_events_url": "https://api.github.com/users/edwardb96/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2019-07-28T17:49:45Z",+ "updated_at": "2019-07-28T19:41:21Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "I'm not sure if this is a bug or a feature so I thought I'd check. Here is a minimal working example.\r\n\r\n```haskell\r\n{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}\r\nimport Data.Aeson\r\nimport Data.Aeson.TH\r\n\r\ndata Fruit = Apple { a :: Integer, b :: Maybe Bool }\r\n | Banana { a :: Integer }\r\n deriving Show\r\n$(deriveJSON defaultOptions { sumEncoding = UntaggedValue } 'Apple)\r\n\r\nmain :: IO ()\r\nmain = \r\n do\r\n putStrLn $ show (decode \"{ \\\"a\\\": 10 }\" :: Maybe Fruit) \r\n -- Expected Just (Banana {a = 10}), got Just (Apple {a = 10, b = Nothing})\r\n putStrLn $ show (decode \"{ \\\"a\\\": 10, \\\"b\\\": true }\" :: Maybe Fruit) \r\n -- Should be and got Just (Apple {a = 10, b = Just True}) \r\n\r\n```\r\n\r\nSince the `omitNothingFields` is `False` by default, and when it is false `Nothing`s are encoded as `null` (rather than omitted). I'd expect `{ \"a\": 10 }` to decode as a `Banana`.\r\n\r\nLikewise when `omitNothingFields` is false I'd expect to get `Nothing` for the following example.\r\n\r\n```haskell \r\n{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}\r\nimport Data.Aeson\r\nimport Data.Aeson.TH\r\n\r\ndata Fruit = Apple { a :: Integer, b :: Maybe Bool }\r\n deriving Show\r\n$(deriveJSON defaultOptions 'Apple)\r\n\r\nmain :: IO ()\r\nmain = \r\n do\r\n putStrLn $ show (decode \"{ \\\"a\\\": 10 }\" :: Maybe Fruit) -- Expected Nothing, got (Apple { a = 10, b = Nothing})\r\n```\r\nI expect this is a design choice rather than a bug, If this is the case perhaps it could be documented more clearly or highlighted in the documentation for `UntaggedValue` e.g.\r\n\r\n> Note: Since omitted `Maybe` typed fields decode to `Nothing`, the set of non-maybe fields from each constructor should be disjoint. \r\n\r\nIf this is indeed a bug, I have hacked together a [patch](https://gist.github.com/edwardb96/aa9cefde626a5141b2cf28ce3c1a7962) for my own purposes but would be happy to tidy it up and turn it into a PR."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/704",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/704/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/704/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/704/events",+ "html_url": "https://github.com/bos/aeson/issues/704",+ "id": 449409556,+ "node_id": "MDU6SXNzdWU0NDk0MDk1NTY=",+ "number": 704,+ "title": "Ord instance for Value?",+ "user": {+ "login": "Gabriel439",+ "id": 1313787,+ "node_id": "MDQ6VXNlcjEzMTM3ODc=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/1313787?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/Gabriel439",+ "html_url": "https://github.com/Gabriel439",+ "followers_url": "https://api.github.com/users/Gabriel439/followers",+ "following_url": "https://api.github.com/users/Gabriel439/following{/other_user}",+ "gists_url": "https://api.github.com/users/Gabriel439/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/Gabriel439/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/Gabriel439/subscriptions",+ "organizations_url": "https://api.github.com/users/Gabriel439/orgs",+ "repos_url": "https://api.github.com/users/Gabriel439/repos",+ "events_url": "https://api.github.com/users/Gabriel439/events{/privacy}",+ "received_events_url": "https://api.github.com/users/Gabriel439/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 17,+ "created_at": "2019-05-28T18:16:49Z",+ "updated_at": "2019-12-06T17:01:40Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "I could add an `Ord` instance for `Value` if it would be welcome. The context for this to be able to store `Value`s inside of a `Set`"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/700",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/700/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/700/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/700/events",+ "html_url": "https://github.com/bos/aeson/issues/700",+ "id": 443832632,+ "node_id": "MDU6SXNzdWU0NDM4MzI2MzI=",+ "number": 700,+ "title": "Expose GToJSON",+ "user": {+ "login": "LukaHorvat",+ "id": 370627,+ "node_id": "MDQ6VXNlcjM3MDYyNw==",+ "avatar_url": "https://avatars0.githubusercontent.com/u/370627?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/LukaHorvat",+ "html_url": "https://github.com/LukaHorvat",+ "followers_url": "https://api.github.com/users/LukaHorvat/followers",+ "following_url": "https://api.github.com/users/LukaHorvat/following{/other_user}",+ "gists_url": "https://api.github.com/users/LukaHorvat/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/LukaHorvat/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/LukaHorvat/subscriptions",+ "organizations_url": "https://api.github.com/users/LukaHorvat/orgs",+ "repos_url": "https://api.github.com/users/LukaHorvat/repos",+ "events_url": "https://api.github.com/users/LukaHorvat/events{/privacy}",+ "received_events_url": "https://api.github.com/users/LukaHorvat/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 6,+ "created_at": "2019-05-14T10:21:04Z",+ "updated_at": "2019-07-28T22:56:05Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Currently, only `GFromJSON` is exposed. `GToJSON` is only exposed via the two type synonyms which means that I can't write custom instances for that class. My use case is that I have a type (`data Unset = Unset`) that I want to use for some fields in a record and I want those fields to be dropped from the JSON representation of that record. If `GToJSON` was exposed, I could just duplicate the trick used for `Maybe`. \r\n\r\nSince `GFromJSON` is already exposed, this already has a precedent."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/694",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/694/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/694/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/694/events",+ "html_url": "https://github.com/bos/aeson/pull/694",+ "id": 433347757,+ "node_id": "MDExOlB1bGxSZXF1ZXN0MjcwNTY4NzM5",+ "number": 694,+ "title": "Export isEmptyArray from Data.Aeson.Types",+ "user": {+ "login": "callummance",+ "id": 4654774,+ "node_id": "MDQ6VXNlcjQ2NTQ3NzQ=",+ "avatar_url": "https://avatars1.githubusercontent.com/u/4654774?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/callummance",+ "html_url": "https://github.com/callummance",+ "followers_url": "https://api.github.com/users/callummance/followers",+ "following_url": "https://api.github.com/users/callummance/following{/other_user}",+ "gists_url": "https://api.github.com/users/callummance/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/callummance/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/callummance/subscriptions",+ "organizations_url": "https://api.github.com/users/callummance/orgs",+ "repos_url": "https://api.github.com/users/callummance/repos",+ "events_url": "https://api.github.com/users/callummance/events{/privacy}",+ "received_events_url": "https://api.github.com/users/callummance/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2019-04-15T15:32:42Z",+ "updated_at": "2019-04-23T16:05:39Z",+ "closed_at": null,+ "author_association": "NONE",+ "pull_request": {+ "url": "https://api.github.com/repos/bos/aeson/pulls/694",+ "html_url": "https://github.com/bos/aeson/pull/694",+ "diff_url": "https://github.com/bos/aeson/pull/694.diff",+ "patch_url": "https://github.com/bos/aeson/pull/694.patch"+ },+ "body": "Export `isEmptyArray` found in Data.Aeson.Types.Internal from Data.Aeson.Types. "+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/693",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/693/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/693/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/693/events",+ "html_url": "https://github.com/bos/aeson/issues/693",+ "id": 428959371,+ "node_id": "MDU6SXNzdWU0Mjg5NTkzNzE=",+ "number": 693,+ "title": "Committed choice for parsing Objects",+ "user": {+ "login": "robrix",+ "id": 59671,+ "node_id": "MDQ6VXNlcjU5Njcx",+ "avatar_url": "https://avatars0.githubusercontent.com/u/59671?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/robrix",+ "html_url": "https://github.com/robrix",+ "followers_url": "https://api.github.com/users/robrix/followers",+ "following_url": "https://api.github.com/users/robrix/following{/other_user}",+ "gists_url": "https://api.github.com/users/robrix/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/robrix/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/robrix/subscriptions",+ "organizations_url": "https://api.github.com/users/robrix/orgs",+ "repos_url": "https://api.github.com/users/robrix/repos",+ "events_url": "https://api.github.com/users/robrix/events{/privacy}",+ "received_events_url": "https://api.github.com/users/robrix/received_events",+ "type": "User",+ "site_admin": true+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2019-04-03T20:27:00Z",+ "updated_at": "2019-04-03T22:47:59Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Is there an idiomatic way to do committed choice when parsing an `Object`? E.g. we are parsing a structure which always has fields `a` and `b`, and may have `c`, `d`, or neither. Our naïve parser looks something like:\r\n\r\n```haskell\r\nparseJSON (Object v) = do\r\n a <- v .: \"a\"\r\n b <- v .: \"b\"\r\n C a b <$> v .: \"c\" <|> D a b <$> v .: \"d\" <|> pure (E a b)\r\n```\r\n\r\nUnfortunately, if parsing the values in `c` or `d` fails, we’ll silently discard the failures and fall back to the `E` case. Instead, we’d like to commit to parsing a `C` as soon as we see the `c` key, or a `D` as soon as we see the `d` key, and only parse the `E` if neither key exists.\r\n\r\nIn `trifecta` (for example), choices are committed by default, and can only be backtracked past with `try`; but there doesn’t seem to be an inverse `commit` operator in `aeson` (at least, not that I’ve found thus far). Our current approach is therefore agonizingly manual:\r\n\r\n```haskell\r\nparseJSON (Object v) = do\r\n a <- v .: \"a\"\r\n b <- v .: \"b\"\r\n c <- v .:? \"c\"\r\n case c of\r\n Just c -> pure (C a b c)\r\n Nothing -> do\r\n d <- v .:? \"d\"\r\n case d of\r\n Just d -> pure (D a b d)\r\n Nothing -> pure (E a b)\r\n```\r\n\r\nIs there a better way to implement this sort of choice?\r\n\r\ncc @aymannadeem"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/687",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/687/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/687/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/687/events",+ "html_url": "https://github.com/bos/aeson/issues/687",+ "id": 417900732,+ "node_id": "MDU6SXNzdWU0MTc5MDA3MzI=",+ "number": 687,+ "title": "Derived ToJSON1 instance does not respect omitNothingFields = True",+ "user": {+ "login": "ejconlon",+ "id": 37287,+ "node_id": "MDQ6VXNlcjM3Mjg3",+ "avatar_url": "https://avatars2.githubusercontent.com/u/37287?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/ejconlon",+ "html_url": "https://github.com/ejconlon",+ "followers_url": "https://api.github.com/users/ejconlon/followers",+ "following_url": "https://api.github.com/users/ejconlon/following{/other_user}",+ "gists_url": "https://api.github.com/users/ejconlon/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/ejconlon/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/ejconlon/subscriptions",+ "organizations_url": "https://api.github.com/users/ejconlon/orgs",+ "repos_url": "https://api.github.com/users/ejconlon/repos",+ "events_url": "https://api.github.com/users/ejconlon/events{/privacy}",+ "received_events_url": "https://api.github.com/users/ejconlon/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 8,+ "created_at": "2019-03-06T16:38:45Z",+ "updated_at": "2019-03-14T11:43:13Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "For an instance\r\n\r\n```\r\ndata C a = C { required :: a, optional :: Maybe a }\r\n deriving (Eq, Show, Generic1)\r\n deriving (ToJSON1) via (AesonWrapper1 C)\r\n```\r\n\r\nwith a simple deriving via newtype wrapper `AesonWrapper1` I am seeing the value `C 1 Nothing` serialize with `null`\r\n\r\n```\r\n Example test case: FAIL\r\n Repro.hs:77:\r\n expected: \"{\\\"required\\\":1}\"\r\n but got: \"{\\\"required\\\":1,\\\"optional\\\":null}\"\r\n```\r\n\r\nSee https://gist.github.com/ejconlon/30dc50b183a0f72501c3e49ce57cae52 for a self-contained repro."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/685",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/685/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/685/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/685/events",+ "html_url": "https://github.com/bos/aeson/issues/685",+ "id": 408401800,+ "node_id": "MDU6SXNzdWU0MDg0MDE4MDA=",+ "number": 685,+ "title": "Feature request: a function to run Parser without pass-through",+ "user": {+ "login": "fumieval",+ "id": 761865,+ "node_id": "MDQ6VXNlcjc2MTg2NQ==",+ "avatar_url": "https://avatars0.githubusercontent.com/u/761865?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/fumieval",+ "html_url": "https://github.com/fumieval",+ "followers_url": "https://api.github.com/users/fumieval/followers",+ "following_url": "https://api.github.com/users/fumieval/following{/other_user}",+ "gists_url": "https://api.github.com/users/fumieval/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/fumieval/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/fumieval/subscriptions",+ "organizations_url": "https://api.github.com/users/fumieval/orgs",+ "repos_url": "https://api.github.com/users/fumieval/repos",+ "events_url": "https://api.github.com/users/fumieval/events{/privacy}",+ "received_events_url": "https://api.github.com/users/fumieval/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2019-02-09T05:45:49Z",+ "updated_at": "2019-02-09T06:04:57Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "I frequently end up writing `flip J.parseEither () $ const $ do ...`. The following function would be very useful:\r\n\r\n```haskell\r\nexecParser :: Parser a -> Either String a\r\nexecParser m = runParser m [] onError Right\r\n where onError path msg = Left (formatError path msg)\r\n```"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/683",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/683/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/683/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/683/events",+ "html_url": "https://github.com/bos/aeson/issues/683",+ "id": 393410619,+ "node_id": "MDU6SXNzdWUzOTM0MTA2MTk=",+ "number": 683,+ "title": "Custom error type",+ "user": {+ "login": "asheshambasta",+ "id": 580365,+ "node_id": "MDQ6VXNlcjU4MDM2NQ==",+ "avatar_url": "https://avatars3.githubusercontent.com/u/580365?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/asheshambasta",+ "html_url": "https://github.com/asheshambasta",+ "followers_url": "https://api.github.com/users/asheshambasta/followers",+ "following_url": "https://api.github.com/users/asheshambasta/following{/other_user}",+ "gists_url": "https://api.github.com/users/asheshambasta/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/asheshambasta/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/asheshambasta/subscriptions",+ "organizations_url": "https://api.github.com/users/asheshambasta/orgs",+ "repos_url": "https://api.github.com/users/asheshambasta/repos",+ "events_url": "https://api.github.com/users/asheshambasta/events{/privacy}",+ "received_events_url": "https://api.github.com/users/asheshambasta/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 6,+ "created_at": "2018-12-21T10:38:51Z",+ "updated_at": "2019-01-04T13:09:22Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "We're using Haskell to write one of our services, and we're using Aeson for input-json parsing. \r\n\r\nHowever, the API of the platform as a whole _must_ expose standardised validation errors like my example:\r\n\r\n```javascript\r\n{\r\n \"errors\": [\r\n {\r\n \"code\": \"ERR.SOMEFIELD.INVALID\",\r\n \"field\": \"foo\",\r\n \"type\": \"field\",\r\n \"message\": \"Expecting an 'int' but got 'string'\"\r\n },\r\n {\r\n \"code\": \"ERR.PLAN.INVALID_NAME\",\r\n \"type\": \"field\",\r\n \"field\" : \"user.plan\", \r\n \"message\": \"All plan names must start with a 'urn:plan'\" // nullable \r\n }\r\n ],\r\n \"service\": \"SomeServiceIdentifier/version\"\r\n}\r\n```\r\nI see that in Aeson this is not possible, since the `Error` data constructor only uses `String` for its output errors. Also, my understanding is that accumulating JSON errors is achieved using `(<*>+)` which is quite cool. \r\n\r\nWhat I'd like to understand is how we achieve some sort of a custom error type that can be represented like the example above, if possible at all. The naive approach would be to use `fail` with some sort of standardized string and then parse the custom error fields out of it, but that is rather unsatisfactory. \r\n\r\nIs there a way to achieve this? \r\n\r\n#\r\n\r\nEdit: I'd like to contribute to this, but I'm quite new to Haskell. "+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/679",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/679/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/679/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/679/events",+ "html_url": "https://github.com/bos/aeson/issues/679",+ "id": 377974398,+ "node_id": "MDU6SXNzdWUzNzc5NzQzOTg=",+ "number": 679,+ "title": "`FromJSON` and `ToJSON` don't roundtrip for `Double`",+ "user": {+ "login": "NorfairKing",+ "id": 3521180,+ "node_id": "MDQ6VXNlcjM1MjExODA=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/3521180?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/NorfairKing",+ "html_url": "https://github.com/NorfairKing",+ "followers_url": "https://api.github.com/users/NorfairKing/followers",+ "following_url": "https://api.github.com/users/NorfairKing/following{/other_user}",+ "gists_url": "https://api.github.com/users/NorfairKing/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/NorfairKing/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/NorfairKing/subscriptions",+ "organizations_url": "https://api.github.com/users/NorfairKing/orgs",+ "repos_url": "https://api.github.com/users/NorfairKing/repos",+ "events_url": "https://api.github.com/users/NorfairKing/events{/privacy}",+ "received_events_url": "https://api.github.com/users/NorfairKing/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 7,+ "created_at": "2018-11-06T18:20:53Z",+ "updated_at": "2018-11-06T21:51:29Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": " ```\r\n1) Test.Validity.Aeson, JSON Double (unchecked), decode :: Data.ByteString.Lazy.ByteString -> Either String Double, ensures that encode and decode are inverses for \"unchecked Double\"'s\r\n Falsifiable (after 12 tests):\r\n Infinity\r\n Decoding succeeded, but the decoded value\r\n NaN\r\n differs from expected decoded value\r\n Infinity\r\n 'encode' encoded it to the json\r\n \"null\"\r\n```"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/666",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/666/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/666/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/666/events",+ "html_url": "https://github.com/bos/aeson/issues/666",+ "id": 357916991,+ "node_id": "MDU6SXNzdWUzNTc5MTY5OTE=",+ "number": 666,+ "title": "Add `withTypeableObject` that extracts name of the object from the type",+ "user": {+ "login": "chshersh",+ "id": 4276606,+ "node_id": "MDQ6VXNlcjQyNzY2MDY=",+ "avatar_url": "https://avatars0.githubusercontent.com/u/4276606?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/chshersh",+ "html_url": "https://github.com/chshersh",+ "followers_url": "https://api.github.com/users/chshersh/followers",+ "following_url": "https://api.github.com/users/chshersh/following{/other_user}",+ "gists_url": "https://api.github.com/users/chshersh/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/chshersh/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/chshersh/subscriptions",+ "organizations_url": "https://api.github.com/users/chshersh/orgs",+ "repos_url": "https://api.github.com/users/chshersh/repos",+ "events_url": "https://api.github.com/users/chshersh/events{/privacy}",+ "received_events_url": "https://api.github.com/users/chshersh/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 0,+ "created_at": "2018-09-07T04:38:20Z",+ "updated_at": "2018-09-07T04:38:20Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Usually when I have custom data type like this one:\r\n\r\n```haskell\r\ndata Foo = Foo\r\n { fooBar :: Int\r\n , fooBaz :: Double\r\n }\r\n```\r\n\r\nI write `FromJSON` instance like this:\r\n\r\n```haskell\r\ninstance FromJSON Foo where\r\n parseJSON = withObject \"Foo\" $ \\o -> ...\r\n```\r\n\r\nI need to specify name of the type to get good error messages. But if type name changes, I need to not forget to update this function. Also, it's not really convenient to write name of type each type. I propose to add the following function to the `aeson` library:\r\n\r\n```haskell\r\ntypeName :: forall a . Typeable a => Text\r\ntypeName = show $ typeRep $ Proxy @a\r\n\r\nwithTypeableObject :: forall a . Typeable a => (Object -> Parser a) -> Value -> Parser a\r\nwithTypeableObject = withObject (typeName @a)\r\n```"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/660",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/660/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/660/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/660/events",+ "html_url": "https://github.com/bos/aeson/issues/660",+ "id": 348521320,+ "node_id": "MDU6SXNzdWUzNDg1MjEzMjA=",+ "number": 660,+ "title": "Inconsistent encoding of Double depending on container structure",+ "user": {+ "login": "akhra",+ "id": 3770453,+ "node_id": "MDQ6VXNlcjM3NzA0NTM=",+ "avatar_url": "https://avatars2.githubusercontent.com/u/3770453?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/akhra",+ "html_url": "https://github.com/akhra",+ "followers_url": "https://api.github.com/users/akhra/followers",+ "following_url": "https://api.github.com/users/akhra/following{/other_user}",+ "gists_url": "https://api.github.com/users/akhra/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/akhra/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/akhra/subscriptions",+ "organizations_url": "https://api.github.com/users/akhra/orgs",+ "repos_url": "https://api.github.com/users/akhra/repos",+ "events_url": "https://api.github.com/users/akhra/events{/privacy}",+ "received_events_url": "https://api.github.com/users/akhra/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 3,+ "created_at": "2018-08-07T23:24:53Z",+ "updated_at": "2018-08-08T12:33:03Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "This is closely related to #546. Initially I thought there was some oddness specific to derived instances, but [this comment](https://github.com/bos/aeson/issues/546#issuecomment-299991780) pointed me toward a nice trivial example of the underlying cause:\r\n```haskell\r\nλ. import Data.Aeson\r\nλ. encode 0.0\r\n\"0.0\"\r\nλ. encode $ toJSON 0.0\r\n\"0\"\r\n```\r\nAnd its consequence:\r\n```haskell\r\nλ. :set -XDeriveGeneric -XDeriveAnyClass -XDerivingStrategies -XGeneralizedNewtypeDeriving\r\nλ. newtype Foo = Foo Double deriving Generic\r\nλ. deriving newtype instance ToJSON Foo\r\nλ. encode $ Foo 0.0\r\n\"0.0\"\r\nλ. newtype Bar = Bar Double deriving Generic\r\nλ. deriving anyclass instance ToJSON Bar\r\nλ. encode $ Bar 0.0\r\n\"0\"\r\nλ. data Baz = Baz Double deriving (Generic, ToJSON)\r\nλ. encode $ Baz 0.0\r\n\"0\"\r\nλ. data Qux = Qux Foo deriving (Generic, ToJSON)\r\nλ. encode $ Qux (Foo 0.0)\r\n\"0\"\r\n```\r\nThat last one is especially irksome: checking the newtype encoding doesn't reflect what the full structure will look like. (And of course, that's exactly the context where I ran into this.)\r\n\r\nThis issue is specifically about the _inconsistency_. It is surprising and undesirable that `Double`s render differently depending on context. Which way they _should_ be rendered probably belongs to #546. However, I should add that at present it's not a clear-cut matter of `deriving newtype` being the odd one out:\r\n```haskell\r\nλ. encode (0.0, \"whoops\")\r\n\"[0.0,\\\"whoops\\\"]\"\r\n```"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/659",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/659/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/659/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/659/events",+ "html_url": "https://github.com/bos/aeson/issues/659",+ "id": 348025769,+ "node_id": "MDU6SXNzdWUzNDgwMjU3Njk=",+ "number": 659,+ "title": "FromJSONKeyCoerce warning",+ "user": {+ "login": "MaxGabriel",+ "id": 1274145,+ "node_id": "MDQ6VXNlcjEyNzQxNDU=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/1274145?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/MaxGabriel",+ "html_url": "https://github.com/MaxGabriel",+ "followers_url": "https://api.github.com/users/MaxGabriel/followers",+ "following_url": "https://api.github.com/users/MaxGabriel/following{/other_user}",+ "gists_url": "https://api.github.com/users/MaxGabriel/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/MaxGabriel/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/MaxGabriel/subscriptions",+ "organizations_url": "https://api.github.com/users/MaxGabriel/orgs",+ "repos_url": "https://api.github.com/users/MaxGabriel/repos",+ "events_url": "https://api.github.com/users/MaxGabriel/events{/privacy}",+ "received_events_url": "https://api.github.com/users/MaxGabriel/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": {+ "url": "https://api.github.com/repos/bos/aeson/milestones/21",+ "html_url": "https://github.com/bos/aeson/milestone/21",+ "labels_url": "https://api.github.com/repos/bos/aeson/milestones/21/labels",+ "id": 5240808,+ "node_id": "MDk6TWlsZXN0b25lNTI0MDgwOA==",+ "number": 21,+ "title": "v1.5.0.0",+ "description": "",+ "creator": {+ "login": "bergmark",+ "id": 100681,+ "node_id": "MDQ6VXNlcjEwMDY4MQ==",+ "avatar_url": "https://avatars2.githubusercontent.com/u/100681?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/bergmark",+ "html_url": "https://github.com/bergmark",+ "followers_url": "https://api.github.com/users/bergmark/followers",+ "following_url": "https://api.github.com/users/bergmark/following{/other_user}",+ "gists_url": "https://api.github.com/users/bergmark/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/bergmark/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/bergmark/subscriptions",+ "organizations_url": "https://api.github.com/users/bergmark/orgs",+ "repos_url": "https://api.github.com/users/bergmark/repos",+ "events_url": "https://api.github.com/users/bergmark/events{/privacy}",+ "received_events_url": "https://api.github.com/users/bergmark/received_events",+ "type": "User",+ "site_admin": false+ },+ "open_issues": 1,+ "closed_issues": 6,+ "state": "open",+ "created_at": "2020-03-26T15:59:09Z",+ "updated_at": "2020-04-05T09:44:01Z",+ "due_on": null,+ "closed_at": null+ },+ "comments": 1,+ "created_at": "2018-08-06T18:02:56Z",+ "updated_at": "2020-04-04T14:40:37Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Hi, I just installed aeson-1.4.0.0 and got this warning:\r\n\r\n```\r\nData/Aeson/Types/FromJSON.hs:468:3: warning: [-Winline-rule-shadowing]\r\n Rule \"FromJSONKeyCoerce: fmap id\" may never fire\r\n because ‘id’ might inline first\r\n Probable fix: add an INLINE[n] or NOINLINE[n] pragma for ‘id’\r\n |\r\n468 | \"FromJSONKeyCoerce: fmap id\" forall (x :: FromJSONKeyFunction a).\r\n |\r\n```\r\n\r\nIt's not causing me any problems, just wanted to let you know.\r\n\r\nLooks like the warning may have been around awhile since it appears here as well: https://github.com/bos/aeson/issues/543"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/658",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/658/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/658/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/658/events",+ "html_url": "https://github.com/bos/aeson/issues/658",+ "id": 346293459,+ "node_id": "MDU6SXNzdWUzNDYyOTM0NTk=",+ "number": 658,+ "title": "Make functions inlinable",+ "user": {+ "login": "flip111",+ "id": 2244480,+ "node_id": "MDQ6VXNlcjIyNDQ0ODA=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/2244480?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/flip111",+ "html_url": "https://github.com/flip111",+ "followers_url": "https://api.github.com/users/flip111/followers",+ "following_url": "https://api.github.com/users/flip111/following{/other_user}",+ "gists_url": "https://api.github.com/users/flip111/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/flip111/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/flip111/subscriptions",+ "organizations_url": "https://api.github.com/users/flip111/orgs",+ "repos_url": "https://api.github.com/users/flip111/repos",+ "events_url": "https://api.github.com/users/flip111/events{/privacy}",+ "received_events_url": "https://api.github.com/users/flip111/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 2,+ "created_at": "2018-07-31T17:49:10Z",+ "updated_at": "2020-04-04T12:57:44Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "When i compile with `-Wmissed-specialisations` actually indirectly by compiling with `-Weverything` i get the following warnings:\r\n\r\n```\r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$ctoJSONKey’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fToJSONKey(,)_$ctoJSONKey’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$ctoJSONKey’\r\n\r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cgToJSON’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fGToJSONencarityM11_$cgToJSON’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cgToJSON’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'2’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fSumToJSON'TYPETwoElemArrayValuearityM2’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'2’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fSumToJSON'TYPEObjectWithSingleFieldencarityM1_$csumToJSON'’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$ctaggedObject’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fTaggedObjectencarityM1_$ctaggedObject’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$ctaggedObject’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fGToJSONValuearity:*:_$cgToJSON’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fGToJSONValuearity:*:_$cgToJSON’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fKeyValuePairvDList_$cpair’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fKeyValuePairvDList’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fKeyValuePairvDList_$cpair’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cwriteProduct’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fWriteProductarity:*:_$cwriteProduct’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cwriteProduct’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cwriteProduct1’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fWriteProductaritya_$cwriteProduct’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$cwriteProduct1’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'1’\r\n when specialising ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fSumToJSON'TYPETwoElemArrayEncoding'arityM2’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$w$csumToJSON'1’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fGToJSONEncoding'arity:*:1’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fGToJSONEncoding'arity:*:1’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fEncodeProductarity:*:_$cencodeProduct’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fEncodeProductarity:*:_$cencodeProduct’\r\n \r\nsrc/Main.hs: warning: \r\n Could not specialise imported function ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fKeyValuePairvDList_$cpair’\r\n Probable fix: add INLINABLE pragma on ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON.$fKeyValuePairvDList_$cpair’\r\n```\r\n\r\nI think there is a missed optimization here. The inlinable pragma should be added in the module which export these functions."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/653",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/653/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/653/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/653/events",+ "html_url": "https://github.com/bos/aeson/pull/653",+ "id": 343386192,+ "node_id": "MDExOlB1bGxSZXF1ZXN0MjAzMDQ1MDcx",+ "number": 653,+ "title": "Rework GFromJSON instances to enable elimination of Generics",+ "user": {+ "login": "remyoudompheng",+ "id": 175246,+ "node_id": "MDQ6VXNlcjE3NTI0Ng==",+ "avatar_url": "https://avatars3.githubusercontent.com/u/175246?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/remyoudompheng",+ "html_url": "https://github.com/remyoudompheng",+ "followers_url": "https://api.github.com/users/remyoudompheng/followers",+ "following_url": "https://api.github.com/users/remyoudompheng/following{/other_user}",+ "gists_url": "https://api.github.com/users/remyoudompheng/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/remyoudompheng/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/remyoudompheng/subscriptions",+ "organizations_url": "https://api.github.com/users/remyoudompheng/orgs",+ "repos_url": "https://api.github.com/users/remyoudompheng/repos",+ "events_url": "https://api.github.com/users/remyoudompheng/events{/privacy}",+ "received_events_url": "https://api.github.com/users/remyoudompheng/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [+ {+ "id": 713822109,+ "node_id": "MDU6TGFiZWw3MTM4MjIxMDk=",+ "url": "https://api.github.com/repos/bos/aeson/labels/versioning%20level:%20minor/non-breaking%20addition",+ "name": "versioning level: minor/non-breaking addition",+ "color": "fbca04",+ "default": false,+ "description": null+ }+ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 9,+ "created_at": "2018-07-22T07:55:44Z",+ "updated_at": "2019-09-07T14:56:10Z",+ "closed_at": null,+ "author_association": "NONE",+ "pull_request": {+ "url": "https://api.github.com/repos/bos/aeson/pulls/653",+ "html_url": "https://github.com/bos/aeson/pull/653",+ "diff_url": "https://github.com/bos/aeson/pull/653.diff",+ "patch_url": "https://github.com/bos/aeson/pull/653.patch"+ },+ "body": "The Parser monad signature makes inlining very difficult\r\nbecause GHC only inlines saturated applications, which is\r\nhard to achieve with Parser.\r\n\r\nWe work around this by defining an alternate method in GFromJSON\r\nwhich manipulates the semantically equivalent IResult monad\r\n(Parser is technically more expressive but not for the terms appearing\r\nin GFromJSON). The existing method is left for API compatibility.\r\n\r\nThe resulting code achieves rougly the same compilation speed but\r\nreaches large performance improvements, for a generated code size\r\nnot far from the manually written instances in benchmarks.\r\n\r\nThe object file for Twitter.Generic grows to 332kB (from 285kB),\r\nbut the Twitter.Manual module is 300kB large. The inlining level of\r\ntypeMismatch has been reduced to curb code blow-up.\r\n\r\nDue to inlining levels differences, the Generic fromJSON is now\r\noften quicker than its TH or Manual counterpart in benchmarks.\r\n\r\nBenchmark results (Core i5 Haswell)\r\n\r\n| | TH | Generic (before) | Generic (after) | Manual |\r\n| --- | --- | --- | --- | --- |\r\n| D/fromJSON | 1.720 µs | 5.076 µs | 1.505 µs | - |\r\n| BigRecord/fromJSON | 3.720 µs | 8.450 µs | 2.964 µs | - |\r\n| BigProduct/fromJSON | 2.412 µs | 3.427 µs | 1.812 µs | - |\r\n| BigSum/fromJSON | 173 ns | 1230 ns | 171 ns | - |\r\n| twitter100 | 1.611 ms | 2.063 ms | 1.334 ms | 1.516 ms |\r\n| jp100 | 1.844 ms | 2.18 ms | 1.481 ms | 1.677 ms |\r\n"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/651",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/651/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/651/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/651/events",+ "html_url": "https://github.com/bos/aeson/issues/651",+ "id": 340747607,+ "node_id": "MDU6SXNzdWUzNDA3NDc2MDc=",+ "number": 651,+ "title": "Can't use .= operator to build JSON because of ambiguous instances.",+ "user": {+ "login": "LeifW",+ "id": 28711,+ "node_id": "MDQ6VXNlcjI4NzEx",+ "avatar_url": "https://avatars2.githubusercontent.com/u/28711?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/LeifW",+ "html_url": "https://github.com/LeifW",+ "followers_url": "https://api.github.com/users/LeifW/followers",+ "following_url": "https://api.github.com/users/LeifW/following{/other_user}",+ "gists_url": "https://api.github.com/users/LeifW/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/LeifW/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/LeifW/subscriptions",+ "organizations_url": "https://api.github.com/users/LeifW/orgs",+ "repos_url": "https://api.github.com/users/LeifW/repos",+ "events_url": "https://api.github.com/users/LeifW/events{/privacy}",+ "received_events_url": "https://api.github.com/users/LeifW/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 4,+ "created_at": "2018-07-12T18:24:11Z",+ "updated_at": "2018-07-15T17:11:21Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "I put up a gist: https://gist.github.com/LeifW/f3e51437f4494eff9d7c23355f268ef7\r\n\r\nEssentially, this file\r\n```haskell\r\n{-# LANGUAGE OverloadedStrings #-}\r\n\r\nimport Data.Aeson \r\n\r\nthing = object [ \"foo\" .= \"bar\" ]\r\n```\r\ngives this error message when you try to compile it:\r\n```\r\ntest.hs:5:18: error:\r\n • Ambiguous type variable ‘v0’ arising from a use of ‘.=’\r\n prevents the constraint ‘(ToJSON v0)’ from being solved.\r\n Probable fix: use a type annotation to specify what ‘v0’ should be.\r\n These potential instances exist:\r\n instance ToJSON DotNetTime\r\n -- Defined in ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON’\r\n instance ToJSON Value\r\n -- Defined in ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON’\r\n instance (ToJSON a, ToJSON b) => ToJSON (Either a b)\r\n -- Defined in ‘aeson-1.3.1.1:Data.Aeson.Types.ToJSON’\r\n ...plus 25 others\r\n ...plus 55 instances involving out-of-scope types\r\n (use -fprint-potential-instances to see them all)\r\n • In the expression: \"foo\" .= \"bar\"\r\n In the first argument of ‘object’, namely ‘[\"foo\" .= \"bar\"]’\r\n In the expression: object [\"foo\" .= \"bar\"]\r\n |\r\n5 | thing = object [ \"foo\" .= \"bar\" ]\r\n | ^^^^^^^^^^^^^^\r\n\r\ntest.hs:5:27: error:\r\n • Ambiguous type variable ‘v0’ arising from the literal ‘\"bar\"’\r\n prevents the constraint ‘(Data.String.IsString\r\n v0)’ from being solved.\r\n Probable fix: use a type annotation to specify what ‘v0’ should be.\r\n These potential instances exist:\r\n instance Data.String.IsString Value\r\n -- Defined in ‘aeson-1.3.1.1:Data.Aeson.Types.Internal’\r\n instance (a ~ Char) => Data.String.IsString [a]\r\n -- Defined in ‘Data.String’\r\n ...plus 11 instances involving out-of-scope types\r\n (use -fprint-potential-instances to see them all)\r\n • In the second argument of ‘(.=)’, namely ‘\"bar\"’\r\n In the expression: \"foo\" .= \"bar\"\r\n In the first argument of ‘object’, namely ‘[\"foo\" .= \"bar\"]’\r\n |\r\n5 | thing = object [ \"foo\" .= \"bar\" ]\r\n```\r\nIt works in the repl and/or if you add `ExtendedDefaultRules`, though."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/647",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/647/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/647/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/647/events",+ "html_url": "https://github.com/bos/aeson/pull/647",+ "id": 331981135,+ "node_id": "MDExOlB1bGxSZXF1ZXN0MTk0NTc1ODg2",+ "number": 647,+ "title": "Export `withBoundedScientific`",+ "user": {+ "login": "qrilka",+ "id": 70110,+ "node_id": "MDQ6VXNlcjcwMTEw",+ "avatar_url": "https://avatars2.githubusercontent.com/u/70110?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/qrilka",+ "html_url": "https://github.com/qrilka",+ "followers_url": "https://api.github.com/users/qrilka/followers",+ "following_url": "https://api.github.com/users/qrilka/following{/other_user}",+ "gists_url": "https://api.github.com/users/qrilka/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/qrilka/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/qrilka/subscriptions",+ "organizations_url": "https://api.github.com/users/qrilka/orgs",+ "repos_url": "https://api.github.com/users/qrilka/repos",+ "events_url": "https://api.github.com/users/qrilka/events{/privacy}",+ "received_events_url": "https://api.github.com/users/qrilka/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [+ {+ "id": 713822109,+ "node_id": "MDU6TGFiZWw3MTM4MjIxMDk=",+ "url": "https://api.github.com/repos/bos/aeson/labels/versioning%20level:%20minor/non-breaking%20addition",+ "name": "versioning level: minor/non-breaking addition",+ "color": "fbca04",+ "default": false,+ "description": null+ }+ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 1,+ "created_at": "2018-06-13T12:52:28Z",+ "updated_at": "2018-06-14T19:59:58Z",+ "closed_at": null,+ "author_association": "NONE",+ "pull_request": {+ "url": "https://api.github.com/repos/bos/aeson/pulls/647",+ "html_url": "https://github.com/bos/aeson/pull/647",+ "diff_url": "https://github.com/bos/aeson/pull/647.diff",+ "patch_url": "https://github.com/bos/aeson/pull/647.patch"+ },+ "body": "http://hackage.haskell.org/package/aeson-1.4.0.0/docs/Data-Aeson.html#v:withScientific advises to use `withBoundedScientific` for unbounded types but the function wasn't exported"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/646",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/646/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/646/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/646/events",+ "html_url": "https://github.com/bos/aeson/issues/646",+ "id": 331495282,+ "node_id": "MDU6SXNzdWUzMzE0OTUyODI=",+ "number": 646,+ "title": "Allow accepting missing fields of other types than Maybe",+ "user": {+ "login": "lortabac",+ "id": 1907333,+ "node_id": "MDQ6VXNlcjE5MDczMzM=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/1907333?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/lortabac",+ "html_url": "https://github.com/lortabac",+ "followers_url": "https://api.github.com/users/lortabac/followers",+ "following_url": "https://api.github.com/users/lortabac/following{/other_user}",+ "gists_url": "https://api.github.com/users/lortabac/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/lortabac/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/lortabac/subscriptions",+ "organizations_url": "https://api.github.com/users/lortabac/orgs",+ "repos_url": "https://api.github.com/users/lortabac/repos",+ "events_url": "https://api.github.com/users/lortabac/events{/privacy}",+ "received_events_url": "https://api.github.com/users/lortabac/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 13,+ "created_at": "2018-06-12T08:51:26Z",+ "updated_at": "2019-12-29T19:42:07Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "When deriving `FromJSON` instances generically, there is no way to provide a default value to a missing field of a record if the field is not a `Maybe`.\r\n\r\nThis seems related to #614, in that the proposed solution may help fixing both issues."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/644",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/644/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/644/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/644/events",+ "html_url": "https://github.com/bos/aeson/issues/644",+ "id": 326014300,+ "node_id": "MDU6SXNzdWUzMjYwMTQzMDA=",+ "number": 644,+ "title": "Expose Data.Aeson.Encoding.Builder",+ "user": {+ "login": "alexbiehl",+ "id": 1876617,+ "node_id": "MDQ6VXNlcjE4NzY2MTc=",+ "avatar_url": "https://avatars2.githubusercontent.com/u/1876617?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/alexbiehl",+ "html_url": "https://github.com/alexbiehl",+ "followers_url": "https://api.github.com/users/alexbiehl/followers",+ "following_url": "https://api.github.com/users/alexbiehl/following{/other_user}",+ "gists_url": "https://api.github.com/users/alexbiehl/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/alexbiehl/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/alexbiehl/subscriptions",+ "organizations_url": "https://api.github.com/users/alexbiehl/orgs",+ "repos_url": "https://api.github.com/users/alexbiehl/repos",+ "events_url": "https://api.github.com/users/alexbiehl/events{/privacy}",+ "received_events_url": "https://api.github.com/users/alexbiehl/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [+ {+ "id": 563935541,+ "node_id": "MDU6TGFiZWw1NjM5MzU1NDE=",+ "url": "https://api.github.com/repos/bos/aeson/labels/needs%20decision",+ "name": "needs decision",+ "color": "c5def5",+ "default": false,+ "description": null+ }+ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 3,+ "created_at": "2018-05-24T08:24:58Z",+ "updated_at": "2018-08-05T20:38:09Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "At work we have a JSON schema which demands composite keys for objects of this form: \r\n\r\n```\r\n\"<some integer>_<day>\": ... \r\n```\r\n\r\nNow, we used the venerable `toKey (n, d) = Text.pack (show n ++ '_' ++ show d)` but this turned out out to show up in our profiling and really was taking 10% of our overall runtime! So we went for some cruel hack: \r\n\r\n```haskell\r\n toKey (n ,d) = Encoding.unsafeToEncoding $\r\n Builder.char7 '\"' <> Builder.intDec r <> Builder.char7 '_'\r\n <> Encoding.fromEncoding (Encoding.day d) <> Builder.char7 '\"'\r\n```\r\n\r\nUnfortunately this results in invalid JSON as `Encoding.day` is wrapped into quotes! It would be really neat if we could access the unsafe stuff from Data.Aeson.Encoding.Builder to use the `Day` encoding without quotes.\r\n\r\n"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/642",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/642/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/642/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/642/events",+ "html_url": "https://github.com/bos/aeson/issues/642",+ "id": 323568779,+ "node_id": "MDU6SXNzdWUzMjM1Njg3Nzk=",+ "number": 642,+ "title": "Dealing with empty object from API service",+ "user": {+ "login": "Rizary",+ "id": 7221768,+ "node_id": "MDQ6VXNlcjcyMjE3Njg=",+ "avatar_url": "https://avatars3.githubusercontent.com/u/7221768?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/Rizary",+ "html_url": "https://github.com/Rizary",+ "followers_url": "https://api.github.com/users/Rizary/followers",+ "following_url": "https://api.github.com/users/Rizary/following{/other_user}",+ "gists_url": "https://api.github.com/users/Rizary/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/Rizary/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/Rizary/subscriptions",+ "organizations_url": "https://api.github.com/users/Rizary/orgs",+ "repos_url": "https://api.github.com/users/Rizary/repos",+ "events_url": "https://api.github.com/users/Rizary/events{/privacy}",+ "received_events_url": "https://api.github.com/users/Rizary/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [+ {+ "id": 315555511,+ "node_id": "MDU6TGFiZWwzMTU1NTU1MTE=",+ "url": "https://api.github.com/repos/bos/aeson/labels/question",+ "name": "question",+ "color": "5319e7",+ "default": true,+ "description": null+ }+ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 2,+ "created_at": "2018-05-16T10:52:05Z",+ "updated_at": "2018-06-23T15:08:38Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "Hi, what is the best to handle when we got `name: {}` (this is from nodejs backend) response from api service, meanwhile what i want is to assume it is 'name: {first: \"\", last: \"\"}` so I can parse it to \r\n```Person {firstName :: Maybe Text, lastName :: Maybe Text}``` ?\r\n\r\nsupposed that I have the following types:\r\nhttps://gist.github.com/Rizary/3b3865a145853cf04df7fad4e348a5c3\r\n\r\nI finally use `optional` but actually that's not what I want, because I can't parsed the `name: {}` as `name: {first: \"\", last: \"\"}`"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/641",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/641/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/641/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/641/events",+ "html_url": "https://github.com/bos/aeson/issues/641",+ "id": 322585124,+ "node_id": "MDU6SXNzdWUzMjI1ODUxMjQ=",+ "number": 641,+ "title": "Building failed on GHC 8.0.2",+ "user": {+ "login": "pe200012",+ "id": 13463539,+ "node_id": "MDQ6VXNlcjEzNDYzNTM5",+ "avatar_url": "https://avatars1.githubusercontent.com/u/13463539?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/pe200012",+ "html_url": "https://github.com/pe200012",+ "followers_url": "https://api.github.com/users/pe200012/followers",+ "following_url": "https://api.github.com/users/pe200012/following{/other_user}",+ "gists_url": "https://api.github.com/users/pe200012/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/pe200012/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/pe200012/subscriptions",+ "organizations_url": "https://api.github.com/users/pe200012/orgs",+ "repos_url": "https://api.github.com/users/pe200012/repos",+ "events_url": "https://api.github.com/users/pe200012/events{/privacy}",+ "received_events_url": "https://api.github.com/users/pe200012/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 5,+ "created_at": "2018-05-13T11:16:59Z",+ "updated_at": "2018-06-23T15:23:48Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "<pre>\r\n[ 1 of 24] Compiling Data.Attoparsec.Time.Internal ( attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs, dist/build/Data/Attoparsec/Time/Internal.o )\r\n[ 2 of 24] Compiling Data.Attoparsec.Time ( attoparsec-iso8601/Data/Attoparsec/Time.hs, dist/build/Data/Attoparsec/Time.o )\r\n[ 3 of 24] Compiling Data.Aeson.Types.Internal ( Data/Aeson/Types/Internal.hs, dist/build/Data/Aeson/Types/Internal.o )\r\n\r\nData/Aeson/Types/Internal.hs:397:17: error:\r\n • Can't find interface-file declaration for type constructor or class Language.Haskell.TH.Lib.ExpQ\r\n Probable cause: bug in .hi-boot file, or inconsistent .hi file\r\n Use -ddump-if-trace to get an idea of which file caused the error\r\n • In the expression: [| Null |]\r\n In an equation for ‘lift’: lift Null = [| Null |]\r\n In the instance declaration for ‘TH.Lift Value’\r\ncabal: Leaving directory '/tmp/cabal-tmp-12259/aeson-1.2.4.0'\r\ncabal: Error: some packages failed to install:\r\naeson-1.2.4.0 failed during the building phase. The exception was:\r\nExitFailure 1\r\nhttp-conduit-2.3.1 depends on aeson-1.2.4.0 which failed to install.\r\nidringen-0.1.0.3 depends on aeson-1.2.4.0 which failed to install.\r\nidris-1.2.0 depends on aeson-1.2.4.0 which failed to install.\r\n\r\n</pre>\r\nbuild separately,\r\n<pre>\r\nPreprocessing library aeson-1.3.1.1...\r\n[ 1 of 24] Compiling Data.Attoparsec.Time.Internal ( attoparsec-iso8601/Data/Attoparsec/Time/Internal.hs, dist/build/Data/Attoparsec/Time/Internal.o )\r\n[ 2 of 24] Compiling Data.Attoparsec.Time ( attoparsec-iso8601/Data/Attoparsec/Time.hs, dist/build/Data/Attoparsec/Time.o )\r\n[ 3 of 24] Compiling Data.Aeson.Types.Internal ( Data/Aeson/Types/Internal.hs, dist/build/Data/Aeson/Types/Internal.o )\r\n\r\nData/Aeson/Types/Internal.hs:399:17: error:\r\n • Can't find interface-file declaration for type constructor or class Language.Haskell.TH.Lib.ExpQ\r\n Probable cause: bug in .hi-boot file, or inconsistent .hi file\r\n Use -ddump-if-trace to get an idea of which file caused the error\r\n • In the expression: [| Null |]\r\n In an equation for ‘lift’: lift Null = [| Null |]\r\n In the instance declaration for ‘TH.Lift Value’\r\ncabal: Leaving directory '/tmp/cabal-tmp-12422/aeson-1.3.1.1'\r\ncabal: Error: some packages failed to install:\r\naeson-1.3.1.1 failed during the building phase. The exception was:\r\nExitFailure 1\r\n</pre>"+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/624",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/624/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/624/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/624/events",+ "html_url": "https://github.com/bos/aeson/issues/624",+ "id": 299707462,+ "node_id": "MDU6SXNzdWUyOTk3MDc0NjI=",+ "number": 624,+ "title": "Add some way (or document if it already exists) to extract data type name in 'defaultOptions'",+ "user": {+ "login": "chshersh",+ "id": 4276606,+ "node_id": "MDQ6VXNlcjQyNzY2MDY=",+ "avatar_url": "https://avatars0.githubusercontent.com/u/4276606?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/chshersh",+ "html_url": "https://github.com/chshersh",+ "followers_url": "https://api.github.com/users/chshersh/followers",+ "following_url": "https://api.github.com/users/chshersh/following{/other_user}",+ "gists_url": "https://api.github.com/users/chshersh/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/chshersh/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/chshersh/subscriptions",+ "organizations_url": "https://api.github.com/users/chshersh/orgs",+ "repos_url": "https://api.github.com/users/chshersh/repos",+ "events_url": "https://api.github.com/users/chshersh/events{/privacy}",+ "received_events_url": "https://api.github.com/users/chshersh/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 6,+ "created_at": "2018-02-23T13:13:52Z",+ "updated_at": "2018-09-07T04:07:09Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "I want to add full data type name to every field of my data type. Like this:\r\n\r\n```haskell\r\ndata ProjectManager = ProjectManager\r\n { projectManagerName :: Text\r\n , projectManagerSalary :: Natural\r\n }\r\n```\r\n\r\nAnd I want to have JSON encoding like this: `{ name: \"Vasya Pupkin\", salary: 42 }`. I don't see any way to implement general `Options` data type which can automatically work for any type with [`fieldLabelModifier`](https://hackage.haskell.org/package/aeson-1.2.4.0/docs/Data-Aeson.html#v:fieldLabelModifier) because I don't know type name from context."+ },+ {+ "url": "https://api.github.com/repos/bos/aeson/issues/622",+ "repository_url": "https://api.github.com/repos/bos/aeson",+ "labels_url": "https://api.github.com/repos/bos/aeson/issues/622/labels{/name}",+ "comments_url": "https://api.github.com/repos/bos/aeson/issues/622/comments",+ "events_url": "https://api.github.com/repos/bos/aeson/issues/622/events",+ "html_url": "https://github.com/bos/aeson/issues/622",+ "id": 296505410,+ "node_id": "MDU6SXNzdWUyOTY1MDU0MTA=",+ "number": 622,+ "title": "ToJSON/FromJSON instances fail for UTCTime, DiffTime",+ "user": {+ "login": "brainwave0",+ "id": 3056980,+ "node_id": "MDQ6VXNlcjMwNTY5ODA=",+ "avatar_url": "https://avatars0.githubusercontent.com/u/3056980?v=4",+ "gravatar_id": "",+ "url": "https://api.github.com/users/brainwave0",+ "html_url": "https://github.com/brainwave0",+ "followers_url": "https://api.github.com/users/brainwave0/followers",+ "following_url": "https://api.github.com/users/brainwave0/following{/other_user}",+ "gists_url": "https://api.github.com/users/brainwave0/gists{/gist_id}",+ "starred_url": "https://api.github.com/users/brainwave0/starred{/owner}{/repo}",+ "subscriptions_url": "https://api.github.com/users/brainwave0/subscriptions",+ "organizations_url": "https://api.github.com/users/brainwave0/orgs",+ "repos_url": "https://api.github.com/users/brainwave0/repos",+ "events_url": "https://api.github.com/users/brainwave0/events{/privacy}",+ "received_events_url": "https://api.github.com/users/brainwave0/received_events",+ "type": "User",+ "site_admin": false+ },+ "labels": [++ ],+ "state": "open",+ "locked": false,+ "assignee": null,+ "assignees": [++ ],+ "milestone": null,+ "comments": 3,+ "created_at": "2018-02-12T20:00:41Z",+ "updated_at": "2018-02-12T22:11:12Z",+ "closed_at": null,+ "author_association": "NONE",+ "body": "If I try to make an instance for a custom type that includes a UTCTime or a DiffTime, I get a compile error. For example, if I try to compile the folowing code\r\n\r\n {-# LANGUAGE DeriveGeneric #-}\r\n import GHC.Generics\r\n import Data.Aeson\r\n import Data.Time\r\n data Foo = Foo UTCTime deriving (Generic, Show)\r\n instance ToJSON Foo\r\n instance FromJSON Foo\r\n\r\nI get\r\n\r\n /home/ian/test.hs:6:10:\r\n No instance for (ToJSON UTCTime)\r\n arising from a use of ‘aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$gdmtoJSON’\r\n In the expression: aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$gdmtoJSON\r\n In an equation for ‘toJSON’:\r\n toJSON = aeson-1.2.4.0:Data.Aeson.Types.ToJSON.$gdmtoJSON\r\n In the instance declaration for ‘ToJSON Foo’\r\n \r\n /home/ian/test.hs:7:10:\r\n No instance for (FromJSON UTCTime)\r\n arising from a use of ‘aeson-1.2.4.0:Data.Aeson.Types.FromJSON.$gdmparseJSON’\r\n In the expression:\r\n aeson-1.2.4.0:Data.Aeson.Types.FromJSON.$gdmparseJSON\r\n In an equation for ‘parseJSON’:\r\n parseJSON = aeson-1.2.4.0:Data.Aeson.Types.FromJSON.$gdmparseJSON\r\n In the instance declaration for ‘FromJSON Foo’"+ }+]
changelog.md view
@@ -1,5 +1,16 @@ For the latest version of this document, please see [https://github.com/bos/aeson/blob/master/changelog.md](https://github.com/bos/aeson/blob/master/changelog.md). +## 1.5.0.0++* Fix bug in `rejectUnknownFields` not respecting `fieldLabelModifier`, thanks to Markus Schirp.+* `GFromJSON` members are no longer exported from `Data.Aeson(.Types)`, if you are using `gParseJSON` consider switching to `gParseJSON'`, thanks to Oleg Grenrus.+* Aeson so longer accepts unescaped control characters, thanks to Oleg Grenrus.+* Remove `CoerceText` since GHC >=7.8 has `Coercible`, thanks to Oleg Grenrus.+* Rename the `GToJSON` class to `GToJSON'` and expose it, thanks to Oleg Grenrus.++Closed tickets: https://github.com/bos/aeson/milestone/21++ #### 1.4.7.1 * GHC 8.10 compatibility, thanks to Ryan Scott.
− examples/Generic.hs
@@ -1,37 +0,0 @@--- This example is basically the same as in Simplest.hs, only it uses--- GHC's builtin generics instead of explicit instances of ToJSON and--- FromJSON.---- We enable the DeriveGeneric language extension so that GHC can--- automatically derive the Generic class for us.--{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}--module Main (main) where--import Prelude.Compat--import Data.Aeson (FromJSON, ToJSON, decode, encode)-import qualified Data.ByteString.Lazy.Char8 as BL-import GHC.Generics (Generic)---- To decode or encode a value using the generic machinery, we must--- make the type an instance of the Generic class.-data Coord = Coord { x :: Double, y :: Double }- deriving (Show, Generic)---- While we still have to declare our type as instances of FromJSON--- and ToJSON, we do *not* need to provide bodies for the instances.--- Default versions will be supplied for us.--instance FromJSON Coord-instance ToJSON Coord--main :: IO ()-main = do- let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord- print req- let reply = Coord { x = 123.4, y = 20 }- BL.putStrLn (encode reply)
− examples/Simplest.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--module Main (main) where--import Prelude.Compat--import Control.Applicative (empty)-import Data.Aeson-import Data.Monoid-import qualified Data.ByteString.Lazy.Char8 as BL--data Coord = Coord { x :: Double, y :: Double }- deriving (Show)---- A ToJSON instance allows us to encode a value as JSON.--instance ToJSON Coord where- toJSON (Coord xV yV) = object [ "x" .= xV,- "y" .= yV ]-- toEncoding Coord{..} = pairs $- "x" .= x <>- "y" .= y---- A FromJSON instance allows us to decode a value from JSON. This--- should match the format used by the ToJSON instance.--instance FromJSON Coord where- parseJSON (Object v) = Coord <$>- v .: "x" <*>- v .: "y"- parseJSON _ = empty--main :: IO ()-main = do- let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord- print req- let reply = Coord 123.4 20- BL.putStrLn (encode reply)
− examples/TemplateHaskell.hs
@@ -1,28 +0,0 @@--- We can use Template Haskell (TH) to generate instances of the--- FromJSON and ToJSON classes automatically. This is the fastest way--- to add JSON support for a type.-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TemplateHaskell #-}--module Main (main) where--import Prelude.Compat--import Data.Aeson (decode, encode)-import Data.Aeson.TH (deriveJSON, defaultOptions)-import qualified Data.ByteString.Lazy.Char8 as BL--data Coord = Coord { x :: Double, y :: Double }- deriving (Show)---- This splice will derive instances of ToJSON and FromJSON for us.--$(deriveJSON defaultOptions ''Coord)--main :: IO ()-main = do- let req = decode "{\"x\":3.0,\"y\":-1.0}" :: Maybe Coord- print req- let reply = Coord { x = 123.4, y = 20 }- BL.putStrLn (encode reply)
− examples/Twitter.hs
@@ -1,80 +0,0 @@--- These types follow the format of Twitter search results, as can be--- found in the benchmarks/json-data directory.------ For uses of these types, see the Twitter subdirectory.------ There is one deviation for the sake of convenience: the Geo field--- named "type_" is really named "type" in Twitter's real feed. I--- renamed "type" to "type_" in the *.json files, to avoid overlap--- with a Haskell reserved keyword.--{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Twitter- (- Metadata(..)- , Geo(..)- , Story(..)- , Result(..)- ) where--import Prelude.Compat--import Control.DeepSeq-import Data.Data (Typeable, Data)-import Data.Int (Int64)-import Data.Text (Text)-import GHC.Generics (Generic)--{-# ANN module "Hlint: ignore Use camelCase" #-}-{-# ANN module "Hlint: ignore Use newtype instead of data" #-}--data Metadata = Metadata {- result_type :: Text- } deriving (Eq, Show, Typeable, Data, Generic)--instance NFData Metadata--data Geo = Geo {- type_ :: Text- , coordinates :: (Double, Double)- } deriving (Eq, Show, Typeable, Data, Generic)--instance NFData Geo--data Story = Story {- from_user_id_str :: Text- , profile_image_url :: Text- , created_at :: Text -- ZonedTime- , from_user :: Text- , id_str :: Text- , metadata :: Metadata- , to_user_id :: Maybe Int64- , text :: Text- , id_ :: Int64- , from_user_id :: Int64- , geo :: Maybe Geo- , iso_language_code :: Text- , to_user_id_str :: Maybe Text- , source :: Text- } deriving (Show, Typeable, Data, Generic)--instance NFData Story--data Result = Result {- results :: [Story]- , max_id :: Int64- , since_id :: Int64- , refresh_url :: Text- , next_page :: Text- , results_per_page :: Int- , page :: Int- , completed_in :: Double- , since_id_str :: Text- , max_id_str :: Text- , query :: Text- } deriving (Show, Typeable, Data, Generic)--instance NFData Result
− examples/Twitter/Generic.hs
@@ -1,44 +0,0 @@--- Use GHC generics to automatically generate good instances.--{-# LANGUAGE NoImplicitPrelude #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--module Twitter.Generic- (- Metadata(..)- , Geo(..)- , Story(..)- , Result(..)- ) where--import Prelude.Compat ()--import Twitter-import Twitter.Options--import Data.Aeson (ToJSON (..), FromJSON (..), genericToJSON, genericToEncoding, genericParseJSON)--instance ToJSON Metadata where- toJSON = genericToJSON twitterOptions- toEncoding = genericToEncoding twitterOptions-instance FromJSON Metadata where- parseJSON = genericParseJSON twitterOptions--instance ToJSON Geo where- toJSON = genericToJSON twitterOptions- toEncoding = genericToEncoding twitterOptions-instance FromJSON Geo where- parseJSON = genericParseJSON twitterOptions--instance ToJSON Story where- toJSON = genericToJSON twitterOptions- toEncoding = genericToEncoding twitterOptions-instance FromJSON Story where- parseJSON = genericParseJSON twitterOptions--instance ToJSON Result where- toJSON = genericToJSON twitterOptions- toEncoding = genericToEncoding twitterOptions-instance FromJSON Result where- parseJSON = genericParseJSON twitterOptions
− examples/Twitter/Manual.hs
@@ -1,146 +0,0 @@--- Manually write instances.-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--module Twitter.Manual- (- Metadata(..)- , Geo(..)- , Story(..)- , Result(..)- ) where--import Prelude.Compat--import Control.Applicative-import Data.Semigroup ((<>))-import Twitter--import Data.Aeson hiding (Result)--instance ToJSON Metadata where-- toJSON Metadata{..} = object [- "result_type" .= result_type- ]-- toEncoding Metadata{..} = pairs $- "result_type" .= result_type--instance FromJSON Metadata where- parseJSON (Object v) = Metadata <$> v .: "result_type"- parseJSON _ = empty--instance ToJSON Geo where- toJSON Geo{..} = object [- "type_" .= type_- , "coordinates" .= coordinates- ]-- toEncoding Geo{..} = pairs $- "type_" .= type_- <> "coordinates" .= coordinates--instance FromJSON Geo where- parseJSON (Object v) = Geo <$>- v .: "type_"- <*> v .: "coordinates"- parseJSON _ = empty--instance ToJSON Story where- toJSON Story{..} = object [- "from_user_id_str" .= from_user_id_str- , "profile_image_url" .= profile_image_url- , "created_at" .= created_at- , "from_user" .= from_user- , "id_str" .= id_str- , "metadata" .= metadata- , "to_user_id" .= to_user_id- , "text" .= text- , "id" .= id_- , "from_user_id" .= from_user_id- , "geo" .= geo- , "iso_language_code" .= iso_language_code- , "to_user_id_str" .= to_user_id_str- , "source" .= source- ]-- toEncoding Story{..} = pairs $- "from_user_id_str" .= from_user_id_str- <> "profile_image_url" .= profile_image_url- <> "created_at" .= created_at- <> "from_user" .= from_user- <> "id_str" .= id_str- <> "metadata" .= metadata- <> "to_user_id" .= to_user_id- <> "text" .= text- <> "id" .= id_- <> "from_user_id" .= from_user_id- <> "geo" .= geo- <> "iso_language_code" .= iso_language_code- <> "to_user_id_str" .= to_user_id_str- <> "source" .= source--instance FromJSON Story where- parseJSON (Object v) = Story <$>- v .: "from_user_id_str"- <*> v .: "profile_image_url"- <*> v .: "created_at"- <*> v .: "from_user"- <*> v .: "id_str"- <*> v .: "metadata"- <*> v .: "to_user_id"- <*> v .: "text"- <*> v .: "id"- <*> v .: "from_user_id"- <*> v .: "geo"- <*> v .: "iso_language_code"- <*> v .: "to_user_id_str"- <*> v .: "source"- parseJSON _ = empty--instance ToJSON Result where- toJSON Result{..} = object [- "results" .= results- , "max_id" .= max_id- , "since_id" .= since_id- , "refresh_url" .= refresh_url- , "next_page" .= next_page- , "results_per_page" .= results_per_page- , "page" .= page- , "completed_in" .= completed_in- , "since_id_str" .= since_id_str- , "max_id_str" .= max_id_str- , "query" .= query- ]-- toEncoding Result{..} = pairs $- "results" .= results- <> "max_id" .= max_id- <> "since_id" .= since_id- <> "refresh_url" .= refresh_url- <> "next_page" .= next_page- <> "results_per_page" .= results_per_page- <> "page" .= page- <> "completed_in" .= completed_in- <> "since_id_str" .= since_id_str- <> "max_id_str" .= max_id_str- <> "query" .= query--instance FromJSON Result where- parseJSON (Object v) = Result <$>- v .: "results"- <*> v .: "max_id"- <*> v .: "since_id"- <*> v .: "refresh_url"- <*> v .: "next_page"- <*> v .: "results_per_page"- <*> v .: "page"- <*> v .: "completed_in"- <*> v .: "since_id_str"- <*> v .: "max_id_str"- <*> v .: "query"- parseJSON _ = empty
− examples/Twitter/Options.hs
@@ -1,10 +0,0 @@-module Twitter.Options (module Twitter.Options) where--import Data.Aeson--twitterOptions :: Options-twitterOptions = defaultOptions- { fieldLabelModifier = \x -> case x of- "id_" -> "id"- _ -> x- }
− examples/Twitter/TH.hs
@@ -1,25 +0,0 @@--- Use Template Haskell to generate good instances.--{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE TemplateHaskell #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--module Twitter.TH- (- Metadata(..)- , Geo(..)- , Story(..)- , Result(..)- ) where---import Twitter-import Twitter.Options--import Data.Aeson.TH--$(deriveJSON twitterOptions ''Metadata)-$(deriveJSON twitterOptions ''Geo)-$(deriveJSON twitterOptions ''Story)-$(deriveJSON twitterOptions ''Result)
− examples/aeson-examples.cabal
@@ -1,32 +0,0 @@-name: aeson-examples-version: 0-build-type: Simple-cabal-version: >=1.8--executable aeson-example-generic- main-is: Generic.hs- ghc-options: -Wall- build-depends:- aeson >= 0.10,- base,- base-compat,- bytestring,- ghc-prim--executable aeson-example-simplest- main-is: Simplest.hs- ghc-options: -Wall- build-depends:- aeson,- base,- base-compat,- bytestring--executable aeson-example-th- main-is: TemplateHaskell.hs- ghc-options: -Wall- build-depends:- aeson,- base,- base-compat,- bytestring
tests/ErrorMessages.hs view
@@ -31,6 +31,7 @@ tests = [ aesonGoldenTest "simple" "tests/golden/simple.expected" output , aesonGoldenTest "generic" "tests/golden/generic.expected" (outputGeneric G)+ , aesonGoldenTest "generic" "tests/golden/th.expected" (outputGeneric TH) ] output :: Output@@ -140,7 +141,7 @@ (select thSomeTypeParseJSONRejectUnknownFields gSomeTypeParseJSONRejectUnknownFields)- [ "{\"tag\": \"record\", \"testOne\": 1.0, \"testZero\": 1}"+ [ "{\"tag\": \"record\", \"testone\": 1.0, \"testZero\": 1}" , "{\"testZero\": 1}" , "{\"tag\": \"record\", \"testone\": true, \"testtwo\": null, \"testthree\": null}" ]
tests/UnitTests.hs view
@@ -267,7 +267,7 @@ formatRelativePathExample :: Assertion formatRelativePathExample =- let rhs = formatPath [Key "x", Index 0]+ let rhs = formatRelativePath [Key "x", Index 0] lhs = ".x[0]" in assertEqual "formatRelativePath example" lhs rhs @@ -349,12 +349,14 @@ #endif ] where- assertIsCoerce _ (FromJSONKeyCoerce _) = pure ()- assertIsCoerce n _ = assertFailure n+ assertIsCoerce :: String -> FromJSONKeyFunction a -> Assertion+ assertIsCoerce _ FromJSONKeyCoerce = pure ()+ assertIsCoerce n _ = assertFailure n #if __GLASGOW_HASKELL__ >= 710- assertIsCoerce' _ (FromJSONKeyCoerce _) = pure ()- assertIsCoerce' n _ = pickWithRules (assertFailure n) (pure ())+ assertIsCoerce' :: String -> FromJSONKeyFunction a -> Assertion+ assertIsCoerce' _ FromJSONKeyCoerce = pure ()+ assertIsCoerce' n _ = pickWithRules (assertFailure n) (pure ()) -- | Pick the first when RULES are enabled, e.g. optimisations are on pickWithRules@@ -505,9 +507,6 @@ , "i_string_not_in_unicode_range.json" , "i_string_truncated-utf-8.json" , "i_structure_UTF-8_BOM_empty_object.json"- , "n_string_unescaped_crtl_char.json"- , "n_string_unescaped_newline.json"- , "n_string_unescaped_tab.json" , "string_1_escaped_invalid_codepoint.json" , "string_1_invalid_codepoint.json" , "string_1_invalid_codepoints.json"
tests/golden/generic.expected view
@@ -34,7 +34,7 @@ SomeType (reject unknown fields) Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testZero"] Error in $: parsing Types.SomeType failed, expected Object with key "tag" containing one of ["nullary","unary","product","record","list"], key "tag" not found-Error in $: parsing Types.SomeType(Record) failed, unknown fields: ["testtwo","testone","testthree"]+Error in $.testone: parsing Double failed, unexpected Boolean Foo (reject unknown fields) Error in $: parsing Types.Foo(Foo) failed, unknown fields: ["tag"] Foo (reject unknown fields, tagged single)
+ tests/golden/th.expected view
@@ -0,0 +1,48 @@+OneConstructor+Error in $: When parsing the constructor OneConstructor of type Types.OneConstructor expected Array but got String.+Error in $: When parsing the constructor OneConstructor of type Types.OneConstructor expected an empty Array but got Array of length 1.+Nullary+Error in $: When parsing Types.Nullary expected a String with the tag of a constructor but got X.+Error in $: When parsing Types.Nullary expected String but got Array.+SomeType (tagged)+Error in $: parsing Int failed, expected Number, but encountered Boolean+Error in $: key "contents" not found+Error in $: When parsing the record record of type Types.SomeType the key testone was not present.+Error in $.testone: parsing Double failed, unexpected Boolean+Error in $: When parsing Types.SomeType expected an Object with a tag field where the value is one of [nullary, unary, product, record, list], but got X.+Error in $: key "tag" not found+Error in $: When parsing Types.SomeType expected Object but got Array.+SomeType (single-field)+Error in $: parsing Int failed, expected Number, but encountered Object+Error in $: parsing Int failed, expected Number, but encountered Array+Error in $: When parsing Types.SomeType expected an Object with a single tag/contents pair where the tag is one of [nullary, unary, product, record, list], but got X.+Error in $: When parsing Types.SomeType expected an Object with a single tag/contents pair but got 2 pairs.+Error in $: When parsing Types.SomeType expected an Object with a single tag/contents pair but got 0 pairs.+Error in $: When parsing Types.SomeType expected Object but got Array.+Error in $: not enough input. Expecting ':'+Error in $: not enough input. Expecting object value+Error in $: not enough input. Expecting ',' or '}'+SomeType (two-element array)+Error in $: parsing Int failed, expected Number, but encountered Boolean+Error in $: When parsing the constructor Record of type Types.SomeType expected Object but got Null.+Error in $: When parsing Types.SomeType expected a 2-element Array with a tag and contents element where the tag is one of [nullary, unary, product, record, list], but got X.+Error in $: When parsing Types.SomeType expected an Array of 2 elements where the first element is a String but got Null at the first element.+Error in $: When parsing Types.SomeType expected an Array of 2 elements but got 0 elements+Error in $: When parsing Types.SomeType expected Array but got Object.+Error in $: not enough input. Expecting ',' or ']'+Error in $: not enough input. Expecting json list value+SomeType (reject unknown fields)+Error in $: Unknown fields: ["testZero"]+Error in $: key "tag" not found+Error in $.testone: parsing Double failed, unexpected Boolean+Foo (reject unknown fields)+Error in $: Unknown fields: ["tag"]+Foo (reject unknown fields, tagged single)+Error in $: Unknown fields: ["unknownField"]+EitherTextInt+Error in $: When parsing the constructor NoneNullary of type Types.EitherTextInt expected String but got String.+Error in $: When parsing the constructor NoneNullary of type Types.EitherTextInt expected String but got Array.+Product2 Int Bool+Error in $: expected Bool, but encountered Null+Error in $: When parsing the constructor Product2 of type Types.Product2 expected Array of length 2 but got Array of length 0.+Error in $: When parsing the constructor Product2 of type Types.Product2 expected Array but got Object.