diff --git a/aeson.cabal b/aeson.cabal index 80a28563c..639ebd21b 100644 --- a/aeson.cabal +++ b/aeson.cabal @@ -165,6 +165,7 @@ test-suite aeson-tests PropUtils Regression.Issue351 Regression.Issue571 + Regression.Issue687 Regression.Issue967 SerializationFormatSpec Types @@ -174,9 +175,11 @@ test-suite aeson-tests UnitTests.KeyMapInsertWith UnitTests.MonadFix UnitTests.NullaryConstructors + UnitTests.OmitNothingFieldsNote UnitTests.OptionalFields UnitTests.OptionalFields.Common UnitTests.OptionalFields.Generics + UnitTests.OptionalFields.Manual UnitTests.OptionalFields.TH UnitTests.UTCTime diff --git a/changelog.md b/changelog.md index 0b54378b0..6c77d8e22 100644 --- a/changelog.md +++ b/changelog.md @@ -1,12 +1,30 @@ For the latest version of this document, please see [https://github.com/haskell/aeson/blob/master/changelog.md](https://github.com/haskell/aeson/blob/master/changelog.md). -### 2.2 - -* Use `Data.Aeson.Decoding` parsing functions as default in `Data.Aeson`. -* Move `Data.Aeson.Parser` module into separate `attoparsec-aeson` package, as these parsers are not used by `aeson` itself anymore. -* Remove `cffi` flag. Then the C implementation for string unescaping was used for `text <2` versions. - The new native Haskell implementation introduced in version 2.0.3.0 is at least as fast. -* Drop instances for `attoparsec.Number`. +### 2.2.0.0 + +* Rework how `omitNothingFields` works. Add `allowOmittedFields` as a parsing counterpart. + + New type-class members were added: `omitField :: a -> Bool` to `ToJSON` and `omittedField :: Maybe a` to `FromJSON`. + These control which fields can be omitted. + The `.:?=`, `.:!=` and `.?=` operators were added to make use of these new members. + GHC.Generics and Template Haskell deriving has been updated accordingly. + + In addition to `Maybe` (and `Option`) fields the `Data.Monoid.First` and `Data.Monoid.Last` are also omitted, + as well as the most newtype wrappers, when their wrap omittable type (e.g. newtypes in `Data.Monoid` and `Data.Semigroup`, `Identity`, `Const`, `Tagged`, `Compose`). + Additionall "boring" types like `()` and `Proxy` can be omitted as well. + As the omitting is now uniform, type arguments are also omitted (also in `Generic1` derived instance). + + Resolves issues + [#687](https://github.com/haskell/aeson/issues/687), + [#571](https://github.com/haskell/aeson/issues/571), + [#792](https://github.com/haskell/aeson/issues/792). + +* Use `Data.Aeson.Decoding` parsing functions (introduced in version 2.1.2.0) as default in `Data.Aeson`. +* Move `Data.Aeson.Parser` module into separate [`attoparsec-aeson`](https://hackage.haskell.org/package/attoparsec-aeson) package, as these parsers are not used by `aeson` itself anymore. +* Use [`text-iso8601`](https://hackage.haskell.org/package/text-iso8601) package for parsing `time` types. These are slightly faster than previously used (copy of) `attoparsec-iso8601`. +* Remove `cffi` flag. Toggling the flag made `aeson` use a C implementation for string unescaping (used for `text <2` versions). + The new native Haskell implementation (introduced in version 2.0.3.0) is at least as fast. +* Drop instances for `Number` from `attoparsec` package. * Improve `Arbitrary Value` instance. ### 2.1.2.1 diff --git a/src/Data/Aeson.hs b/src/Data/Aeson.hs index adb2fd283..2c2eb83a2 100644 --- a/src/Data/Aeson.hs +++ b/src/Data/Aeson.hs @@ -76,6 +76,7 @@ module Data.Aeson , fromJSON , ToJSON(..) , KeyValue(..) + , KeyValueOmit(..) , () , JSONPath -- ** Keys for maps @@ -91,14 +92,18 @@ module Data.Aeson -- ** Liftings to unary and binary type constructors , FromJSON1(..) , parseJSON1 + , omittedField1 , FromJSON2(..) , parseJSON2 + , omittedField2 , ToJSON1(..) , toJSON1 , toEncoding1 + , omitField1 , ToJSON2(..) , toJSON2 , toEncoding2 + , omitField2 -- ** Generic JSON classes and options , GFromJSON , FromArgs @@ -123,6 +128,7 @@ module Data.Aeson , constructorTagModifier , allNullaryToStringTag , omitNothingFields + , allowOmittedFields , sumEncoding , unwrapUnaryRecords , tagSingleConstructors @@ -151,6 +157,8 @@ module Data.Aeson , (.:?) , (.:!) , (.!=) + , (.:?=) + , (.:!=) , object -- * Parsing , parseIndexedJSON diff --git a/src/Data/Aeson/Encoding/Internal.hs b/src/Data/Aeson/Encoding/Internal.hs index c04e8e79d..b8ce355a3 100644 --- a/src/Data/Aeson/Encoding/Internal.hs +++ b/src/Data/Aeson/Encoding/Internal.hs @@ -115,7 +115,7 @@ instance Ord (Encoding' a) where compare (Encoding a) (Encoding b) = compare (toLazyByteString a) (toLazyByteString b) --- | @since 2.2 +-- | @since 2.2.0.0 instance IsString (Encoding' a) where fromString = string diff --git a/src/Data/Aeson/TH.hs b/src/Data/Aeson/TH.hs index ef53e277a..088b1fbae 100644 --- a/src/Data/Aeson/TH.hs +++ b/src/Data/Aeson/TH.hs @@ -114,7 +114,6 @@ module Data.Aeson.TH import Data.Aeson.Internal.Prelude -import Data.Bool (bool) import Data.Char (ord) import Data.Aeson (Object, (.:), FromJSON(..), FromJSON1(..), FromJSON2(..), ToJSON(..), ToJSON1(..), ToJSON2(..)) import Data.Aeson.Types (Options(..), Parser, SumEncoding(..), Value(..), defaultOptions, defaultTaggedObject) @@ -124,7 +123,7 @@ import Data.Aeson.Key (Key) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM import Data.Foldable (foldr') -import Data.List (genericLength, intercalate, partition, union) +import Data.List (genericLength, intercalate, union) import Data.List.NonEmpty ((<|), NonEmpty((:|))) import Data.Map (Map) import qualified Data.Monoid as Monoid @@ -321,10 +320,11 @@ consToValue _ _ _ _ [] = consToValue target jc opts instTys cons = autoletE liftSBS $ \letInsert -> do value <- newName "value" + os <- newNameList "_o" $ arityInt jc tjs <- newNameList "_tj" $ arityInt jc tjls <- newNameList "_tjl" $ arityInt jc - let zippedTJs = zip tjs tjls - interleavedTJs = interleave tjs tjls + let zippedTJs = zip3 os tjs tjls + interleavedTJs = flatten3 zippedTJs lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys tvMap = M.fromList $ zip lastTyVars zippedTJs lamE (map varP $ interleavedTJs ++ [value]) $ @@ -461,14 +461,16 @@ argsToValue letInsert target jc tvMap opts multiCons toPair (arg, argTy, fld) = let fieldName = fieldLabel opts fld toValue = dispatchToJSON target jc conName tvMap argTy + + omitFn :: Q Exp omitFn - | omitNothingFields opts = [| omitField |] + | omitNothingFields opts = dispatchOmitField jc conName tvMap argTy | otherwise = [| const False |] - in - [| \f x arg' -> bool x mempty (f arg') |] - `appE` omitFn - `appE` pairE letInsert target fieldName (toValue `appE` arg) - `appE` arg + + in condE + (omitFn `appE` arg) + [| mempty |] + (pairE letInsert target fieldName (toValue `appE` arg)) pairs = mconcatE (map toPair argCons) @@ -653,10 +655,11 @@ consFromJSON _ _ _ _ [] = consFromJSON jc tName opts instTys cons = do value <- newName "value" + os <- newNameList "_o" $ arityInt jc pjs <- newNameList "_pj" $ arityInt jc pjls <- newNameList "_pjl" $ arityInt jc - let zippedPJs = zip pjs pjls - interleavedPJs = interleave pjs pjls + let zippedPJs = zip3 os pjs pjls + interleavedPJs = flatten3 zippedPJs lastTyVars = map varTToName $ drop (length instTys - arityInt jc) instTys tvMap = M.fromList $ zip lastTyVars zippedPJs lamE (map varP $ interleavedPJs ++ [value]) $ lamExpr value tvMap @@ -921,9 +924,11 @@ parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject = (infixApp (conE conName) [|(<$>)|] x) xs where - defVal = case jc of - JSONClass From Arity0 -> [|omittedField|] - _ -> [|Nothing|] + lookupField :: Type -> Q Exp + lookupField argTy + | allowOmittedFields opts = [| lookupFieldOmit |] `appE` dispatchOmittedField jc conName tvMap argTy + | otherwise = [| lookupFieldNoOmit |] + tagFieldNameAppender = if inTaggedObject then (tagFieldName (sumEncoding opts) :) else id knownFields = appE [|KM.fromList|] $ listE $ @@ -940,8 +945,7 @@ parseRecord jc tvMap argTys opts tName conName fields obj inTaggedObject = (appE [|show|] (varE unknownFields))) [] ] - x:xs = [ [|lookupField|] - `appE` defVal + x:xs = [ lookupField argTy `appE` dispatchParseJSON jc conName tvMap argTy `appE` litE (stringL $ show tName) `appE` litE (stringL $ constructorTagModifier opts $ nameBase conName) @@ -1109,9 +1113,8 @@ parseTypeMismatch tName conName expected actual = , actual ] -lookupField :: Maybe a -> (Value -> Parser a) -> String -> String - -> Object -> Key -> Parser a -lookupField maybeDefault pj tName rec obj key = +lookupFieldOmit :: Maybe a -> (Value -> Parser a) -> String -> String -> Object -> Key -> Parser a +lookupFieldOmit maybeDefault pj tName rec obj key = case KM.lookup key obj of Nothing -> case maybeDefault of @@ -1119,6 +1122,12 @@ lookupField maybeDefault pj tName rec obj key = Just x -> pure x Just v -> pj v Key key +lookupFieldNoOmit :: (Value -> Parser a) -> String -> String -> Object -> Key -> Parser a +lookupFieldNoOmit pj tName rec obj key = + case KM.lookup key obj of + Nothing -> unknownFieldFail tName rec (Key.toString key) + Just v -> pj v Key key + unknownFieldFail :: String -> String -> String -> Parser fail unknownFieldFail tName rec key = fail $ printf "When parsing the record %s of type %s the key %s was not present." @@ -1245,20 +1254,26 @@ mkFunCommon consFun jc opts name = do !_ <- buildTypeInstance parentName jc ctxt instTys variant consFun jc parentName opts instTys cons +data FunArg = Omit | Single | Plural deriving (Eq) + dispatchFunByType :: JSONClass -> JSONFun -> Name -> TyVarMap - -> Bool -- True if we are using the function argument that works - -- on lists (e.g., [a] -> Value). False is we are using - -- the function argument that works on single values - -- (e.g., a -> Value). + -> FunArg -- Plural if we are using the function argument that works + -- on lists (e.g., [a] -> Value). Single is we are using + -- the function argument that works on single values + -- (e.g., a -> Value). Omit if we use it to check omission + -- (e.g. a -> Bool) -> Type -> Q Exp dispatchFunByType _ jf _ tvMap list (VarT tyName) = varE $ case M.lookup tyName tvMap of - Just (tfjExp, tfjlExp) -> if list then tfjlExp else tfjExp - Nothing -> jsonFunValOrListName list jf Arity0 + Just (tfjoExp, tfjExp, tfjlExp) -> case list of + Omit -> tfjoExp + Single -> tfjExp + Plural -> tfjlExp + Nothing -> jsonFunValOrListName list jf Arity0 dispatchFunByType jc jf conName tvMap list (SigT ty _) = dispatchFunByType jc jf conName tvMap list ty dispatchFunByType jc jf conName tvMap list (ForallT _ _ ty) = @@ -1277,24 +1292,29 @@ dispatchFunByType jc jf conName tvMap list ty = do tyVarNames :: [Name] tyVarNames = M.keys tvMap + args :: [Q Exp] + args + | list == Omit = map (dispatchFunByType jc jf conName tvMap Omit) rhsArgs + | otherwise = zipWith (dispatchFunByType jc jf conName tvMap) (cycle [Omit,Single,Plural]) (triple rhsArgs) + itf <- isInTypeFamilyApp tyVarNames tyCon tyArgs if any (`mentionsName` tyVarNames) lhsArgs || itf then outOfPlaceTyVarError jc conName else if any (`mentionsName` tyVarNames) rhsArgs - then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs) - : zipWith (dispatchFunByType jc jf conName tvMap) - (cycle [False,True]) - (interleave rhsArgs rhsArgs) + then appsE $ varE (jsonFunValOrListName list jf $ toEnum numLastArgs) : args else varE $ jsonFunValOrListName list jf Arity0 -dispatchToJSON - :: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp -dispatchToJSON target jc n tvMap = - dispatchFunByType jc (targetToJSONFun target) n tvMap False +dispatchToJSON :: ToJSONFun -> JSONClass -> Name -> TyVarMap -> Type -> Q Exp +dispatchToJSON target jc n tvMap = dispatchFunByType jc (targetToJSONFun target) n tvMap Single + +dispatchOmitField :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp +dispatchOmitField jc n tvMap = dispatchFunByType jc ToJSON n tvMap Omit -dispatchParseJSON - :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp -dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap False +dispatchParseJSON :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp +dispatchParseJSON jc n tvMap = dispatchFunByType jc ParseJSON n tvMap Single + +dispatchOmittedField :: JSONClass -> Name -> TyVarMap -> Type -> Q Exp +dispatchOmittedField jc n tvMap = dispatchFunByType jc ParseJSON n tvMap Omit -------------------------------------------------------------------------------- -- Utility functions @@ -1565,13 +1585,14 @@ Both. -- A mapping of type variable Names to their encoding/decoding function Names. -- For example, in a ToJSON2 declaration, a TyVarMap might look like -- --- { a ~> (tj1, tjl1) --- , b ~> (tj2, tjl2) } +-- { a ~> (o1, tj1, tjl1) +-- , b ~> (o2, tj2, tjl2) } -- --- where a and b are the last two type variables of the datatype, tj1 and tjl1 are --- the function arguments of types (a -> Value) and ([a] -> Value), and tj2 and tjl2 --- are the function arguments of types (b -> Value) and ([b] -> Value). -type TyVarMap = Map Name (Name, Name) +-- where a and b are the last two type variables of the datatype, +-- o1 and o2 are function argument of types (a -> Bool), +-- tj1 and tjl1 are the function arguments of types (a -> Value) +-- and ([a] -> Value), and tj2 and tjl2 are the function arguments of types (b -> Value) and ([b] -> Value). +type TyVarMap = Map Name (Name, Name, Name) -- | Returns True if a Type has kind *. hasKindStar :: Type -> Bool @@ -1616,9 +1637,11 @@ varTToNameMaybe _ = Nothing varTToName :: Type -> Name varTToName = fromMaybe (error "Not a type variable!") . varTToNameMaybe -interleave :: [a] -> [a] -> [a] -interleave (a1:a1s) (a2:a2s) = a1:a2:interleave a1s a2s -interleave _ _ = [] +flatten3 :: [(a,a,a)] -> [a] +flatten3 = foldr (\(a,b,c) xs -> a:b:c:xs) [] + +triple :: [a] -> [a] +triple = foldr (\x xs -> x:x:x:xs) [] -- | Fully applies a type constructor to its type variables. applyTyCon :: Name -> [Type] -> Type @@ -1909,6 +1932,17 @@ jsonClassName (JSONClass From Arity0) = ''FromJSON jsonClassName (JSONClass From Arity1) = ''FromJSON1 jsonClassName (JSONClass From Arity2) = ''FromJSON2 +jsonFunOmitName :: JSONFun -> Arity -> Name +jsonFunOmitName ToJSON Arity0 = 'omitField +jsonFunOmitName ToJSON Arity1 = 'liftOmitField +jsonFunOmitName ToJSON Arity2 = 'liftOmitField2 +jsonFunOmitName ToEncoding Arity0 = 'omitField +jsonFunOmitName ToEncoding Arity1 = 'liftOmitField +jsonFunOmitName ToEncoding Arity2 = 'liftOmitField2 +jsonFunOmitName ParseJSON Arity0 = 'omittedField +jsonFunOmitName ParseJSON Arity1 = 'liftOmittedField +jsonFunOmitName ParseJSON Arity2 = 'liftOmittedField2 + jsonFunValName :: JSONFun -> Arity -> Name jsonFunValName ToJSON Arity0 = 'toJSON jsonFunValName ToJSON Arity1 = 'liftToJSON @@ -1931,10 +1965,11 @@ jsonFunListName ParseJSON Arity0 = 'parseJSONList jsonFunListName ParseJSON Arity1 = 'liftParseJSONList jsonFunListName ParseJSON Arity2 = 'liftParseJSONList2 -jsonFunValOrListName :: Bool -- e.g., toJSONList if True, toJSON if False +jsonFunValOrListName :: FunArg -- e.g., toJSONList if True, toJSON if False -> JSONFun -> Arity -> Name -jsonFunValOrListName False = jsonFunValName -jsonFunValOrListName True = jsonFunListName +jsonFunValOrListName Omit = jsonFunOmitName +jsonFunValOrListName Single = jsonFunValName +jsonFunValOrListName Plural = jsonFunListName arityInt :: JSONClass -> Int arityInt = fromEnum . arity diff --git a/src/Data/Aeson/Types.hs b/src/Data/Aeson/Types.hs index f4b935b95..5b5566165 100644 --- a/src/Data/Aeson/Types.hs +++ b/src/Data/Aeson/Types.hs @@ -50,6 +50,7 @@ module Data.Aeson.Types -- ** Encoding , ToJSON(..) , KeyValue(..) + , KeyValueOmit(..) -- ** Keys for maps , ToJSONKey(..) @@ -72,14 +73,18 @@ module Data.Aeson.Types -- ** Liftings to unary and binary type constructors , FromJSON1(..) , parseJSON1 + , omittedField1 , FromJSON2(..) , parseJSON2 + , omittedField2 , ToJSON1(..) , toJSON1 , toEncoding1 + , omitField1 , ToJSON2(..) , toJSON2 , toEncoding2 + , omitField2 -- ** Generic JSON classes , GFromJSON @@ -111,13 +116,19 @@ module Data.Aeson.Types , (.:?) , (.:!) , (.!=) + , (.:?=) + , (.:!=) , object , parseField , parseFieldMaybe , parseFieldMaybe' + , parseFieldOmit + , parseFieldOmit' , explicitParseField , explicitParseFieldMaybe , explicitParseFieldMaybe' + , explicitParseFieldOmit + , explicitParseFieldOmit' , listEncoding , listValue @@ -132,6 +143,7 @@ module Data.Aeson.Types , constructorTagModifier , allNullaryToStringTag , omitNothingFields + , allowOmittedFields , sumEncoding , unwrapUnaryRecords , tagSingleConstructors diff --git a/src/Data/Aeson/Types/Class.hs b/src/Data/Aeson/Types/Class.hs index ecdb3fb7d..5aec453df 100644 --- a/src/Data/Aeson/Types/Class.hs +++ b/src/Data/Aeson/Types/Class.hs @@ -26,14 +26,18 @@ module Data.Aeson.Types.Class -- * Liftings to unary and binary type constructors , FromJSON1(..) , parseJSON1 + , omittedField1 , FromJSON2(..) , parseJSON2 + , omittedField2 , ToJSON1(..) , toJSON1 , toEncoding1 + , omitField1 , ToJSON2(..) , toJSON2 , toEncoding2 + , omitField2 -- * Generic JSON classes , GFromJSON(..) , FromArgs(..) @@ -67,6 +71,7 @@ module Data.Aeson.Types.Class , genericFromJSONKey -- * Object key-value pairs , KeyValue(..) + , KeyValueOmit(..) -- * List functions , listEncoding @@ -89,14 +94,20 @@ module Data.Aeson.Types.Class , parseField , parseFieldMaybe , parseFieldMaybe' + , parseFieldOmit + , parseFieldOmit' , explicitParseField , explicitParseFieldMaybe , explicitParseFieldMaybe' + , explicitParseFieldOmit + , explicitParseFieldOmit' -- ** Operators , (.:) , (.:?) , (.:!) , (.!=) + , (.:?=) + , (.:!=) ) where diff --git a/src/Data/Aeson/Types/FromJSON.hs b/src/Data/Aeson/Types/FromJSON.hs index 3a3454683..074ff113c 100644 --- a/src/Data/Aeson/Types/FromJSON.hs +++ b/src/Data/Aeson/Types/FromJSON.hs @@ -10,6 +10,7 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE UndecidableInstances #-} @@ -25,8 +26,10 @@ module Data.Aeson.Types.FromJSON -- * Liftings to unary and binary type constructors , FromJSON1(..) , parseJSON1 + , omittedField1 , FromJSON2(..) , parseJSON2 + , omittedField2 -- * Generic JSON classes , GFromJSON(..) , FromArgs(..) @@ -61,22 +64,28 @@ module Data.Aeson.Types.FromJSON , parseField , parseFieldMaybe , parseFieldMaybe' + , parseFieldOmit + , parseFieldOmit' , explicitParseField , explicitParseFieldMaybe , explicitParseFieldMaybe' + , explicitParseFieldOmit + , explicitParseFieldOmit' , parseIndexedJSON -- ** Operators , (.:) , (.:?) , (.:!) , (.!=) + , (.:?=) + , (.:!=) -- * Internal , parseOptionalFieldWith ) where import Data.Aeson.Internal.Prelude -import Control.Monad (zipWithM) +import Control.Monad (zipWithM, guard) import Data.Aeson.Internal.Functions (mapKey, mapKeyO) import Data.Aeson.Internal.Scientific import Data.Aeson.Types.Generic @@ -236,11 +245,11 @@ class GFromJSON arity f where gParseJSON :: Options -> FromArgs arity a -> Value -> Parser (f a) -- | A 'FromArgs' value either stores nothing (for 'FromJSON') or it stores the --- two function arguments that decode occurrences of the type parameter (for +-- three function arguments that decode occurrences of the type parameter (for -- 'FromJSON1'). data FromArgs arity a where NoFromArgs :: FromArgs Zero a - From1Args :: (Value -> Parser a) -> (Value -> Parser [a]) -> FromArgs One a + From1Args :: Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> FromArgs One a -- | A configurable generic JSON decoder. This function applied to -- 'defaultOptions' is used as the default for 'parseJSON' when the @@ -253,9 +262,9 @@ genericParseJSON opts = fmap to . gParseJSON opts NoFromArgs -- 'defaultOptions' is used as the default for 'liftParseJSON' when the -- type is an instance of 'Generic1'. genericLiftParseJSON :: (Generic1 f, GFromJSON One (Rep1 f)) - => Options -> (Value -> Parser a) -> (Value -> Parser [a]) + => Options -> Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) -genericLiftParseJSON opts pj pjl = fmap to1 . gParseJSON opts (From1Args pj pjl) +genericLiftParseJSON opts o pj pjl = fmap to1 . gParseJSON opts (From1Args o pj pjl) ------------------------------------------------------------------------------- -- Class @@ -379,29 +388,10 @@ class FromJSON a where $ a -- | Default value for optional fields. + -- Used by @('.:?=')@ operator, and Generics and TH deriving + -- with @'allowOmittedFields' = True@ (default). -- - -- Defining @omittedField = 'Just' x@ makes object fields of this type optional. - -- When the field is omitted, the default value @x@ will be used. - -- - -- @ - -- newtype A = A Int deriving (Generic) - -- instance FromJSON A where omittedField = Just (A 0) - -- - -- data R = R { a :: A, b :: Int } deriving ('Generic', 'FromJSON') - -- - -- decode "{\"b\":1}" -- Just (R (A 0) 1) - -- @ - -- - -- Defining @omittedField = 'Nothing'@ makes object fields of this type required. - -- - -- @ - -- omittedField :: Maybe Int -- Nothing - -- decode "{\"a\":1}" -- Nothing - -- @ - -- - -- The default implementation is @omittedField = Nothing@. - -- - -- @since x.x.x.x + -- @since 2.2.0.0 omittedField :: Maybe a omittedField = Nothing @@ -617,25 +607,32 @@ typeOf v = case v of -- 'liftParseJSON' = 'genericLiftParseJSON' customOptions -- @ class FromJSON1 f where - liftParseJSON :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) + liftParseJSON :: Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) default liftParseJSON :: (Generic1 f, GFromJSON One (Rep1 f)) - => (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) + => Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) liftParseJSON = genericLiftParseJSON defaultOptions - liftParseJSONList :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser [f a] - liftParseJSONList f g v = listParser (liftParseJSON f g) v + liftParseJSONList :: Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser [f a] + liftParseJSONList o f g v = listParser (liftParseJSON o f g) v + + liftOmittedField :: Maybe a -> Maybe (f a) + liftOmittedField _ = Nothing -- | @since 2.1.0.0 instance (Generic1 f, GFromJSON One (Rep1 f)) => FromJSON1 (Generically1 f) where - liftParseJSON :: forall a. (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (Generically1 f a) - liftParseJSON = coerce (genericLiftParseJSON defaultOptions :: (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)) + liftParseJSON :: forall a. Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (Generically1 f a) + liftParseJSON = coerce (genericLiftParseJSON defaultOptions :: Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a)) -- | Lift the standard 'parseJSON' function through the type constructor. parseJSON1 :: (FromJSON1 f, FromJSON a) => Value -> Parser (f a) -parseJSON1 = liftParseJSON parseJSON parseJSONList +parseJSON1 = liftParseJSON omittedField parseJSON parseJSONList {-# INLINE parseJSON1 #-} +-- | @since 2.2.0.0 +omittedField1 :: (FromJSON1 f, FromJSON a) => Maybe (f a) +omittedField1 = liftOmittedField omittedField + -- | Lifting of the 'FromJSON' class to binary type constructors. -- -- Instead of manually writing your 'FromJSON2' instance, "Data.Aeson.TH" @@ -645,25 +642,36 @@ parseJSON1 = liftParseJSON parseJSON parseJSONList -- unlike 'parseJSON' and 'liftParseJSON'. class FromJSON2 f where liftParseJSON2 - :: (Value -> Parser a) + :: Maybe a + -> (Value -> Parser a) -> (Value -> Parser [a]) + -> Maybe b -> (Value -> Parser b) -> (Value -> Parser [b]) -> Value -> Parser (f a b) liftParseJSONList2 - :: (Value -> Parser a) + :: Maybe a + -> (Value -> Parser a) -> (Value -> Parser [a]) + -> Maybe b -> (Value -> Parser b) -> (Value -> Parser [b]) -> Value -> Parser [f a b] - liftParseJSONList2 fa ga fb gb = withArray "[]" $ \vals -> - fmap V.toList (V.mapM (liftParseJSON2 fa ga fb gb) vals) + liftParseJSONList2 oa fa ga ob fb gb = withArray "[]" $ \vals -> + fmap V.toList (V.mapM (liftParseJSON2 oa fa ga ob fb gb) vals) + + liftOmittedField2 :: Maybe a -> Maybe b -> Maybe (f a b) + liftOmittedField2 _ _ = Nothing -- | Lift the standard 'parseJSON' function through the type constructor. parseJSON2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Value -> Parser (f a b) -parseJSON2 = liftParseJSON2 parseJSON parseJSONList parseJSON parseJSONList +parseJSON2 = liftParseJSON2 omittedField parseJSON parseJSONList omittedField parseJSON parseJSONList {-# INLINE parseJSON2 #-} +-- | @since 2.2.0.0 +omittedField2 :: (FromJSON2 f, FromJSON a, FromJSON b) => Maybe (f a b) +omittedField2 = liftOmittedField2 omittedField omittedField + ------------------------------------------------------------------------------- -- List functions ------------------------------------------------------------------------------- @@ -679,7 +687,7 @@ listParser _ v = typeMismatch "Array" v ------------------------------------------------------------------------------- instance FromJSON1 [] where - liftParseJSON _ p' = p' + liftParseJSON _ _ p' = p' instance (FromJSON a) => FromJSON [a] where parseJSON = parseJSON1 @@ -854,6 +862,25 @@ ifromJSON = iparse parseJSON (.:!) :: (FromJSON a) => Object -> Key -> Parser (Maybe a) (.:!) = explicitParseFieldMaybe' parseJSON +-- | Retrieve the value associated with the given key of an 'Object'. +-- If the key is not present and the 'omittedField' is @'Just' x@ for some @x@, +-- the result will be that @x@. +-- +-- @since 2.2.0.0 +(.:?=) :: (FromJSON a) => Object -> Key -> Parser a +(.:?=) = explicitParseFieldOmit omittedField parseJSON + +-- | Retrieve the value associated with the given key of an 'Object'. +-- If the key is not present or the field is @null@ and the 'omittedField' is @'Just' x@ for some @x@, +-- the result will be that @x@. +-- +-- This differs from '.:?=' by attempting to parse 'Null' the same as any +-- other JSON value, instead of using 'omittedField' when it's 'Just'. +-- +-- @since 2.2.0.0 +(.:!=) :: (FromJSON a) => Object -> Key -> Parser a +(.:!=) = explicitParseFieldOmit' omittedField parseJSON + -- | Function variant of '.:'. parseField :: (FromJSON a) => Object -> Key -> Parser a parseField = (.:) @@ -866,6 +893,18 @@ parseFieldMaybe = (.:?) parseFieldMaybe' :: (FromJSON a) => Object -> Key -> Parser (Maybe a) parseFieldMaybe' = (.:!) +-- | Function variant of '.:?='. +-- +-- @since 2.2.0.0 +parseFieldOmit :: (FromJSON a) => Object -> Key -> Parser a +parseFieldOmit = (.:?=) + +-- | Function variant of '.:!='. +-- +-- @since 2.2.0.0 +parseFieldOmit' :: (FromJSON a) => Object -> Key -> Parser a +parseFieldOmit' = (.:!=) + -- | Variant of '.:' with explicit parser function. -- -- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@ @@ -878,7 +917,7 @@ explicitParseField p obj key = case KM.lookup key obj of explicitParseFieldMaybe :: (Value -> Parser a) -> Object -> Key -> Parser (Maybe a) explicitParseFieldMaybe p obj key = case KM.lookup key obj of Nothing -> pure Nothing - Just v -> liftParseJSON p (listParser p) v Key key -- listParser isn't used by maybe instance. + Just v -> liftParseJSON Nothing p (listParser p) v Key key -- listParser isn't used by maybe instance. -- | Variant of '.:!' with explicit parser function. explicitParseFieldMaybe' :: (Value -> Parser a) -> Object -> Key -> Parser (Maybe a) @@ -886,6 +925,20 @@ explicitParseFieldMaybe' p obj key = case KM.lookup key obj of Nothing -> pure Nothing Just v -> Just <$> p v Key key +-- | Variant of '.:?=' with explicit arguments. +-- +-- @since 2.2.0.0 +explicitParseFieldOmit :: Maybe a -> (Value -> Parser a) -> Object -> Key -> Parser a +explicitParseFieldOmit Nothing p obj key = explicitParseField p obj key +explicitParseFieldOmit (Just def) p obj key = explicitParseFieldMaybe p obj key .!= def + +-- | Variant of '.:!=' with explicit arguments. +-- +-- @since 2.2.0.0 +explicitParseFieldOmit' :: Maybe a -> (Value -> Parser a) -> Object -> Key -> Parser a +explicitParseFieldOmit' Nothing p obj key = explicitParseField p obj key +explicitParseFieldOmit' (Just def) p obj key = explicitParseFieldMaybe' p obj key .!= def + -- | Helper for use in combination with '.:?' to provide default -- values for optional JSON object fields. -- @@ -958,22 +1011,25 @@ instance (FromJSON a) => GFromJSON arity (K1 i a) where instance GFromJSON One Par1 where -- Direct occurrences of the last type parameter are decoded with the -- function passed in as an argument: - gParseJSON _opts (From1Args pj _) = fmap Par1 . pj + gParseJSON _opts (From1Args _ pj _) = fmap Par1 . pj {-# INLINE gParseJSON #-} instance (FromJSON1 f) => GFromJSON One (Rec1 f) where -- Recursive occurrences of the last type parameter are decoded using their -- FromJSON1 instance: - gParseJSON _opts (From1Args pj pjl) = fmap Rec1 . liftParseJSON pj pjl + gParseJSON _opts (From1Args o pj pjl) = fmap Rec1 . liftParseJSON o pj pjl {-# INLINE gParseJSON #-} instance (FromJSON1 f, GFromJSON One g) => GFromJSON One (f :.: g) where -- If an occurrence of the last type parameter is nested inside two -- composed types, it is decoded by using the outermost type's FromJSON1 -- instance to generically decode the innermost type: + -- + -- Note: the ommitedField is not passed here. + -- This might be related for :.: associated the wrong way in Generics Rep. gParseJSON opts fargs = - let gpj = gParseJSON opts fargs in - fmap Comp1 . liftParseJSON gpj (listParser gpj) + let gpj = gParseJSON opts fargs + in fmap Comp1 . liftParseJSON Nothing gpj (listParser gpj) {-# INLINE gParseJSON #-} -------------------------------------------------------------------------------- @@ -1369,22 +1425,32 @@ instance {-# OVERLAPPABLE #-} instance (Selector s, FromJSON a, Generic a, K1 i a ~ Rep a) => RecordFromJSON' arity (S1 s (K1 i a)) where - recordParseJSON' args obj = - recordParseJSONImpl (fmap K1 omittedField) gParseJSON args obj + recordParseJSON' args@(_ :* _ :* opts :* _) obj = + recordParseJSONImpl (guard (allowOmittedFields opts) >> fmap K1 omittedField) gParseJSON args obj {-# INLINE recordParseJSON' #-} instance {-# OVERLAPPING #-} (Selector s, FromJSON a) => RecordFromJSON' arity (S1 s (Rec0 a)) where - recordParseJSON' args obj = - recordParseJSONImpl (fmap K1 omittedField) gParseJSON args obj + recordParseJSON' args@(_ :* _ :* opts :* _) obj = + recordParseJSONImpl (guard (allowOmittedFields opts) >> fmap K1 omittedField) gParseJSON args obj {-# INLINE recordParseJSON' #-} -instance (Selector s, GFromJSON arity (Rec1 f), FromJSON1 f) => - RecordFromJSON' arity (S1 s (Rec1 f)) where - recordParseJSON' args obj = recordParseJSONImpl Nothing gParseJSON args obj +instance {-# OVERLAPPING #-} + (Selector s, GFromJSON One (Rec1 f), FromJSON1 f) => + RecordFromJSON' One (S1 s (Rec1 f)) where + recordParseJSON' args@(_ :* _ :* opts :* From1Args o _ _) obj = + recordParseJSONImpl (guard (allowOmittedFields opts) >> fmap Rec1 (liftOmittedField o)) gParseJSON args obj {-# INLINE recordParseJSON' #-} +instance {-# OVERLAPPING #-} + (Selector s, GFromJSON One Par1) => + RecordFromJSON' One (S1 s Par1) where + recordParseJSON' args@(_ :* _ :* opts :* From1Args o _ _) obj = + recordParseJSONImpl (guard (allowOmittedFields opts) >> fmap Par1 o) gParseJSON args obj + {-# INLINE recordParseJSON' #-} + + recordParseJSONImpl :: forall s arity a f i . (Selector s) => Maybe (f a) @@ -1527,28 +1593,33 @@ instance {-# OVERLAPPING #-} instance FromJSON2 Const where - liftParseJSON2 p _ _ _ = fmap Const . p + liftParseJSON2 _ p _ _ _ _ = coerce p + liftOmittedField2 o _ = coerce o instance FromJSON a => FromJSON1 (Const a) where - liftParseJSON _ _ = fmap Const . parseJSON + liftParseJSON _ _ _ = coerce (parseJSON @a) + liftOmittedField _ = coerce (omittedField @a) instance FromJSON a => FromJSON (Const a b) where - parseJSON = fmap Const . parseJSON + parseJSON = coerce (parseJSON @a) + omittedField = coerce (omittedField @a) instance (FromJSON a, FromJSONKey a) => FromJSONKey (Const a b) where - fromJSONKey = fmap Const fromJSONKey + fromJSONKey = coerce (fromJSONKey @a) instance FromJSON1 Maybe where - liftParseJSON _ _ Null = pure Nothing - liftParseJSON p _ a = Just <$> p a + liftParseJSON _ _ _ Null = pure Nothing + liftParseJSON _ p _ a = Just <$> p a + + liftOmittedField _ = Just Nothing instance (FromJSON a) => FromJSON (Maybe a) where parseJSON = parseJSON1 - omittedField = Just Nothing + omittedField = omittedField1 instance FromJSON2 Either where - liftParseJSON2 pA _ pB _ (Object (KM.toList -> [(key, value)])) + liftParseJSON2 _ pA _ _ pB _ (Object (KM.toList -> [(key, value)])) | key == left = Left <$> pA value Key left | key == right = Right <$> pB value Key right where @@ -1556,13 +1627,13 @@ instance FromJSON2 Either where left = "Left" right = "Right" - liftParseJSON2 _ _ _ _ _ = fail $ + liftParseJSON2 _ _ _ _ _ _ _ = fail $ "expected an object with a single property " ++ "where the property key should be either " ++ "\"Left\" or \"Right\"" instance (FromJSON a) => FromJSON1 (Either a) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where parseJSON = parseJSON2 @@ -1595,6 +1666,7 @@ instance FromJSON Ordering where instance FromJSON () where parseJSON _ = pure () + omittedField = Just () instance FromJSON Char where parseJSON = withText "Char" parseChar @@ -1783,7 +1855,7 @@ parseVersionText = go . readP_to_S parseVersion . unpack ------------------------------------------------------------------------------- instance FromJSON1 NonEmpty where - liftParseJSON p _ = withArray "NonEmpty" $ + liftParseJSON _ p _ = withArray "NonEmpty" $ (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList where ne [] = fail "parsing NonEmpty failed, unexpected empty list" @@ -1804,7 +1876,7 @@ instance FromJSON Scientific where ------------------------------------------------------------------------------- instance FromJSON1 DList.DList where - liftParseJSON p _ = withArray "DList" $ + liftParseJSON _ p _ = withArray "DList" $ fmap DList.fromList . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList @@ -1813,7 +1885,7 @@ instance (FromJSON a) => FromJSON (DList.DList a) where -- | @since 1.5.3.0 instance FromJSON1 DNE.DNonEmpty where - liftParseJSON p _ = withArray "DNonEmpty" $ + liftParseJSON _ p _ = withArray "DNonEmpty" $ (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList where ne [] = fail "parsing DNonEmpty failed, unexpected empty list" @@ -1829,13 +1901,13 @@ instance (FromJSON a) => FromJSON (DNE.DNonEmpty a) where -- | @since 2.0.2.0 instance FromJSON1 Solo where - liftParseJSON p _ a = Solo <$> p a - liftParseJSONList _ p a = fmap Solo <$> p a + liftParseJSON _ p _ a = Solo <$> p a + liftParseJSONList _ _ p a = fmap Solo <$> p a -- | @since 2.0.2.0 instance (FromJSON a) => FromJSON (Solo a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList -- | @since 2.0.2.0 instance (FromJSONKey a) => FromJSONKey (Solo a) where @@ -1847,14 +1919,18 @@ instance (FromJSONKey a) => FromJSONKey (Solo a) where ------------------------------------------------------------------------------- instance FromJSON1 Identity where - liftParseJSON p _ a = Identity <$> p a + liftParseJSON _ p _ a = coerce (p a) + + liftParseJSONList _ _ p a = coerce (p a) - liftParseJSONList _ p a = fmap Identity <$> p a + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Identity a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList + + omittedField = coerce (omittedField @a) instance (FromJSONKey a) => FromJSONKey (Identity a) where fromJSONKey = coerceFromJSONKeyFunction (fromJSONKey :: FromJSONKeyFunction a) @@ -1862,44 +1938,45 @@ instance (FromJSONKey a) => FromJSONKey (Identity a) where instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Compose f g) where - liftParseJSON p pl a = Compose <$> liftParseJSON g gl a + liftParseJSON o p pl a = coerce (liftParseJSON @f (liftOmittedField o) g gl a) where - g = liftParseJSON p pl - gl = liftParseJSONList p pl + g = liftParseJSON @g o p pl + gl = liftParseJSONList @g o p pl - liftParseJSONList p pl a = map Compose <$> liftParseJSONList g gl a + liftParseJSONList o p pl a = coerce (liftParseJSONList @f (liftOmittedField o) g gl a) where - g = liftParseJSON p pl - gl = liftParseJSONList p pl + g = liftParseJSON @g o p pl + gl = liftParseJSONList @g o p pl instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Compose f g a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList - + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Product f g) where - liftParseJSON p pl a = uncurry Pair <$> liftParseJSON2 px pxl py pyl a + liftParseJSON o p pl a = uncurry Pair <$> liftParseJSON2 ox px pxl oy py pyl a where - px = liftParseJSON p pl - pxl = liftParseJSONList p pl - py = liftParseJSON p pl - pyl = liftParseJSONList p pl + ox = liftOmittedField o + px = liftParseJSON o p pl + pxl = liftParseJSONList o p pl + oy = liftOmittedField o + py = liftParseJSON o p pl + pyl = liftParseJSONList o p pl instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Product f g a) where parseJSON = parseJSON1 instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (Sum f g) where - liftParseJSON p pl (Object (KM.toList -> [(key, value)])) - | key == inl = InL <$> liftParseJSON p pl value Key inl - | key == inr = InR <$> liftParseJSON p pl value Key inr + liftParseJSON o p pl (Object (KM.toList -> [(key, value)])) + | key == inl = InL <$> liftParseJSON o p pl value Key inl + | key == inr = InR <$> liftParseJSON o p pl value Key inr where inl, inr :: Key inl = "InL" inr = "InR" - liftParseJSON _ _ _ = fail $ + liftParseJSON _ _ _ _ = fail $ "parsing Sum failed, expected an object with a single property " ++ "where the property key should be either " ++ "\"InL\" or \"InR\"" @@ -1912,7 +1989,7 @@ instance (FromJSON1 f, FromJSON1 g, FromJSON a) => FromJSON (Sum f g a) where ------------------------------------------------------------------------------- instance FromJSON1 Seq.Seq where - liftParseJSON p _ = withArray "Seq" $ + liftParseJSON _ p _ = withArray "Seq" $ fmap Seq.fromList . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList @@ -1929,18 +2006,19 @@ instance FromJSON IntSet.IntSet where instance FromJSON1 IntMap.IntMap where - liftParseJSON p pl = fmap IntMap.fromList . liftParseJSON p' pl' + liftParseJSON o p pl = fmap IntMap.fromList . liftParseJSON o' p' pl' where - p' = liftParseJSON2 parseJSON parseJSONList p pl - pl' = liftParseJSONList2 parseJSON parseJSONList p pl + o' = liftOmittedField o + p' = liftParseJSON o p pl + pl' = liftParseJSONList o p pl instance FromJSON a => FromJSON (IntMap.IntMap a) where parseJSON = fmap IntMap.fromList . parseJSON instance (FromJSONKey k, Ord k) => FromJSON1 (M.Map k) where - liftParseJSON :: forall a. (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (M.Map k a) - liftParseJSON p _ = case fromJSONKey of + liftParseJSON :: forall a. Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (M.Map k a) + liftParseJSON _ p _ = case fromJSONKey of FromJSONKeyCoerce -> withObject "Map ~Text" $ case Key.coercionToText of Nothing -> text coerce Just Coercion -> case KM.coercionToMap of @@ -1971,12 +2049,13 @@ instance (FromJSONKey k, Ord k, FromJSON v) => FromJSON (M.Map k v) where instance FromJSON1 Tree.Tree where - liftParseJSON p pl = go + liftParseJSON o p pl = go where - go v = uncurry Tree.Node <$> liftParseJSON2 p pl p' pl' v + go v = uncurry Tree.Node <$> liftParseJSON2 o p pl o' p' pl' v - p' = liftParseJSON go (listParser go) - pl'= liftParseJSONList go (listParser go) + o' = Nothing + p' = liftParseJSON Nothing go (listParser go) + pl'= liftParseJSONList Nothing go (listParser go) instance (FromJSON v) => FromJSON (Tree.Tree v) where parseJSON = parseJSON1 @@ -1998,7 +2077,7 @@ instance FromJSONKey UUID.UUID where ------------------------------------------------------------------------------- instance FromJSON1 Vector where - liftParseJSON p _ = withArray "Vector" $ + liftParseJSON _ p _ = withArray "Vector" $ V.mapM (uncurry $ parseIndexedJSON p) . V.indexed instance (FromJSON a) => FromJSON (Vector a) where @@ -2026,8 +2105,8 @@ instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where instance (FromJSONKey k, Eq k, Hashable k) => FromJSON1 (H.HashMap k) where - liftParseJSON :: forall a. (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (H.HashMap k a) - liftParseJSON p _ = case fromJSONKey of + liftParseJSON :: forall a. Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (H.HashMap k a) + liftParseJSON _ p _ = case fromJSONKey of FromJSONKeyCoerce -> withObject "HashMap ~Text" $ case Key.coercionToText of Nothing -> text coerce Just Coercion -> case KM.coercionToHashMap of @@ -2077,7 +2156,7 @@ instance FromJSONKey Key where -- | @since 2.0.1.0 instance FromJSON1 KM.KeyMap where - liftParseJSON p _ = withObject "KeyMap" $ \obj -> + liftParseJSON _ p _ = withObject "KeyMap" $ \obj -> traverse p obj -- | @since 2.0.1.0 @@ -2232,87 +2311,100 @@ instance FromJSONKey Month where ------------------------------------------------------------------------------- instance FromJSON1 Monoid.Dual where - liftParseJSON p _ = fmap Monoid.Dual . p + liftParseJSON _ p _ = coerce p + + liftOmittedField = coerce instance FromJSON a => FromJSON (Monoid.Dual a) where parseJSON = parseJSON1 instance FromJSON1 Monoid.First where - liftParseJSON p p' = fmap Monoid.First . liftParseJSON p p' + liftParseJSON o = coerce (liftParseJSON @Maybe o) + liftOmittedField _ = Just (Monoid.First Nothing) instance FromJSON a => FromJSON (Monoid.First a) where parseJSON = parseJSON1 - + omittedField = omittedField1 instance FromJSON1 Monoid.Last where - liftParseJSON p p' = fmap Monoid.Last . liftParseJSON p p' + liftParseJSON o = coerce (liftParseJSON @Maybe o) + liftOmittedField _ = Just (Monoid.Last Nothing) instance FromJSON a => FromJSON (Monoid.Last a) where parseJSON = parseJSON1 - + omittedField = omittedField1 instance FromJSON1 Semigroup.Min where - liftParseJSON p _ a = Semigroup.Min <$> p a + liftParseJSON _ p _ a = coerce (p a) + + liftParseJSONList _ _ p a = coerce (p a) - liftParseJSONList _ p a = fmap Semigroup.Min <$> p a + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Semigroup.Min a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList + omittedField = omittedField1 instance FromJSON1 Semigroup.Max where - liftParseJSON p _ a = Semigroup.Max <$> p a + liftParseJSON _ p _ a = coerce (p a) - liftParseJSONList _ p a = fmap Semigroup.Max <$> p a + liftParseJSONList _ _ p a = coerce (p a) + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Semigroup.Max a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList - + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList + omittedField = omittedField1 instance FromJSON1 Semigroup.First where - liftParseJSON p _ a = Semigroup.First <$> p a + liftParseJSON _ p _ a = coerce (p a) - liftParseJSONList _ p a = fmap Semigroup.First <$> p a + liftParseJSONList _ _ p a = coerce (p a) + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Semigroup.First a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList instance FromJSON1 Semigroup.Last where - liftParseJSON p _ a = Semigroup.Last <$> p a + liftParseJSON _ p _ a = coerce (p a) - liftParseJSONList _ p a = fmap Semigroup.Last <$> p a + liftParseJSONList _ _ p a = coerce (p a) + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Semigroup.Last a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList - + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList + omittedField = omittedField1 instance FromJSON1 Semigroup.WrappedMonoid where - liftParseJSON p _ a = Semigroup.WrapMonoid <$> p a + liftParseJSON _ p _ a = coerce (p a) - liftParseJSONList _ p a = fmap Semigroup.WrapMonoid <$> p a + liftParseJSONList _ _ p a = coerce (p a) + liftOmittedField = coerce instance (FromJSON a) => FromJSON (Semigroup.WrappedMonoid a) where parseJSON = parseJSON1 - parseJSONList = liftParseJSONList parseJSON parseJSONList + parseJSONList = liftParseJSONList omittedField parseJSON parseJSONList + omittedField = omittedField1 #if !MIN_VERSION_base(4,16,0) instance FromJSON1 Semigroup.Option where - liftParseJSON p p' = fmap Semigroup.Option . liftParseJSON p p' + liftParseJSON o = coerce (liftParseJSON @Maybe o) + liftOmittedField _ = Just (Semigroup.Option Nothing) instance FromJSON a => FromJSON (Semigroup.Option a) where parseJSON = parseJSON1 - omittedField = Just (Semigroup.Option Nothing) + omittedField = omittedField1 #endif ------------------------------------------------------------------------------- @@ -2321,7 +2413,7 @@ instance FromJSON a => FromJSON (Semigroup.Option a) where -- | @since 1.5.3.0 instance FromJSON1 f => FromJSON (F.Fix f) where - parseJSON = go where go = fmap F.Fix . liftParseJSON go parseJSONList + parseJSON = go where go = coerce (liftParseJSON @f Nothing go parseJSONList) -- | @since 1.5.3.0 instance (FromJSON1 f, Functor f) => FromJSON (F.Mu f) where @@ -2341,11 +2433,11 @@ instance (FromJSON a, FromJSON b) => FromJSON (S.These a b) where -- | @since 1.5.3.0 instance FromJSON2 S.These where - liftParseJSON2 pa pas pb pbs = fmap S.toStrict . liftParseJSON2 pa pas pb pbs + liftParseJSON2 oa pa pas ob pb pbs = fmap S.toStrict . liftParseJSON2 oa pa pas ob pb pbs -- | @since 1.5.3.0 instance FromJSON a => FromJSON1 (S.These a) where - liftParseJSON pa pas = fmap S.toStrict . liftParseJSON pa pas + liftParseJSON oa pa pas = fmap S.toStrict . liftParseJSON oa pa pas -- | @since 1.5.3.0 instance (FromJSON a, FromJSON b) => FromJSON (S.Pair a b) where @@ -2353,11 +2445,11 @@ instance (FromJSON a, FromJSON b) => FromJSON (S.Pair a b) where -- | @since 1.5.3.0 instance FromJSON2 S.Pair where - liftParseJSON2 pa pas pb pbs = fmap S.toStrict . liftParseJSON2 pa pas pb pbs + liftParseJSON2 oa pa pas ob pb pbs = fmap S.toStrict . liftParseJSON2 oa pa pas ob pb pbs -- | @since 1.5.3.0 instance FromJSON a => FromJSON1 (S.Pair a) where - liftParseJSON pa pas = fmap S.toStrict . liftParseJSON pa pas + liftParseJSON oa pa pas = fmap S.toStrict . liftParseJSON oa pa pas -- | @since 1.5.3.0 instance (FromJSON a, FromJSON b) => FromJSON (S.Either a b) where @@ -2365,38 +2457,45 @@ instance (FromJSON a, FromJSON b) => FromJSON (S.Either a b) where -- | @since 1.5.3.0 instance FromJSON2 S.Either where - liftParseJSON2 pa pas pb pbs = fmap S.toStrict . liftParseJSON2 pa pas pb pbs + liftParseJSON2 oa pa pas ob pb pbs = fmap S.toStrict . liftParseJSON2 oa pa pas ob pb pbs -- | @since 1.5.3.0 instance FromJSON a => FromJSON1 (S.Either a) where - liftParseJSON pa pas = fmap S.toStrict . liftParseJSON pa pas + liftParseJSON oa pa pas = fmap S.toStrict . liftParseJSON oa pa pas -- | @since 1.5.3.0 instance FromJSON a => FromJSON (S.Maybe a) where parseJSON = fmap S.toStrict . parseJSON + omittedField = fmap S.toStrict omittedField -- | @since 1.5.3.0 instance FromJSON1 S.Maybe where - liftParseJSON pa pas = fmap S.toStrict . liftParseJSON pa pas + liftParseJSON oa pa pas = fmap S.toStrict . liftParseJSON oa pa pas + liftOmittedField = fmap S.toStrict . liftOmittedField ------------------------------------------------------------------------------- -- tagged ------------------------------------------------------------------------------- instance FromJSON1 Proxy where - liftParseJSON _ _ _ = pure Proxy + liftParseJSON _ _ _ _ = pure Proxy + liftOmittedField _ = Just Proxy instance FromJSON (Proxy a) where parseJSON _ = pure Proxy + omittedField = Just Proxy instance FromJSON2 Tagged where - liftParseJSON2 _ _ p _ = fmap Tagged . p + liftParseJSON2 _ _ _ _ p _ = coerce p + liftOmittedField2 _ = coerce instance FromJSON1 (Tagged a) where - liftParseJSON p _ = fmap Tagged . p + liftParseJSON _ p _ = coerce p + liftOmittedField = coerce instance FromJSON b => FromJSON (Tagged a b) where parseJSON = parseJSON1 + omittedField = coerce (omittedField @b) instance FromJSONKey b => FromJSONKey (Tagged a b) where fromJSONKey = coerceFromJSONKeyFunction (fromJSONKey :: FromJSONKeyFunction b) @@ -2418,7 +2517,7 @@ instance (FromJSON a, FromJSON b) => FromJSON (These a b) where -- | @since 1.5.1.0 instance FromJSON a => FromJSON1 (These a) where - liftParseJSON pb _ = withObject "These a b" (p . KM.toList) + liftParseJSON _ pb _ = withObject "These a b" (p . KM.toList) where p [("This", a), ("That", b)] = These <$> parseJSON a <*> pb b p [("That", b), ("This", a)] = These <$> parseJSON a <*> pb b @@ -2428,7 +2527,7 @@ instance FromJSON a => FromJSON1 (These a) where -- | @since 1.5.1.0 instance FromJSON2 These where - liftParseJSON2 pa _ pb _ = withObject "These a b" (p . KM.toList) + liftParseJSON2 _ pa _ _ pb _ = withObject "These a b" (p . KM.toList) where p [("This", a), ("That", b)] = These <$> pa a <*> pb b p [("That", b), ("This", a)] = These <$> pa a <*> pb b @@ -2438,12 +2537,12 @@ instance FromJSON2 These where -- | @since 1.5.1.0 instance (FromJSON1 f, FromJSON1 g) => FromJSON1 (These1 f g) where - liftParseJSON px pl = withObject "These1" (p . KM.toList) + liftParseJSON o px pl = withObject "These1" (p . KM.toList) where - p [("This", a), ("That", b)] = These1 <$> liftParseJSON px pl a <*> liftParseJSON px pl b - p [("That", b), ("This", a)] = These1 <$> liftParseJSON px pl a <*> liftParseJSON px pl b - p [("This", a)] = This1 <$> liftParseJSON px pl a - p [("That", b)] = That1 <$> liftParseJSON px pl b + p [("This", a), ("That", b)] = These1 <$> liftParseJSON o px pl a <*> liftParseJSON o px pl b + p [("That", b), ("This", a)] = These1 <$> liftParseJSON o px pl a <*> liftParseJSON o px pl b + p [("This", a)] = This1 <$> liftParseJSON o px pl a + p [("That", b)] = That1 <$> liftParseJSON o px pl b p _ = fail "Expected object with 'This' and 'That' keys only" -- | @since 1.5.1.0 @@ -2470,7 +2569,7 @@ instance (FromJSONKey a, FromJSON a) => FromJSONKey [a] where ------------------------------------------------------------------------------- instance FromJSON2 (,) where - liftParseJSON2 pA _ pB _ = withArray "(a, b)" $ \t -> + liftParseJSON2 _ pA _ _ pB _ = withArray "(a, b)" $ \t -> let n = V.length t in if n == 2 then (,) @@ -2479,14 +2578,14 @@ instance FromJSON2 (,) where else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 2" instance (FromJSON a) => FromJSON1 ((,) a) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b) => FromJSON (a, b) where parseJSON = parseJSON2 instance (FromJSON a) => FromJSON2 ((,,) a) where - liftParseJSON2 pB _ pC _ = withArray "(a, b, c)" $ \t -> + liftParseJSON2 _ pB _ _ pC _ = withArray "(a, b, c)" $ \t -> let n = V.length t in if n == 3 then (,,) @@ -2496,14 +2595,14 @@ instance (FromJSON a) => FromJSON2 ((,,) a) where else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 3" instance (FromJSON a, FromJSON b) => FromJSON1 ((,,) a b) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a, b, c) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b) => FromJSON2 ((,,,) a b) where - liftParseJSON2 pC _ pD _ = withArray "(a, b, c, d)" $ \t -> + liftParseJSON2 _ pC _ _ pD _ = withArray "(a, b, c, d)" $ \t -> let n = V.length t in if n == 4 then (,,,) @@ -2514,14 +2613,14 @@ instance (FromJSON a, FromJSON b) => FromJSON2 ((,,,) a b) where else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 4" instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON1 ((,,,) a b c) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON (a, b, c, d) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON2 ((,,,,) a b c) where - liftParseJSON2 pD _ pE _ = withArray "(a, b, c, d, e)" $ \t -> + liftParseJSON2 _ pD _ _ pE _ = withArray "(a, b, c, d, e)" $ \t -> let n = V.length t in if n == 5 then (,,,,) @@ -2533,14 +2632,14 @@ instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON2 ((,,,,) a b c) where else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 5" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON1 ((,,,,) a b c d) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e) => FromJSON (a, b, c, d, e) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON2 ((,,,,,) a b c d) where - liftParseJSON2 pE _ pF _ = withArray "(a, b, c, d, e, f)" $ \t -> + liftParseJSON2 _ pE _ _ pF _ = withArray "(a, b, c, d, e, f)" $ \t -> let n = V.length t in if n == 6 then (,,,,,) @@ -2553,14 +2652,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) => FromJSON2 ((,,,,,) else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 6" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e) => FromJSON1 ((,,,,,) a b c d e) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f) => FromJSON (a, b, c, d, e, f) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e) => FromJSON2 ((,,,,,,) a b c d e) where - liftParseJSON2 pF _ pG _ = withArray "(a, b, c, d, e, f, g)" $ \t -> + liftParseJSON2 _ pF _ _ pG _ = withArray "(a, b, c, d, e, f, g)" $ \t -> let n = V.length t in if n == 7 then (,,,,,,) @@ -2574,14 +2673,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e) => FromJSO else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 7" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f) => FromJSON1 ((,,,,,,) a b c d e f) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g) => FromJSON (a, b, c, d, e, f, g) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f) => FromJSON2 ((,,,,,,,) a b c d e f) where - liftParseJSON2 pG _ pH _ = withArray "(a, b, c, d, e, f, g, h)" $ \t -> + liftParseJSON2 _ pG _ _ pH _ = withArray "(a, b, c, d, e, f, g, h)" $ \t -> let n = V.length t in if n == 8 then (,,,,,,,) @@ -2596,14 +2695,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 8" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g) => FromJSON1 ((,,,,,,,) a b c d e f g) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h) => FromJSON (a, b, c, d, e, f, g, h) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g) => FromJSON2 ((,,,,,,,,) a b c d e f g) where - liftParseJSON2 pH _ pI _ = withArray "(a, b, c, d, e, f, g, h, i)" $ \t -> + liftParseJSON2 _ pH _ _ pI _ = withArray "(a, b, c, d, e, f, g, h, i)" $ \t -> let n = V.length t in if n == 9 then (,,,,,,,,) @@ -2619,14 +2718,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 9" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h) => FromJSON1 ((,,,,,,,,) a b c d e f g h) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i) => FromJSON (a, b, c, d, e, f, g, h, i) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h) => FromJSON2 ((,,,,,,,,,) a b c d e f g h) where - liftParseJSON2 pI _ pJ _ = withArray "(a, b, c, d, e, f, g, h, i, j)" $ \t -> + liftParseJSON2 _ pI _ _ pJ _ = withArray "(a, b, c, d, e, f, g, h, i, j)" $ \t -> let n = V.length t in if n == 10 then (,,,,,,,,,) @@ -2643,14 +2742,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 10" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i) => FromJSON1 ((,,,,,,,,,) a b c d e f g h i) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j) => FromJSON (a, b, c, d, e, f, g, h, i, j) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i) => FromJSON2 ((,,,,,,,,,,) a b c d e f g h i) where - liftParseJSON2 pJ _ pK _ = withArray "(a, b, c, d, e, f, g, h, i, j, k)" $ \t -> + liftParseJSON2 _ pJ _ _ pK _ = withArray "(a, b, c, d, e, f, g, h, i, j, k)" $ \t -> let n = V.length t in if n == 11 then (,,,,,,,,,,) @@ -2668,14 +2767,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 11" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j) => FromJSON1 ((,,,,,,,,,,) a b c d e f g h i j) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k) => FromJSON (a, b, c, d, e, f, g, h, i, j, k) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j) => FromJSON2 ((,,,,,,,,,,,) a b c d e f g h i j) where - liftParseJSON2 pK _ pL _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l)" $ \t -> + liftParseJSON2 _ pK _ _ pL _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l)" $ \t -> let n = V.length t in if n == 12 then (,,,,,,,,,,,) @@ -2694,14 +2793,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 12" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k) => FromJSON1 ((,,,,,,,,,,,) a b c d e f g h i j k) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l) => FromJSON (a, b, c, d, e, f, g, h, i, j, k, l) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k) => FromJSON2 ((,,,,,,,,,,,,) a b c d e f g h i j k) where - liftParseJSON2 pL _ pM _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m)" $ \t -> + liftParseJSON2 _ pL _ _ pM _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m)" $ \t -> let n = V.length t in if n == 13 then (,,,,,,,,,,,,) @@ -2721,14 +2820,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 13" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l) => FromJSON1 ((,,,,,,,,,,,,) a b c d e f g h i j k l) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m) => FromJSON (a, b, c, d, e, f, g, h, i, j, k, l, m) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l) => FromJSON2 ((,,,,,,,,,,,,,) a b c d e f g h i j k l) where - liftParseJSON2 pM _ pN _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m, n)" $ \t -> + liftParseJSON2 _ pM _ _ pN _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m, n)" $ \t -> let n = V.length t in if n == 14 then (,,,,,,,,,,,,,) @@ -2749,14 +2848,14 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 14" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m) => FromJSON1 ((,,,,,,,,,,,,,) a b c d e f g h i j k l m) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m, FromJSON n) => FromJSON (a, b, c, d, e, f, g, h, i, j, k, l, m, n) where parseJSON = parseJSON2 instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m) => FromJSON2 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m) where - liftParseJSON2 pN _ pO _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)" $ \t -> + liftParseJSON2 _ pN _ _ pO _ = withArray "(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)" $ \t -> let n = V.length t in if n == 15 then (,,,,,,,,,,,,,,) @@ -2778,7 +2877,7 @@ instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f else fail $ "cannot unpack array of length " ++ show n ++ " into a tuple of length 15" instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m, FromJSON n) => FromJSON1 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n) where - liftParseJSON = liftParseJSON2 parseJSON parseJSONList + liftParseJSON = liftParseJSON2 omittedField parseJSON parseJSONList instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e, FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j, FromJSON k, FromJSON l, FromJSON m, FromJSON n, FromJSON o) => FromJSON (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) where parseJSON = parseJSON2 diff --git a/src/Data/Aeson/Types/Internal.hs b/src/Data/Aeson/Types/Internal.hs index bdc74750e..758f5fc4b 100644 --- a/src/Data/Aeson/Types/Internal.hs +++ b/src/Data/Aeson/Types/Internal.hs @@ -57,6 +57,7 @@ module Data.Aeson.Types.Internal , constructorTagModifier , allNullaryToStringTag , omitNothingFields + , allowOmittedFields , sumEncoding , unwrapUnaryRecords , tagSingleConstructors @@ -718,50 +719,16 @@ data Options = Options -- omitted from the resulting object. If 'False', the resulting -- object will include those fields mapping to @null@. -- - -- Note that this /does not/ affect parsing: 'Maybe' fields are - -- optional regardless of the value of 'omitNothingFields', subject - -- to the note below. - -- - -- === Note - -- - -- Setting 'omitNothingFields' to 'True' only affects fields which are of - -- type 'Maybe' /uniformly/ in the 'ToJSON' instance. - -- In particular, if the type of a field is declared as a type variable, it - -- will not be omitted from the JSON object, unless the field is - -- specialized upfront in the instance. - -- - -- The same holds for 'Maybe' fields being optional in the 'FromJSON' instance. - -- - -- ==== __Example__ - -- - -- The generic instance for the following type @Fruit@ depends on whether - -- the instance head is @Fruit a@ or @Fruit (Maybe a)@. - -- - -- @ - -- data Fruit a = Fruit - -- { apples :: a -- A field whose type is a type variable. - -- , oranges :: 'Maybe' Int - -- } deriving 'Generic' - -- - -- -- apples required, oranges optional - -- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)). - -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) - -- - -- -- apples optional, oranges optional - -- -- In this instance, the field apples is uniformly of type ('Maybe' a). - -- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) - -- - -- options :: 'Options' - -- options = 'defaultOptions' { 'omitNothingFields' = 'True' } + -- In @aeson-2.2@ this flag is generalised to omit all values with @'Data.Aeson.Types.omitField' x = True@. + -- If 'False', the resulting object will include those fields encoded as specified. -- - -- -- apples always present in the output, oranges is omitted if 'Nothing' - -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit a) where - -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options - -- - -- -- both apples and oranges are omitted if 'Nothing' - -- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit ('Maybe' a)) where - -- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options - -- @ + -- Note that this /does not/ affect parsing: 'Maybe' fields are + -- optional regardless of the value of 'omitNothingFields'. + -- 'allowOmittedFieds' controls parsing behavior. + , allowOmittedFields :: Bool + -- ^ If 'True', missing fields of a record will be filled + -- with 'omittedField' values (if they are 'Just'). + -- If 'False', all fields will required to present in the record object. , sumEncoding :: SumEncoding -- ^ Specifies how to encode constructors of a sum datatype. , unwrapUnaryRecords :: Bool @@ -777,13 +744,14 @@ data Options = Options } instance Show Options where - show (Options f c a o s u t r) = + show (Options f c a o q s u t r) = "Options {" ++ intercalate ", " [ "fieldLabelModifier =~ " ++ show (f "exampleField") , "constructorTagModifier =~ " ++ show (c "ExampleConstructor") , "allNullaryToStringTag = " ++ show a , "omitNothingFields = " ++ show o + , "allowOmittedFields = " ++ show q , "sumEncoding = " ++ show s , "unwrapUnaryRecords = " ++ show u , "tagSingleConstructors = " ++ show t @@ -866,6 +834,7 @@ data JSONKeyOptions = JSONKeyOptions -- , 'constructorTagModifier' = id -- , 'allNullaryToStringTag' = True -- , 'omitNothingFields' = False +-- , 'allowOmittedFields' = True -- , 'sumEncoding' = 'defaultTaggedObject' -- , 'unwrapUnaryRecords' = False -- , 'tagSingleConstructors' = False @@ -878,6 +847,7 @@ defaultOptions = Options , constructorTagModifier = id , allNullaryToStringTag = True , omitNothingFields = False + , allowOmittedFields = True , sumEncoding = defaultTaggedObject , unwrapUnaryRecords = False , tagSingleConstructors = False diff --git a/src/Data/Aeson/Types/ToJSON.hs b/src/Data/Aeson/Types/ToJSON.hs index 04852aa21..b30b57a2e 100644 --- a/src/Data/Aeson/Types/ToJSON.hs +++ b/src/Data/Aeson/Types/ToJSON.hs @@ -12,6 +12,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE UndecidableInstances #-} module Data.Aeson.Types.ToJSON @@ -22,9 +23,11 @@ module Data.Aeson.Types.ToJSON , ToJSON1(..) , toJSON1 , toEncoding1 + , omitField1 , ToJSON2(..) , toJSON2 , toEncoding2 + , omitField2 -- * Generic JSON classes , GToJSON'(..) , ToArgs(..) @@ -44,6 +47,7 @@ module Data.Aeson.Types.ToJSON -- * Object key-value pairs , KeyValue(..) + , KeyValueOmit(..) , KeyValuePair(..) , FromPairs(..) -- * Functions needed for documentation @@ -131,7 +135,7 @@ import qualified Data.Primitive.Types as PM import qualified Data.Primitive.PrimArray as PM toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value -toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b) +toJSONPair a b = liftToJSON2 (const False) a (listValue a) (const False) b (listValue b) realFloatToJSON :: RealFloat a => a -> Value realFloatToJSON d @@ -156,12 +160,12 @@ class GToJSON' enc arity f where -- and 'liftToEncoding' (if the @arity@ is 'One'). gToJSON :: Options -> ToArgs enc arity a -> f a -> enc --- | A 'ToArgs' value either stores nothing (for 'ToJSON') or it stores the two +-- | A 'ToArgs' value either stores nothing (for 'ToJSON') or it stores the three -- function arguments that encode occurrences of the type parameter (for -- 'ToJSON1'). data ToArgs res arity a where NoToArgs :: ToArgs res Zero a - To1Args :: (a -> res) -> ([a] -> res) -> ToArgs res One a + To1Args :: (a -> Bool) -> (a -> res) -> ([a] -> res) -> ToArgs res One a -- | A configurable generic JSON creator. This function applied to -- 'defaultOptions' is used as the default for 'toJSON' when the type @@ -174,9 +178,9 @@ genericToJSON opts = gToJSON opts NoToArgs . from -- 'defaultOptions' is used as the default for 'liftToJSON' when the type -- is an instance of 'Generic1'. genericLiftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) - => Options -> (a -> Value) -> ([a] -> Value) + => Options -> (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> f a -> Value -genericLiftToJSON opts tj tjl = gToJSON opts (To1Args tj tjl) . from1 +genericLiftToJSON opts o tj tjl = gToJSON opts (To1Args o tj tjl) . from1 -- | A configurable generic JSON encoder. This function applied to -- 'defaultOptions' is used as the default for 'toEncoding' when the type @@ -189,9 +193,9 @@ genericToEncoding opts = gToJSON opts NoToArgs . from -- 'defaultOptions' is used as the default for 'liftToEncoding' when the type -- is an instance of 'Generic1'. genericLiftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) - => Options -> (a -> Encoding) -> ([a] -> Encoding) + => Options -> (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding -genericLiftToEncoding opts te tel = gToJSON opts (To1Args te tel) . from1 +genericLiftToEncoding opts o te tel = gToJSON opts (To1Args o te tel) . from1 ------------------------------------------------------------------------------- -- Class @@ -313,29 +317,10 @@ class ToJSON a where toEncodingList = listEncoding toEncoding -- | Defines when it is acceptable to omit a field of this type from a record. + -- Used by @('.?=')@ operator, and Generics and TH deriving + -- with @'omitNothingFields' = True@. -- - -- @ - -- newtype A = A Int deriving (Generic, Eq) - -- instance ToJSON A where omitField = (== A 0) - -- - -- data R = R { a :: A, b :: Int } deriving ('Generic') - -- instance ToJSON R where toJSON = genericToJSON 'defaultOptions' {'omitNothingFields' = True} - -- - -- encode R {a = A 0, b = 0} -- "{\"b\":0}" - -- @ - -- - -- The default implementation is @omitField = const False@. - -- - -- @omitField@ Has no effect when using an 'Options' where @omitNothingFields@ is @False@ - -- (which is the case for @defaultOptions@). - -- - -- @ - -- data R' = R' { a :: A, b :: Int } deriving (Generic, FromJSON) - -- - -- encode R' {a = A 0, b = 0} -- "{\"a\":0,\"b\":0}" - -- @ - -- - -- @since x.x.x.x + -- @since 2.2.0.0 omitField :: a -> Bool omitField = const False @@ -349,24 +334,61 @@ instance (Generic a, GToJSON' Value Zero (Rep a), GToJSON' Encoding Zero (Rep a) ------------------------------------------------------------------------------- -- | A key-value pair for encoding a JSON object. -class KeyValue kv where +class KeyValue e kv | kv -> e where + (.=) :: ToJSON v => Key -> v -> kv infixr 8 .= -instance KeyValue Series where - name .= value = E.pair name (toEncoding value) + -- | @since 2.2.0.0 + explicitToField :: (v -> e) -> Key -> v -> kv + +instance KeyValue Encoding Series where + (.=) = explicitToField toEncoding {-# INLINE (.=) #-} -instance (key ~ Key, value ~ Value) => KeyValue (key, value) where - name .= value = (name, toJSON value) + explicitToField f name value = E.pair name (f value) + {-# INLINE explicitToField #-} + +instance (key ~ Key, value ~ Value) => KeyValue Value (key, value) where + (.=) = explicitToField toJSON {-# INLINE (.=) #-} + explicitToField f name value = (name, f value) + {-# INLINE explicitToField #-} + -- | Constructs a singleton 'KM.KeyMap'. For calling functions that -- demand an 'Object' for constructing objects. To be used in -- conjunction with 'mconcat'. Prefer to use 'object' where possible. -instance value ~ Value => KeyValue (KM.KeyMap value) where - name .= value = KM.singleton name (toJSON value) +instance value ~ Value => KeyValue Value (KM.KeyMap value) where + (.=) = explicitToField toJSON {-# INLINE (.=) #-} + + explicitToField f name value = KM.singleton name (f value) + {-# INLINE explicitToField #-} + +-- | An optional key-value pair for envoding to a JSON object +-- +-- @since 2.2.0.0 +-- +class KeyValue e kv => KeyValueOmit e kv | kv -> e where + (.?=) :: ToJSON v => Key -> v -> kv + infixr 8 .?= + + explicitToFieldOmit :: (v -> Bool) -> (v -> e) -> Key -> v -> kv + +instance KeyValueOmit Encoding Series where + name .?= value = if omitField value then mempty else name .= value + {-# INLINE (.?=) #-} + + explicitToFieldOmit o f name value = if o value then mempty else explicitToField f name value + {-# INLINE explicitToFieldOmit #-} + +instance value ~ Value => KeyValueOmit Value (KM.KeyMap value) where + name .?= value = if omitField value then KM.empty else name .= value + {-# INLINE (.?=) #-} + + explicitToFieldOmit o f name value = if o value then KM.empty else explicitToField f name value + {-# INLINE explicitToFieldOmit #-} ------------------------------------------------------------------------------- -- Classes and types for map keys @@ -619,43 +641,50 @@ instance GetConName f => GToJSONKey f -- -- See also 'ToJSON'. class ToJSON1 f where - liftToJSON :: (a -> Value) -> ([a] -> Value) -> f a -> Value + liftToJSON :: (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> f a -> Value default liftToJSON :: (Generic1 f, GToJSON' Value One (Rep1 f)) - => (a -> Value) -> ([a] -> Value) -> f a -> Value + => (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> f a -> Value liftToJSON = genericLiftToJSON defaultOptions - liftToJSONList :: (a -> Value) -> ([a] -> Value) -> [f a] -> Value - liftToJSONList f g = listValue (liftToJSON f g) + liftToJSONList :: (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> [f a] -> Value + liftToJSONList o f g = listValue (liftToJSON o f g) - liftToEncoding :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding + liftToEncoding :: (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding default liftToEncoding :: (Generic1 f, GToJSON' Encoding One (Rep1 f)) - => (a -> Encoding) -> ([a] -> Encoding) + => (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding liftToEncoding = genericLiftToEncoding defaultOptions - liftToEncodingList :: (a -> Encoding) -> ([a] -> Encoding) -> [f a] -> Encoding - liftToEncodingList f g = listEncoding (liftToEncoding f g) + liftToEncodingList :: (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> [f a] -> Encoding + liftToEncodingList o f g = listEncoding (liftToEncoding o f g) + + -- | @since 2.2.0.0 + liftOmitField :: (a -> Bool) -> f a -> Bool + liftOmitField _ _ = False -- | @since 2.1.0.0 instance (Generic1 f, GToJSON' Value One (Rep1 f), GToJSON' Encoding One (Rep1 f)) => ToJSON1 (Generically1 f) where - liftToJSON :: forall a. (a -> Value) -> ([a] -> Value) -> Generically1 f a -> Value - liftToJSON = coerce (genericLiftToJSON defaultOptions :: (a -> Value) -> ([a] -> Value) -> f a -> Value) + liftToJSON :: forall a. (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> Generically1 f a -> Value + liftToJSON = coerce (genericLiftToJSON defaultOptions :: (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> f a -> Value) - liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> Generically1 f a -> Encoding - liftToEncoding = coerce (genericLiftToEncoding defaultOptions :: (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding) + liftToEncoding :: forall a. (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> Generically1 f a -> Encoding + liftToEncoding = coerce (genericLiftToEncoding defaultOptions :: (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding) -- | Lift the standard 'toJSON' function through the type constructor. toJSON1 :: (ToJSON1 f, ToJSON a) => f a -> Value -toJSON1 = liftToJSON toJSON toJSONList +toJSON1 = liftToJSON omitField toJSON toJSONList {-# INLINE toJSON1 #-} -- | Lift the standard 'toEncoding' function through the type constructor. toEncoding1 :: (ToJSON1 f, ToJSON a) => f a -> Encoding -toEncoding1 = liftToEncoding toEncoding toEncodingList +toEncoding1 = liftToEncoding omitField toEncoding toEncodingList {-# INLINE toEncoding1 #-} +omitField1 :: (ToJSON1 f, ToJSON a) => f a -> Bool +omitField1 = liftOmitField omitField + -- | Lifting of the 'ToJSON' class to binary type constructors. -- -- Instead of manually writing your 'ToJSON2' instance, "Data.Aeson.TH" @@ -664,24 +693,32 @@ toEncoding1 = liftToEncoding toEncoding toEncodingList -- The compiler cannot provide a default generic implementation for 'liftToJSON2', -- unlike 'toJSON' and 'liftToJSON'. class ToJSON2 f where - liftToJSON2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> f a b -> Value - liftToJSONList2 :: (a -> Value) -> ([a] -> Value) -> (b -> Value) -> ([b] -> Value) -> [f a b] -> Value - liftToJSONList2 fa ga fb gb = listValue (liftToJSON2 fa ga fb gb) + liftToJSON2 :: (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> (b -> Bool) -> (b -> Value) -> ([b] -> Value) -> f a b -> Value + liftToJSONList2 :: (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> (b -> Bool) -> (b -> Value) -> ([b] -> Value) -> [f a b] -> Value + liftToJSONList2 oa fa ga ob fb gb = listValue (liftToJSON2 oa fa ga ob fb gb) - liftToEncoding2 :: (a -> Encoding) -> ([a] -> Encoding) -> (b -> Encoding) -> ([b] -> Encoding) -> f a b -> Encoding - liftToEncodingList2 :: (a -> Encoding) -> ([a] -> Encoding) -> (b -> Encoding) -> ([b] -> Encoding) -> [f a b] -> Encoding - liftToEncodingList2 fa ga fb gb = listEncoding (liftToEncoding2 fa ga fb gb) + liftToEncoding2 :: (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> (b -> Bool) -> (b -> Encoding) -> ([b] -> Encoding) -> f a b -> Encoding + liftToEncodingList2 :: (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> (b -> Bool) -> (b -> Encoding) -> ([b] -> Encoding) -> [f a b] -> Encoding + liftToEncodingList2 oa fa ga ob fb gb = listEncoding (liftToEncoding2 oa fa ga ob fb gb) + + -- | @since 2.2.0.0 + liftOmitField2 :: (a -> Bool) -> (b -> Bool) -> f a b -> Bool + liftOmitField2 _ _ _ = False -- | Lift the standard 'toJSON' function through the type constructor. toJSON2 :: (ToJSON2 f, ToJSON a, ToJSON b) => f a b -> Value -toJSON2 = liftToJSON2 toJSON toJSONList toJSON toJSONList +toJSON2 = liftToJSON2 omitField toJSON toJSONList omitField toJSON toJSONList {-# INLINE toJSON2 #-} -- | Lift the standard 'toEncoding' function through the type constructor. toEncoding2 :: (ToJSON2 f, ToJSON a, ToJSON b) => f a b -> Encoding -toEncoding2 = liftToEncoding2 toEncoding toEncodingList toEncoding toEncodingList +toEncoding2 = liftToEncoding2 omitField toEncoding toEncodingList omitField toEncoding toEncodingList {-# INLINE toEncoding2 #-} +omitField2 :: (ToJSON2 f, ToJSON a, ToJSON b) => f a b -> Bool +omitField2 = liftOmitField2 omitField omitField +{-# INLINE omitField2 #-} + ------------------------------------------------------------------------------- -- Encoding functions ------------------------------------------------------------------------------- @@ -716,9 +753,9 @@ listValue f = Array . V.fromList . map f -- These are needed for key-class default definitions instance ToJSON1 [] where - liftToJSON _ to' = to' + liftToJSON _ _ to' = to' - liftToEncoding _ to' = to' + liftToEncoding _ _ to' = to' instance (ToJSON a) => ToJSON [a] where {-# SPECIALIZE instance ToJSON String #-} @@ -742,7 +779,7 @@ instance {-# OVERLAPPABLE #-} (GToJSON' enc arity a) => GToJSON' enc arity (M1 i 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 + gToJSON _opts (To1Args _ tj _) = tj . unPar1 -- TODO {-# INLINE gToJSON #-} instance ( ConsToJSON enc arity a @@ -796,7 +833,7 @@ instance ToJSON a => GToJSON' Value arity (K1 i a) 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 + gToJSON _opts (To1Args o tj tjl) = liftToJSON o tj tjl . unRec1 {-# INLINE gToJSON #-} instance GToJSON' Value arity U1 where @@ -830,7 +867,7 @@ instance ( ToJSON1 f -- instance to generically encode the innermost type: gToJSON opts targs = let gtj = gToJSON opts targs in - liftToJSON gtj (listValue gtj) . unComp1 + liftToJSON (const False) gtj (listValue gtj) . unComp1 {-# INLINE gToJSON #-} -------------------------------------------------------------------------------- @@ -850,7 +887,7 @@ instance ToJSON a => GToJSON' Encoding arity (K1 i a) 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 + gToJSON _opts (To1Args o te tel) = liftToEncoding o te tel . unRec1 {-# INLINE gToJSON #-} instance GToJSON' Encoding arity U1 where @@ -877,7 +914,7 @@ instance ( ToJSON1 f -- instance to generically encode the innermost type: gToJSON opts targs = let gte = gToJSON opts targs in - liftToEncoding gte (listEncoding gte) . unComp1 + liftToEncoding (const False) gte (listEncoding gte) . unComp1 {-# INLINE gToJSON #-} -------------------------------------------------------------------------------- @@ -1134,7 +1171,10 @@ instance ( Selector s ) => RecordToPairs enc pairs arity (S1 s (K1 i t)) where recordToPairs opts targs m1 - | omitNothingFields opts && omitField (unK1 $ unM1 m1 :: t) = mempty + | omitNothingFields opts + , omitField (unK1 $ unM1 m1 :: t) + = mempty + | otherwise = let key = Key.fromString $ fieldLabelModifier opts (selName m1) value = gToJSON opts targs (unM1 m1) @@ -1142,14 +1182,36 @@ instance ( Selector s {-# INLINE recordToPairs #-} instance ( Selector s - , GToJSON' enc arity (Rec1 f) + , GToJSON' enc One (Rec1 f) , KeyValuePair enc pairs - ) => RecordToPairs enc pairs arity (S1 s (Rec1 f)) + , ToJSON1 f + ) => RecordToPairs enc pairs One (S1 s (Rec1 f)) where - recordToPairs opts targs m1 = - let key = Key.fromString $ fieldLabelModifier opts (selName m1) - value = gToJSON opts targs (unM1 m1) - in key `pair` value + recordToPairs opts targs@(To1Args o _ _) m1 + | omitNothingFields opts + , liftOmitField o $ unRec1 $ unM1 m1 + = mempty + + | otherwise = + let key = Key.fromString $ fieldLabelModifier opts (selName m1) + value = gToJSON opts targs (unM1 m1) + in key `pair` value + {-# INLINE recordToPairs #-} + +instance ( Selector s + , GToJSON' enc One Par1 + , KeyValuePair enc pairs + ) => RecordToPairs enc pairs One (S1 s Par1) + where + recordToPairs opts targs@(To1Args o _ _) m1 + | omitNothingFields opts + , o (unPar1 (unM1 m1)) + = mempty + + | otherwise = + let key = Key.fromString $ fieldLabelModifier opts (selName m1) + value = gToJSON opts targs (unM1 m1) + in key `pair` value {-# INLINE recordToPairs #-} -------------------------------------------------------------------------------- @@ -1245,44 +1307,49 @@ instance {-# OVERLAPPING #-} ------------------------------------------------------------------------------- instance ToJSON2 Const where - liftToJSON2 t _ _ _ (Const x) = t x - liftToEncoding2 t _ _ _ (Const x) = t x + liftToJSON2 _ t _ _ _ _ (Const x) = t x + liftToEncoding2 _ t _ _ _ _ (Const x) = t x + liftOmitField2 o _ (Const x) = o x instance ToJSON a => ToJSON1 (Const a) where - liftToJSON _ _ (Const x) = toJSON x - liftToEncoding _ _ (Const x) = toEncoding x + liftToJSON _ _ _ (Const x) = toJSON x + liftToEncoding _ _ _ (Const x) = toEncoding x + liftOmitField _ (Const x) = omitField x instance ToJSON a => ToJSON (Const a b) where toJSON (Const x) = toJSON x toEncoding (Const x) = toEncoding x + omitField (Const x) = omitField x instance (ToJSON a, ToJSONKey a) => ToJSONKey (Const a b) where toJSONKey = contramap getConst toJSONKey instance ToJSON1 Maybe where - liftToJSON t _ (Just a) = t a - liftToJSON _ _ Nothing = Null + liftToJSON _ t _ (Just a) = t a + liftToJSON _ _ _ Nothing = Null + + liftToEncoding _ t _ (Just a) = t a + liftToEncoding _ _ _ Nothing = E.null_ - liftToEncoding t _ (Just a) = t a - liftToEncoding _ _ Nothing = E.null_ + liftOmitField _ = isNothing instance (ToJSON a) => ToJSON (Maybe a) where toJSON = toJSON1 - omitField = isNothing + omitField = omitField1 toEncoding = toEncoding1 instance ToJSON2 Either where - liftToJSON2 toA _ _toB _ (Left a) = Object $ KM.singleton "Left" (toA a) - liftToJSON2 _toA _ toB _ (Right b) = Object $ KM.singleton "Right" (toB b) + liftToJSON2 _ toA _ _ _toB _ (Left a) = Object $ KM.singleton "Left" (toA a) + liftToJSON2 _ _toA _ _ toB _ (Right b) = Object $ KM.singleton "Right" (toB b) - liftToEncoding2 toA _ _toB _ (Left a) = E.pairs $ E.pair "Left" $ toA a - liftToEncoding2 _toA _ toB _ (Right b) = E.pairs $ E.pair "Right" $ toB b + liftToEncoding2 _ toA _ _ _toB _ (Left a) = E.pairs $ E.pair "Left" $ toA a + liftToEncoding2 _ _toA _ _ toB _ (Right b) = E.pairs $ E.pair "Right" $ toB b instance (ToJSON a) => ToJSON1 (Either a) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where toJSON = toJSON2 @@ -1317,6 +1384,7 @@ orderingToText o = case o of instance ToJSON () where toJSON _ = emptyArray toEncoding _ = emptyArray_ + omitField _ = True instance ToJSON Char where @@ -1493,8 +1561,8 @@ instance ToJSONKey Version where ------------------------------------------------------------------------------- instance ToJSON1 NonEmpty where - liftToJSON t _ = listValue t . NE.toList - liftToEncoding t _ = listEncoding t . NE.toList + liftToJSON _ t _ = listValue t . NE.toList + liftToEncoding _ t _ = listEncoding t . NE.toList instance (ToJSON a) => ToJSON (NonEmpty a) where toJSON = toJSON1 @@ -1516,8 +1584,8 @@ instance ToJSONKey Scientific where ------------------------------------------------------------------------------- instance ToJSON1 DList.DList where - liftToJSON t _ = listValue t . toList - liftToEncoding t _ = listEncoding t . toList + liftToJSON _ t _ = listValue t . toList + liftToEncoding _ t _ = listEncoding t . toList instance (ToJSON a) => ToJSON (DList.DList a) where toJSON = toJSON1 @@ -1525,8 +1593,8 @@ instance (ToJSON a) => ToJSON (DList.DList a) where -- | @since 1.5.3.0 instance ToJSON1 DNE.DNonEmpty where - liftToJSON t _ = listValue t . DNE.toList - liftToEncoding t _ = listEncoding t . DNE.toList + liftToJSON _ t _ = listValue t . DNE.toList + liftToEncoding _ t _ = listEncoding t . DNE.toList -- | @since 1.5.3.0 instance (ToJSON a) => ToJSON (DNE.DNonEmpty a) where @@ -1539,19 +1607,19 @@ instance (ToJSON a) => ToJSON (DNE.DNonEmpty a) where -- | @since 2.0.2.0 instance ToJSON1 Solo where - liftToJSON t _ (Solo a) = t a - liftToJSONList _ tl xs = tl (map getSolo xs) + liftToJSON _ t _ (Solo a) = t a + liftToJSONList _ _ tl xs = tl (map getSolo xs) - liftToEncoding t _ (Solo a) = t a - liftToEncodingList _ tl xs = tl (map getSolo xs) + liftToEncoding _ t _ (Solo a) = t a + liftToEncodingList _ _ tl xs = tl (map getSolo xs) -- | @since 2.0.2.0 instance (ToJSON a) => ToJSON (Solo a) where toJSON = toJSON1 - toJSONList = liftToJSONList toJSON toJSONList + toJSONList = liftToJSONList omitField toJSON toJSONList toEncoding = toEncoding1 - toEncodingList = liftToEncodingList toEncoding toEncodingList + toEncodingList = liftToEncodingList omitField toEncoding toEncodingList -- | @since 2.0.2.0 instance (ToJSONKey a) => ToJSONKey (Solo a) where @@ -1563,18 +1631,22 @@ instance (ToJSONKey a) => ToJSONKey (Solo a) where ------------------------------------------------------------------------------- instance ToJSON1 Identity where - liftToJSON t _ (Identity a) = t a - liftToJSONList _ tl xs = tl (map runIdentity xs) + liftToJSON _ t _ (Identity a) = t a + liftToJSONList _ _ tl xs = tl (map runIdentity xs) - liftToEncoding t _ (Identity a) = t a - liftToEncodingList _ tl xs = tl (map runIdentity xs) + liftToEncoding _ t _ (Identity a) = t a + liftToEncodingList _ _ tl xs = tl (map runIdentity xs) + + liftOmitField o (Identity a) = o a instance (ToJSON a) => ToJSON (Identity a) where toJSON = toJSON1 - toJSONList = liftToJSONList toJSON toJSONList + toJSONList = liftToJSONList omitField toJSON toJSONList toEncoding = toEncoding1 - toEncodingList = liftToEncodingList toEncoding toEncodingList + toEncodingList = liftToEncodingList omitField toEncoding toEncodingList + + omitField (Identity x) = omitField x instance (ToJSONKey a) => ToJSONKey (Identity a) where toJSONKey = contramapToJSONKeyFunction runIdentity toJSONKey @@ -1582,58 +1654,60 @@ instance (ToJSONKey a) => ToJSONKey (Identity a) where instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Compose f g) where - liftToJSON tv tvl (Compose x) = liftToJSON g gl x + liftToJSON o tv tvl (Compose x) = liftToJSON (liftOmitField o) g gl x where - g = liftToJSON tv tvl - gl = liftToJSONList tv tvl + g = liftToJSON o tv tvl + gl = liftToJSONList o tv tvl - liftToJSONList te tel xs = liftToJSONList g gl (map getCompose xs) + liftToJSONList o te tel xs = liftToJSONList (liftOmitField o) g gl (map getCompose xs) where - g = liftToJSON te tel - gl = liftToJSONList te tel + g = liftToJSON o te tel + gl = liftToJSONList o te tel - liftToEncoding te tel (Compose x) = liftToEncoding g gl x + liftToEncoding o te tel (Compose x) = liftToEncoding (liftOmitField o) g gl x where - g = liftToEncoding te tel - gl = liftToEncodingList te tel + g = liftToEncoding o te tel + gl = liftToEncodingList o te tel - liftToEncodingList te tel xs = liftToEncodingList g gl (map getCompose xs) + liftToEncodingList o te tel xs = liftToEncodingList (liftOmitField o) g gl (map getCompose xs) where - g = liftToEncoding te tel - gl = liftToEncodingList te tel + g = liftToEncoding o te tel + gl = liftToEncodingList o te tel + + liftOmitField o (Compose xs)= liftOmitField (liftOmitField o) xs instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Compose f g a) where toJSON = toJSON1 - toJSONList = liftToJSONList toJSON toJSONList + toJSONList = liftToJSONList omitField toJSON toJSONList toEncoding = toEncoding1 - toEncodingList = liftToEncodingList toEncoding toEncodingList - + toEncodingList = liftToEncodingList omitField toEncoding toEncodingList + omitField = omitField1 instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Product f g) where - liftToJSON tv tvl (Pair x y) = liftToJSON2 tx txl ty tyl (x, y) + liftToJSON o tv tvl (Pair x y) = liftToJSON2 (liftOmitField o) tx txl (liftOmitField o) ty tyl (x, y) where - tx = liftToJSON tv tvl - txl = liftToJSONList tv tvl - ty = liftToJSON tv tvl - tyl = liftToJSONList tv tvl + tx = liftToJSON o tv tvl + txl = liftToJSONList o tv tvl + ty = liftToJSON o tv tvl + tyl = liftToJSONList o tv tvl - liftToEncoding te tel (Pair x y) = liftToEncoding2 tx txl ty tyl (x, y) + liftToEncoding o te tel (Pair x y) = liftToEncoding2 (liftOmitField o) tx txl (liftOmitField o) ty tyl (x, y) where - tx = liftToEncoding te tel - txl = liftToEncodingList te tel - ty = liftToEncoding te tel - tyl = liftToEncodingList te tel + tx = liftToEncoding o te tel + txl = liftToEncodingList o te tel + ty = liftToEncoding o te tel + tyl = liftToEncodingList o te tel instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Product f g a) where toJSON = toJSON1 toEncoding = toEncoding1 instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (Sum f g) where - liftToJSON tv tvl (InL x) = Object $ KM.singleton "InL" (liftToJSON tv tvl x) - liftToJSON tv tvl (InR y) = Object $ KM.singleton "InR" (liftToJSON tv tvl y) + liftToJSON o tv tvl (InL x) = Object $ KM.singleton "InL" (liftToJSON o tv tvl x) + liftToJSON o tv tvl (InR y) = Object $ KM.singleton "InR" (liftToJSON o tv tvl y) - liftToEncoding te tel (InL x) = E.pairs $ E.pair "InL" $ liftToEncoding te tel x - liftToEncoding te tel (InR y) = E.pairs $ E.pair "InR" $ liftToEncoding te tel y + liftToEncoding o te tel (InL x) = E.pairs $ E.pair "InL" $ liftToEncoding o te tel x + liftToEncoding o te tel (InR y) = E.pairs $ E.pair "InR" $ liftToEncoding o te tel y instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Sum f g a) where toJSON = toJSON1 @@ -1644,8 +1718,8 @@ instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (Sum f g a) where ------------------------------------------------------------------------------- instance ToJSON1 Seq.Seq where - liftToJSON t _ = listValue t . toList - liftToEncoding t _ = listEncoding t . toList + liftToJSON _ t _ = listValue t . toList + liftToEncoding _ t _ = listEncoding t . toList instance (ToJSON a) => ToJSON (Seq.Seq a) where toJSON = toJSON1 @@ -1653,8 +1727,8 @@ instance (ToJSON a) => ToJSON (Seq.Seq a) where instance ToJSON1 Set.Set where - liftToJSON t _ = listValue t . Set.toList - liftToEncoding t _ = listEncoding t . Set.toList + liftToJSON _ t _ = listValue t . Set.toList + liftToEncoding _ t _ = listEncoding t . Set.toList instance (ToJSON a) => ToJSON (Set.Set a) where toJSON = toJSON1 @@ -1666,15 +1740,15 @@ instance ToJSON IntSet.IntSet where toEncoding = toEncoding . IntSet.toList instance ToJSON1 IntMap.IntMap where - liftToJSON t tol = liftToJSON to' tol' . IntMap.toList + liftToJSON o t tol = liftToJSON (liftOmitField o) to' tol' . IntMap.toList where - to' = liftToJSON2 toJSON toJSONList t tol - tol' = liftToJSONList2 toJSON toJSONList t tol + to' = liftToJSON2 omitField toJSON toJSONList o t tol + tol' = liftToJSONList2 omitField toJSON toJSONList o t tol - liftToEncoding t tol = liftToEncoding to' tol' . IntMap.toList + liftToEncoding o t tol = liftToEncoding (liftOmitField o) to' tol' . IntMap.toList where - to' = liftToEncoding2 toEncoding toEncodingList t tol - tol' = liftToEncodingList2 toEncoding toEncodingList t tol + to' = liftToEncoding2 omitField toEncoding toEncodingList o t tol + tol' = liftToEncodingList2 omitField toEncoding toEncodingList o t tol instance ToJSON a => ToJSON (IntMap.IntMap a) where toJSON = toJSON1 @@ -1682,11 +1756,11 @@ instance ToJSON a => ToJSON (IntMap.IntMap a) where instance ToJSONKey k => ToJSON1 (M.Map k) where - liftToJSON g _ = case toJSONKey of + liftToJSON _ g _ = case toJSONKey of ToJSONKeyText f _ -> Object . KM.fromMap . mapKeyValO f g ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . M.toList - liftToEncoding g _ = case toJSONKey of + liftToEncoding _ g _ = case toJSONKey of ToJSONKeyText _ f -> dict f g M.foldrWithKey ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . M.toList where @@ -1699,21 +1773,21 @@ instance (ToJSON v, ToJSONKey k) => ToJSON (M.Map k v) where instance ToJSON1 Tree.Tree where - liftToJSON t tol = go + liftToJSON o t tol = go where go (Tree.Node root branches) = - liftToJSON2 t tol to' tol' (root, branches) + liftToJSON2 o t tol (const False) to' tol' (root, branches) - to' = liftToJSON go (listValue go) - tol' = liftToJSONList go (listValue go) + to' = liftToJSON (const False) go (listValue go) + tol' = liftToJSONList (const False) go (listValue go) - liftToEncoding t tol = go + liftToEncoding o t tol = go where go (Tree.Node root branches) = - liftToEncoding2 t tol to' tol' (root, branches) + liftToEncoding2 o t tol (const False) to' tol' (root, branches) - to' = liftToEncoding go (listEncoding go) - tol' = liftToEncodingList go (listEncoding go) + to' = liftToEncoding (const False) go (listEncoding go) + tol' = liftToEncodingList (const False) go (listEncoding go) instance (ToJSON v) => ToJSON (Tree.Tree v) where toJSON = toJSON1 @@ -1736,8 +1810,8 @@ instance ToJSONKey UUID.UUID where ------------------------------------------------------------------------------- instance ToJSON1 Vector where - liftToJSON t _ = Array . V.map t - liftToEncoding t _ = listEncoding t . V.toList + liftToJSON _ t _ = Array . V.map t + liftToEncoding _ t _ = listEncoding t . V.toList instance (ToJSON a) => ToJSON (Vector a) where {-# SPECIALIZE instance ToJSON Array #-} @@ -1772,8 +1846,8 @@ instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where ------------------------------------------------------------------------------- instance ToJSON1 HashSet.HashSet where - liftToJSON t _ = listValue t . HashSet.toList - liftToEncoding t _ = listEncoding t . HashSet.toList + liftToJSON _ t _ = listValue t . HashSet.toList + liftToEncoding _ t _ = listEncoding t . HashSet.toList instance (ToJSON a) => ToJSON (HashSet.HashSet a) where toJSON = toJSON1 @@ -1781,13 +1855,13 @@ instance (ToJSON a) => ToJSON (HashSet.HashSet a) where instance ToJSONKey k => ToJSON1 (H.HashMap k) where - liftToJSON g _ = case toJSONKey of + liftToJSON _ g _ = case toJSONKey of ToJSONKeyText f _ -> Object . KM.fromHashMap . mapKeyVal f g ToJSONKeyValue f _ -> Array . V.fromList . map (toJSONPair f g) . H.toList -- liftToEncoding :: forall a. (a -> Encoding) -> ([a] -> Encoding) -> KM.HashMap k a -> Encoding - liftToEncoding g _ = case toJSONKey of + liftToEncoding _ g _ = case toJSONKey of ToJSONKeyText _ f -> dict f g H.foldrWithKey ToJSONKeyValue _ f -> listEncoding (pairEncoding f) . H.toList where @@ -1802,8 +1876,8 @@ instance (ToJSON v, ToJSONKey k) => ToJSON (H.HashMap k v) where ------------------------------------------------------------------------------- instance ToJSON1 KM.KeyMap where - liftToJSON g _ = Object . fmap g - liftToEncoding g _ = dict E.key g KM.foldrWithKey + liftToJSON _ g _ = Object . fmap g + liftToEncoding _ g _ = dict E.key g KM.foldrWithKey instance (ToJSON v) => ToJSON (KM.KeyMap v) where {-# SPECIALIZE instance ToJSON Object #-} @@ -1999,86 +2073,99 @@ instance ToJSONKey QuarterOfYear where ------------------------------------------------------------------------------- instance ToJSON1 Monoid.Dual where - liftToJSON t _ = t . Monoid.getDual - liftToEncoding t _ = t . Monoid.getDual + liftToJSON _ t _ = t . Monoid.getDual + liftToEncoding _ t _ = t . Monoid.getDual + liftOmitField = coerce instance ToJSON a => ToJSON (Monoid.Dual a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 instance ToJSON1 Monoid.First where - liftToJSON t to' = liftToJSON t to' . Monoid.getFirst - liftToEncoding t to' = liftToEncoding t to' . Monoid.getFirst - + liftToJSON o t to' = liftToJSON o t to' . Monoid.getFirst + liftToEncoding o t to' = liftToEncoding o t to' . Monoid.getFirst + liftOmitField :: forall a. (a -> Bool) -> Monoid.First a -> Bool + liftOmitField _ = coerce (isNothing @a) + instance ToJSON a => ToJSON (Monoid.First a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 instance ToJSON1 Monoid.Last where - liftToJSON t to' = liftToJSON t to' . Monoid.getLast - liftToEncoding t to' = liftToEncoding t to' . Monoid.getLast + liftToJSON o t to' = liftToJSON o t to' . Monoid.getLast + liftToEncoding o t to' = liftToEncoding o t to' . Monoid.getLast + + liftOmitField :: forall a. (a -> Bool) -> Monoid.Last a -> Bool + liftOmitField _ = coerce (isNothing @a) instance ToJSON a => ToJSON (Monoid.Last a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 instance ToJSON1 Semigroup.Min where - liftToJSON t _ (Semigroup.Min x) = t x - liftToEncoding t _ (Semigroup.Min x) = t x + liftToJSON _ t _ (Semigroup.Min x) = t x + liftToEncoding _ t _ (Semigroup.Min x) = t x + liftOmitField = coerce instance ToJSON a => ToJSON (Semigroup.Min a) where toJSON = toJSON1 toEncoding = toEncoding1 + omitField = omitField1 instance ToJSON1 Semigroup.Max where - liftToJSON t _ (Semigroup.Max x) = t x - liftToEncoding t _ (Semigroup.Max x) = t x + liftToJSON _ t _ (Semigroup.Max x) = t x + liftToEncoding _ t _ (Semigroup.Max x) = t x + liftOmitField = coerce instance ToJSON a => ToJSON (Semigroup.Max a) where toJSON = toJSON1 toEncoding = toEncoding1 + omitField = omitField1 instance ToJSON1 Semigroup.First where - liftToJSON t _ (Semigroup.First x) = t x - liftToEncoding t _ (Semigroup.First x) = t x + liftToJSON _ t _ (Semigroup.First x) = t x + liftToEncoding _ t _ (Semigroup.First x) = t x + liftOmitField = coerce instance ToJSON a => ToJSON (Semigroup.First a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 instance ToJSON1 Semigroup.Last where - liftToJSON t _ (Semigroup.Last x) = t x - liftToEncoding t _ (Semigroup.Last x) = t x + liftToJSON _ t _ (Semigroup.Last x) = t x + liftToEncoding _ t _ (Semigroup.Last x) = t x + liftOmitField = coerce instance ToJSON a => ToJSON (Semigroup.Last a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 instance ToJSON1 Semigroup.WrappedMonoid where - liftToJSON t _ (Semigroup.WrapMonoid x) = t x - liftToEncoding t _ (Semigroup.WrapMonoid x) = t x - + liftToJSON _ t _ (Semigroup.WrapMonoid x) = t x + liftToEncoding _ t _ (Semigroup.WrapMonoid x) = t x + liftOmitField = coerce + instance ToJSON a => ToJSON (Semigroup.WrappedMonoid a) where toJSON = toJSON1 toEncoding = toEncoding1 - + omitField = omitField1 #if !MIN_VERSION_base(4,16,0) instance ToJSON1 Semigroup.Option where - liftToJSON t to' = liftToJSON t to' . Semigroup.getOption - liftToEncoding t to' = liftToEncoding t to' . Semigroup.getOption + liftToJSON o t to' = liftToJSON o t to' . Semigroup.getOption + liftToEncoding o t to' = liftToEncoding o t to' . Semigroup.getOption + liftOmitField _ = isNothing . Semigroup.getOption instance ToJSON a => ToJSON (Semigroup.Option a) where toJSON = toJSON1 toEncoding = toEncoding1 - omitField (Semigroup.Option Nothing) = True - omitField (Semigroup.Option Just {}) = False + omitField = omitField1 #endif ------------------------------------------------------------------------------- @@ -2087,18 +2174,19 @@ instance ToJSON a => ToJSON (Semigroup.Option a) where -- | @since 1.5.3.0 instance ToJSON1 f => ToJSON (F.Fix f) where - toJSON = go where go (F.Fix f) = liftToJSON go toJSONList f - toEncoding = go where go (F.Fix f) = liftToEncoding go toEncodingList f + toJSON = go where go (F.Fix f) = liftToJSON omitField go toJSONList f + toEncoding = go where go (F.Fix f) = liftToEncoding omitField go toEncodingList f + omitField = go where go (F.Fix f) = liftOmitField go f -- | @since 1.5.3.0 instance (ToJSON1 f, Functor f) => ToJSON (F.Mu f) where - toJSON = F.foldMu (liftToJSON id (listValue id)) - toEncoding = F.foldMu (liftToEncoding id (listEncoding id)) + toJSON = F.foldMu (liftToJSON (const False) id (listValue id)) + toEncoding = F.foldMu (liftToEncoding (const False) id (listEncoding id)) -- | @since 1.5.3.0 instance (ToJSON1 f, Functor f) => ToJSON (F.Nu f) where - toJSON = F.foldNu (liftToJSON id (listValue id)) - toEncoding = F.foldNu (liftToEncoding id (listEncoding id)) + toJSON = F.foldNu (liftToJSON (const False) id (listValue id)) + toEncoding = F.foldNu (liftToEncoding (const False) id (listEncoding id)) ------------------------------------------------------------------------------- -- strict @@ -2111,13 +2199,13 @@ instance (ToJSON a, ToJSON b) => ToJSON (S.These a b) where -- | @since 1.5.3.0 instance ToJSON2 S.These where - liftToJSON2 toa toas tob tobs = liftToJSON2 toa toas tob tobs . S.toLazy - liftToEncoding2 toa toas tob tobs = liftToEncoding2 toa toas tob tobs . S.toLazy + liftToJSON2 oa toa toas ob tob tobs = liftToJSON2 oa toa toas ob tob tobs . S.toLazy + liftToEncoding2 oa toa toas ob tob tobs = liftToEncoding2 oa toa toas ob tob tobs . S.toLazy -- | @since 1.5.3.0 instance ToJSON a => ToJSON1 (S.These a) where - liftToJSON toa tos = liftToJSON toa tos . S.toLazy - liftToEncoding toa tos = liftToEncoding toa tos . S.toLazy + liftToJSON oa toa tos = liftToJSON oa toa tos . S.toLazy + liftToEncoding oa toa tos = liftToEncoding oa toa tos . S.toLazy -- | @since 1.5.3.0 instance (ToJSON a, ToJSON b) => ToJSON (S.Pair a b) where @@ -2126,13 +2214,13 @@ instance (ToJSON a, ToJSON b) => ToJSON (S.Pair a b) where -- | @since 1.5.3.0 instance ToJSON2 S.Pair where - liftToJSON2 toa toas tob tobs = liftToJSON2 toa toas tob tobs . S.toLazy - liftToEncoding2 toa toas tob tobs = liftToEncoding2 toa toas tob tobs . S.toLazy + liftToJSON2 oa toa toas ob tob tobs = liftToJSON2 oa toa toas ob tob tobs . S.toLazy + liftToEncoding2 oa toa toas ob tob tobs = liftToEncoding2 oa toa toas ob tob tobs . S.toLazy -- | @since 1.5.3.0 instance ToJSON a => ToJSON1 (S.Pair a) where - liftToJSON toa tos = liftToJSON toa tos . S.toLazy - liftToEncoding toa tos = liftToEncoding toa tos . S.toLazy + liftToJSON oa toa tos = liftToJSON oa toa tos . S.toLazy + liftToEncoding oa toa tos = liftToEncoding oa toa tos . S.toLazy -- | @since 1.5.3.0 instance (ToJSON a, ToJSON b) => ToJSON (S.Either a b) where @@ -2141,48 +2229,54 @@ instance (ToJSON a, ToJSON b) => ToJSON (S.Either a b) where -- | @since 1.5.3.0 instance ToJSON2 S.Either where - liftToJSON2 toa toas tob tobs = liftToJSON2 toa toas tob tobs . S.toLazy - liftToEncoding2 toa toas tob tobs = liftToEncoding2 toa toas tob tobs . S.toLazy + liftToJSON2 oa toa toas ob tob tobs = liftToJSON2 oa toa toas ob tob tobs . S.toLazy + liftToEncoding2 oa toa toas ob tob tobs = liftToEncoding2 oa toa toas ob tob tobs . S.toLazy -- | @since 1.5.3.0 instance ToJSON a => ToJSON1 (S.Either a) where - liftToJSON toa tos = liftToJSON toa tos . S.toLazy - liftToEncoding toa tos = liftToEncoding toa tos . S.toLazy + liftToJSON oa toa tos = liftToJSON oa toa tos . S.toLazy + liftToEncoding oa toa tos = liftToEncoding oa toa tos . S.toLazy -- | @since 1.5.3.0 instance ToJSON a => ToJSON (S.Maybe a) where toJSON = toJSON . S.toLazy toEncoding = toEncoding . S.toLazy + omitField = omitField . S.toLazy -- | @since 1.5.3.0 instance ToJSON1 S.Maybe where - liftToJSON toa tos = liftToJSON toa tos . S.toLazy - liftToEncoding toa tos = liftToEncoding toa tos . S.toLazy + liftToJSON oa toa tos = liftToJSON oa toa tos . S.toLazy + liftToEncoding oa toa tos = liftToEncoding oa toa tos . S.toLazy + liftOmitField oa = liftOmitField oa . S.toLazy ------------------------------------------------------------------------------- -- tagged ------------------------------------------------------------------------------- instance ToJSON1 Proxy where - liftToJSON _ _ _ = Null - liftToEncoding _ _ _ = E.null_ + liftToJSON _ _ _ _ = Null + liftToEncoding _ _ _ _ = E.null_ + liftOmitField _ _ = True instance ToJSON (Proxy a) where toJSON _ = Null toEncoding _ = E.null_ - + omitField _ = True instance ToJSON2 Tagged where - liftToJSON2 _ _ t _ (Tagged x) = t x - liftToEncoding2 _ _ t _ (Tagged x) = t x + liftToJSON2 _ _ _ _ t _ (Tagged x) = t x + liftToEncoding2 _ _ _ _ t _ (Tagged x) = t x + liftOmitField2 _ = coerce instance ToJSON1 (Tagged a) where - liftToJSON t _ (Tagged x) = t x - liftToEncoding t _ (Tagged x) = t x + liftToJSON _ t _ (Tagged x) = t x + liftToEncoding _ t _ (Tagged x) = t x + liftOmitField = coerce instance ToJSON b => ToJSON (Tagged a b) where toJSON = toJSON1 toEncoding = toEncoding1 + omitField = coerce (omitField @b) instance ToJSONKey b => ToJSONKey (Tagged a b) where toJSONKey = contramapToJSONKeyFunction unTagged toJSONKey @@ -2204,35 +2298,35 @@ instance (ToJSON a, ToJSON b) => ToJSON (These a b) where -- | @since 1.5.1.0 instance ToJSON2 These where - liftToJSON2 toa _ _tob _ (This a) = object [ "This" .= toa a ] - liftToJSON2 _toa _ tob _ (That b) = object [ "That" .= tob b ] - liftToJSON2 toa _ tob _ (These a b) = object [ "This" .= toa a, "That" .= tob b ] + liftToJSON2 _ toa _ _ _tob _ (This a) = object [ "This" .= toa a ] + liftToJSON2 _ _toa _ _ tob _ (That b) = object [ "That" .= tob b ] + liftToJSON2 _ toa _ _ tob _ (These a b) = object [ "This" .= toa a, "That" .= tob b ] - liftToEncoding2 toa _ _tob _ (This a) = E.pairs $ E.pair "This" (toa a) - liftToEncoding2 _toa _ tob _ (That b) = E.pairs $ E.pair "That" (tob b) - liftToEncoding2 toa _ tob _ (These a b) = E.pairs $ E.pair "This" (toa a) <> E.pair "That" (tob b) + liftToEncoding2 _ toa _ _ _tob _ (This a) = E.pairs $ E.pair "This" (toa a) + liftToEncoding2 _ _toa _ _ tob _ (That b) = E.pairs $ E.pair "That" (tob b) + liftToEncoding2 _ toa _ _ tob _ (These a b) = E.pairs $ E.pair "This" (toa a) <> E.pair "That" (tob b) -- | @since 1.5.1.0 instance ToJSON a => ToJSON1 (These a) where - liftToJSON _tob _ (This a) = object [ "This" .= a ] - liftToJSON tob _ (That b) = object [ "That" .= tob b ] - liftToJSON tob _ (These a b) = object [ "This" .= a, "That" .= tob b ] + liftToJSON _ _tob _ (This a) = object [ "This" .= a ] + liftToJSON _ tob _ (That b) = object [ "That" .= tob b ] + liftToJSON _ tob _ (These a b) = object [ "This" .= a, "That" .= tob b ] - liftToEncoding _tob _ (This a) = E.pairs $ "This" .= a - liftToEncoding tob _ (That b) = E.pairs $ E.pair "That" (tob b) - liftToEncoding tob _ (These a b) = E.pairs $ "This" .= a <> E.pair "That" (tob b) + liftToEncoding _ _tob _ (This a) = E.pairs $ "This" .= a + liftToEncoding _ tob _ (That b) = E.pairs $ E.pair "That" (tob b) + liftToEncoding _ tob _ (These a b) = E.pairs $ "This" .= a <> E.pair "That" (tob b) -- | @since 1.5.1.0 instance (ToJSON1 f, ToJSON1 g) => ToJSON1 (These1 f g) where - liftToJSON tx tl (This1 a) = object [ "This" .= liftToJSON tx tl a ] - liftToJSON tx tl (That1 b) = object [ "That" .= liftToJSON tx tl b ] - liftToJSON tx tl (These1 a b) = object [ "This" .= liftToJSON tx tl a, "That" .= liftToJSON tx tl b ] + liftToJSON o tx tl (This1 a) = object [ "This" .= liftToJSON o tx tl a ] + liftToJSON o tx tl (That1 b) = object [ "That" .= liftToJSON o tx tl b ] + liftToJSON o tx tl (These1 a b) = object [ "This" .= liftToJSON o tx tl a, "That" .= liftToJSON o tx tl b ] - liftToEncoding tx tl (This1 a) = E.pairs $ E.pair "This" (liftToEncoding tx tl a) - liftToEncoding tx tl (That1 b) = E.pairs $ E.pair "That" (liftToEncoding tx tl b) - liftToEncoding tx tl (These1 a b) = E.pairs $ - pair "This" (liftToEncoding tx tl a) `mappend` - pair "That" (liftToEncoding tx tl b) + liftToEncoding o tx tl (This1 a) = E.pairs $ E.pair "This" (liftToEncoding o tx tl a) + liftToEncoding o tx tl (That1 b) = E.pairs $ E.pair "That" (liftToEncoding o tx tl b) + liftToEncoding o tx tl (These1 a b) = E.pairs $ + pair "This" (liftToEncoding o tx tl a) `mappend` + pair "That" (liftToEncoding o tx tl b) -- | @since 1.5.1.0 instance (ToJSON1 f, ToJSON1 g, ToJSON a) => ToJSON (These1 f g a) where @@ -2259,46 +2353,47 @@ instance (ToJSONKey a, ToJSON a) => ToJSONKey [a] where ------------------------------------------------------------------------------- instance ToJSON2 (,) where - liftToJSON2 toA _ toB _ (a, b) = Array $ V.create $ do + liftToJSON2 _ toA _ _ toB _ (a, b) = Array $ V.create $ do mv <- VM.unsafeNew 2 VM.unsafeWrite mv 0 (toA a) VM.unsafeWrite mv 1 (toB b) return mv - liftToEncoding2 toA _ toB _ (a, b) = E.list id [toA a, toB b] + liftToEncoding2 _ toA _ _ toB _ (a, b) = E.list id [toA a, toB b] instance (ToJSON a) => ToJSON1 ((,) a) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b) => ToJSON (a, b) where toJSON = toJSON2 toEncoding = toEncoding2 + -- omitField = omitField2 instance (ToJSON a) => ToJSON2 ((,,) a) where - liftToJSON2 toB _ toC _ (a, b, c) = Array $ V.create $ do + liftToJSON2 _ toB _ _ toC _ (a, b, c) = Array $ V.create $ do mv <- VM.unsafeNew 3 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toB b) VM.unsafeWrite mv 2 (toC c) return mv - liftToEncoding2 toB _ toC _ (a, b, c) = E.list id + liftToEncoding2 _ toB _ _ toC _ (a, b, c) = E.list id [ toEncoding a , toB b , toC c ] instance (ToJSON a, ToJSON b) => ToJSON1 ((,,) a b) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (a, b, c) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where - liftToJSON2 toC _ toD _ (a, b, c, d) = Array $ V.create $ do + liftToJSON2 _ toC _ _ toD _ (a, b, c, d) = Array $ V.create $ do mv <- VM.unsafeNew 4 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2306,7 +2401,7 @@ instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where VM.unsafeWrite mv 3 (toD d) return mv - liftToEncoding2 toC _ toD _ (a, b, c, d) = E.list id + liftToEncoding2 _ toC _ _ toD _ (a, b, c, d) = E.list id [ toEncoding a , toEncoding b , toC c @@ -2314,15 +2409,15 @@ instance (ToJSON a, ToJSON b) => ToJSON2 ((,,,) a b) where ] instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON1 ((,,,) a b c) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON (a, b, c, d) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where - liftToJSON2 toD _ toE _ (a, b, c, d, e) = Array $ V.create $ do + liftToJSON2 _ toD _ _ toE _ (a, b, c, d, e) = Array $ V.create $ do mv <- VM.unsafeNew 5 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2331,7 +2426,7 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where VM.unsafeWrite mv 4 (toE e) return mv - liftToEncoding2 toD _ toE _ (a, b, c, d, e) = E.list id + liftToEncoding2 _ toD _ _ toE _ (a, b, c, d, e) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2340,15 +2435,15 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON2 ((,,,,) a b c) where ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON1 ((,,,,) a b c d) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON (a, b, c, d, e) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) where - liftToJSON2 toE _ toF _ (a, b, c, d, e, f) = Array $ V.create $ do + liftToJSON2 _ toE _ _ toF _ (a, b, c, d, e, f) = Array $ V.create $ do mv <- VM.unsafeNew 6 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2358,7 +2453,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) w VM.unsafeWrite mv 5 (toF f) return mv - liftToEncoding2 toE _ toF _ (a, b, c, d, e, f) = E.list id + liftToEncoding2 _ toE _ _ toF _ (a, b, c, d, e, f) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2368,15 +2463,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON2 ((,,,,,) a b c d) w ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON1 ((,,,,,) a b c d e) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON (a, b, c, d, e, f) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,) a b c d e) where - liftToJSON2 toF _ toG _ (a, b, c, d, e, f, g) = Array $ V.create $ do + liftToJSON2 _ toF _ _ toG _ (a, b, c, d, e, f, g) = Array $ V.create $ do mv <- VM.unsafeNew 7 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2387,7 +2482,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,) VM.unsafeWrite mv 6 (toG g) return mv - liftToEncoding2 toF _ toG _ (a, b, c, d, e, f, g) = E.list id + liftToEncoding2 _ toF _ _ toG _ (a, b, c, d, e, f, g) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2398,15 +2493,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) => ToJSON2 ((,,,,,,) ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON1 ((,,,,,,) a b c d e f) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON (a, b, c, d, e, f, g) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2 ((,,,,,,,) a b c d e f) where - liftToJSON2 toG _ toH _ (a, b, c, d, e, f, g, h) = Array $ V.create $ do + liftToJSON2 _ toG _ _ toH _ (a, b, c, d, e, f, g, h) = Array $ V.create $ do mv <- VM.unsafeNew 8 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2418,7 +2513,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2 VM.unsafeWrite mv 7 (toH h) return mv - liftToEncoding2 toG _ toH _ (a, b, c, d, e, f, g, h) = E.list id + liftToEncoding2 _ toG _ _ toH _ (a, b, c, d, e, f, g, h) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2430,15 +2525,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) => ToJSON2 ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON1 ((,,,,,,,) a b c d e f g) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON (a, b, c, d, e, f, g, h) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) => ToJSON2 ((,,,,,,,,) a b c d e f g) where - liftToJSON2 toH _ toI _ (a, b, c, d, e, f, g, h, i) = Array $ V.create $ do + liftToJSON2 _ toH _ _ toI _ (a, b, c, d, e, f, g, h, i) = Array $ V.create $ do mv <- VM.unsafeNew 9 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2451,7 +2546,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) VM.unsafeWrite mv 8 (toI i) return mv - liftToEncoding2 toH _ toI _ (a, b, c, d, e, f, g, h, i) = E.list id + liftToEncoding2 _ toH _ _ toI _ (a, b, c, d, e, f, g, h, i) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2464,15 +2559,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g) ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON1 ((,,,,,,,,) a b c d e f g h) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON (a, b, c, d, e, f, g, h, i) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h) => ToJSON2 ((,,,,,,,,,) a b c d e f g h) where - liftToJSON2 toI _ toJ _ (a, b, c, d, e, f, g, h, i, j) = Array $ V.create $ do + liftToJSON2 _ toI _ _ toJ _ (a, b, c, d, e, f, g, h, i, j) = Array $ V.create $ do mv <- VM.unsafeNew 10 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2486,7 +2581,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 9 (toJ j) return mv - liftToEncoding2 toI _ toJ _ (a, b, c, d, e, f, g, h, i, j) = E.list id + liftToEncoding2 _ toI _ _ toJ _ (a, b, c, d, e, f, g, h, i, j) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2500,15 +2595,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON1 ((,,,,,,,,,) a b c d e f g h i) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON (a, b, c, d, e, f, g, h, i, j) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i) => ToJSON2 ((,,,,,,,,,,) a b c d e f g h i) where - liftToJSON2 toJ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = Array $ V.create $ do + liftToJSON2 _ toJ _ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = Array $ V.create $ do mv <- VM.unsafeNew 11 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2523,7 +2618,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 10 (toK k) return mv - liftToEncoding2 toJ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = E.list id + liftToEncoding2 _ toJ _ _ toK _ (a, b, c, d, e, f, g, h, i, j, k) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2538,15 +2633,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON1 ((,,,,,,,,,,) a b c d e f g h i j) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON (a, b, c, d, e, f, g, h, i, j, k) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j) => ToJSON2 ((,,,,,,,,,,,) a b c d e f g h i j) where - liftToJSON2 toK _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = Array $ V.create $ do + liftToJSON2 _ toK _ _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = Array $ V.create $ do mv <- VM.unsafeNew 12 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2562,7 +2657,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 11 (toL l) return mv - liftToEncoding2 toK _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = E.list id + liftToEncoding2 _ toK _ _ toL _ (a, b, c, d, e, f, g, h, i, j, k, l) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2578,15 +2673,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON1 ((,,,,,,,,,,,) a b c d e f g h i j k) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON (a, b, c, d, e, f, g, h, i, j, k, l) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k) => ToJSON2 ((,,,,,,,,,,,,) a b c d e f g h i j k) where - liftToJSON2 toL _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = Array $ V.create $ do + liftToJSON2 _ toL _ _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = Array $ V.create $ do mv <- VM.unsafeNew 13 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2603,7 +2698,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 12 (toM m) return mv - liftToEncoding2 toL _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = E.list id + liftToEncoding2 _ toL _ _ toM _ (a, b, c, d, e, f, g, h, i, j, k, l, m) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2620,15 +2715,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON1 ((,,,,,,,,,,,,) a b c d e f g h i j k l) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON (a, b, c, d, e, f, g, h, i, j, k, l, m) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l) => ToJSON2 ((,,,,,,,,,,,,,) a b c d e f g h i j k l) where - liftToJSON2 toM _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = Array $ V.create $ do + liftToJSON2 _ toM _ _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = Array $ V.create $ do mv <- VM.unsafeNew 14 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2646,7 +2741,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 13 (toN n) return mv - liftToEncoding2 toM _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = E.list id + liftToEncoding2 _ toM _ _ toN _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2664,15 +2759,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON1 ((,,,,,,,,,,,,,) a b c d e f g h i j k l m) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n) => ToJSON (a, b, c, d, e, f, g, h, i, j, k, l, m, n) where toJSON = toJSON2 toEncoding = toEncoding2 instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m) => ToJSON2 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m) where - liftToJSON2 toN _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = Array $ V.create $ do + liftToJSON2 _ toN _ _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = Array $ V.create $ do mv <- VM.unsafeNew 15 VM.unsafeWrite mv 0 (toJSON a) VM.unsafeWrite mv 1 (toJSON b) @@ -2691,7 +2786,7 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, VM.unsafeWrite mv 14 (toO o) return mv - liftToEncoding2 toN _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = E.list id + liftToEncoding2 _ toN _ _ toO _ (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) = E.list id [ toEncoding a , toEncoding b , toEncoding c @@ -2710,8 +2805,8 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ] instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n) => ToJSON1 ((,,,,,,,,,,,,,,) a b c d e f g h i j k l m n) where - liftToJSON = liftToJSON2 toJSON toJSONList - liftToEncoding = liftToEncoding2 toEncoding toEncodingList + liftToJSON = liftToJSON2 omitField toJSON toJSONList + liftToEncoding = liftToEncoding2 omitField toEncoding toEncodingList instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f, ToJSON g, ToJSON h, ToJSON i, ToJSON j, ToJSON k, ToJSON l, ToJSON m, ToJSON n, ToJSON o) => ToJSON (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) where toJSON = toJSON2 diff --git a/tests/Encoders.hs b/tests/Encoders.hs index c0257962a..d8099331f 100644 --- a/tests/Encoders.hs +++ b/tests/Encoders.hs @@ -3,6 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -ddump-splices -ddump-to-file #-} module Encoders (module Encoders) where @@ -117,11 +118,11 @@ gNullaryFromJSONKey t = case genericFromJSONKey keyOptions of -- Unary types type LiftToJSON f a = - (a -> Value) -> ([a] -> Value) -> f a -> Value + (a -> Bool) -> (a -> Value) -> ([a] -> Value) -> f a -> Value type LiftToEncoding f a = - (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding + (a -> Bool) -> (a -> Encoding) -> ([a] -> Encoding) -> f a -> Encoding type LiftParseJSON f a = - (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) + Maybe a -> (Value -> Parser a) -> (Value -> Parser [a]) -> Value -> Parser (f a) thSomeTypeToJSON2ElemArray :: SomeType Int -> Value thSomeTypeToJSON2ElemArray = $(mkToJSON opts2ElemArray ''SomeType) diff --git a/tests/PropUtils.hs b/tests/PropUtils.hs index d685ea9e5..19b54fe7f 100644 --- a/tests/PropUtils.hs +++ b/tests/PropUtils.hs @@ -39,7 +39,7 @@ import Data.Hashable (Hashable) import Data.Map (Map) import Encoders import Instances () -import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample) +import Test.QuickCheck (Arbitrary(..), Property, Testable, (===), (.&&.), counterexample, property) import Types import Text.Read (readMaybe) import qualified Data.ByteString.Lazy.Char8 as L @@ -72,8 +72,8 @@ toParseJSON1 -> Property toParseJSON1 parsejson1 tojson1 = toParseJSON parsejson tojson where - parsejson = parsejson1 parseJSON (listParser parseJSON) - tojson = tojson1 toJSON (listValue toJSON) + parsejson = parsejson1 omittedField parseJSON (listParser parseJSON) + tojson = tojson1 omitField toJSON (listValue toJSON) roundTripEnc :: (FromJSON a, ToJSON a, Show a) => (a -> a -> Property) -> a -> Property @@ -96,8 +96,23 @@ roundTripNoEnc eq i = (ISuccess v) -> v `eq` i (IError path err) -> failure "fromJSON" (formatError path err) i +roundTripOmit :: (FromJSON a, ToJSON a, Show a) => + (Maybe a -> Maybe a -> Property) -> a -> Property +roundTripOmit eq i + | omitField i = omf `eq` Just i + | otherwise = case fmap omitField omf of + Nothing -> property True + Just True -> property True + Just False -> counterexample (show omf) False + where + omf = omittedField + roundTripEq :: (Eq a, FromJSON a, ToJSON a, Show a) => a -> Property -roundTripEq y = roundTripEnc (===) y .&&. roundTripNoEnc (===) y .&&. roundTripDecEnc (===) y +roundTripEq y = + roundTripEnc (===) y .&&. + roundTripNoEnc (===) y .&&. + roundTripDecEnc (===) y .&&. + roundTripOmit (===) y roundtripReadShow :: Value -> Property roundtripReadShow v = readMaybe (show v) === Just v @@ -161,9 +176,9 @@ sameAs1 -> Property sameAs1 toVal1 toEnc1 v = lhs === rhs where - rhs = Right $ toVal1 toJSON (listValue toJSON) v + rhs = Right $ toVal1 omitField toJSON (listValue toJSON) v lhs = eitherDecode . encodingToLazyByteString $ - toEnc1 toEncoding (listEncoding toEncoding) v + toEnc1 omitField toEncoding (listEncoding toEncoding) v sameAs1Agree :: ToJSON a @@ -174,7 +189,7 @@ sameAs1Agree sameAs1Agree toEnc toEnc1 v = rhs === lhs where rhs = encodingToLazyByteString $ toEnc v - lhs = encodingToLazyByteString $ toEnc1 toEncoding (listEncoding toEncoding) v + lhs = encodingToLazyByteString $ toEnc1 omitField toEncoding (listEncoding toEncoding) v -------------------------------------------------------------------------------- -- Value properties diff --git a/tests/PropertyRoundTrip.hs b/tests/PropertyRoundTrip.hs index 95f68d26c..1ec4b4785 100644 --- a/tests/PropertyRoundTrip.hs +++ b/tests/PropertyRoundTrip.hs @@ -27,6 +27,7 @@ import Numeric.Natural (Natural) import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (testProperty) import Types +import qualified Data.Monoid as Monoid import qualified Data.Text as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Short as ST @@ -41,8 +42,9 @@ import Instances () roundTripTests :: TestTree roundTripTests = - testGroup "roundTrip" [ - testProperty "Value" $ roundTripEq @Value + testGroup "roundTrip" + [ testProperty "()" $ roundTripEq @() + , testProperty "Value" $ roundTripEq @Value , testProperty "Bool" $ roundTripEq @Bool , testProperty "Double" $ roundTripEq @(Approx Double) , testProperty "Int" $ roundTripEq @Int @@ -82,6 +84,8 @@ roundTripTests = , testProperty "Fix" $ roundTripEq @(F.Fix (These Char)) , testProperty "Mu" $ roundTripEq @(F.Mu (These Char)) , testProperty "Nu" $ roundTripEq @(F.Nu (These Char)) + , testProperty "Maybe" $ roundTripEq @(Maybe Int) + , testProperty "Monoid.First" $ roundTripEq @(Monoid.First Int) , testProperty "Strict Pair" $ roundTripEq @(S.Pair Int Char) , testProperty "Strict Either" $ roundTripEq @(S.Either Int Char) , testProperty "Strict These" $ roundTripEq @(S.These Int Char) diff --git a/tests/PropertyTH.hs b/tests/PropertyTH.hs index 36a4fa6d1..9c2e090f3 100644 --- a/tests/PropertyTH.hs +++ b/tests/PropertyTH.hs @@ -130,5 +130,10 @@ templateHaskellTests = thOneConstructorToJSONDefault `sameAs` thOneConstructorToEncodingDefault , testProperty "OneConstructorTagged" $ thOneConstructorToJSONTagged `sameAs` thOneConstructorToEncodingTagged + +#if !MIN_VERSION_base(4,16,0) + , testProperty "OptionField" $ + thOptionFieldToJSON `sameAs` thOptionFieldToEncoding +#endif ] ] diff --git a/tests/Regression/Issue571.hs b/tests/Regression/Issue571.hs index f3891288c..9898b7270 100644 --- a/tests/Regression/Issue571.hs +++ b/tests/Regression/Issue571.hs @@ -16,8 +16,21 @@ data F = F instance FromJSON F where parseJSON = genericParseJSON defaultOptions { omitNothingFields = False } -- default +data G = G + { c :: Maybe Int + , d :: Maybe Int + } + deriving (Eq, Show, Generic) + +instance FromJSON G where + parseJSON = genericParseJSON defaultOptions { omitNothingFields = False, allowOmittedFields = False } + + issue571 :: TestTree issue571 = testCase "issue571" $ do -- the Maybe fields can be omitted. - let actual = decode "{}" :: Maybe F - actual @?= Just F { a = Nothing, b = Nothing } + let actualF = decode "{}" :: Maybe F + actualF @?= Just F { a = Nothing, b = Nothing } + + let actualG = decode "{}" :: Maybe G + actualG @?= Nothing \ No newline at end of file diff --git a/tests/Regression/Issue687.hs b/tests/Regression/Issue687.hs new file mode 100644 index 000000000..c56a22cf1 --- /dev/null +++ b/tests/Regression/Issue687.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE DeriveGeneric, TypeApplications, OverloadedStrings, TemplateHaskell, DuplicateRecordFields #-} +module Regression.Issue687 where + +import GHC.Generics (Generic1) +import Data.Aeson +import Data.Aeson.Types (iparseEither) +import Data.Aeson.Encoding (encodingToLazyByteString) +import Data.Aeson.TH (deriveJSON1) +import Test.Tasty (TestTree) +import Test.Tasty.HUnit (testCase, (@?=)) + +data ExG a = ExG { required :: a, optional :: Maybe a } + deriving (Eq, Show, Generic1) + +data ExTH a = ExTH { required :: a, optional :: Maybe a } + deriving (Eq, Show, Generic1) + +instance ToJSON1 ExG where + liftToJSON = genericLiftToJSON defaultOptions { omitNothingFields = True } + liftToEncoding = genericLiftToEncoding defaultOptions { omitNothingFields = True } + +instance FromJSON1 ExG where + liftParseJSON = genericLiftParseJSON defaultOptions { omitNothingFields = True } + +$(deriveJSON1 defaultOptions { omitNothingFields = True } ''ExTH) + +issue687 :: TestTree +issue687 = testCase "issue687" $ do + example (ExG @Int 1 Nothing) $ object [ "required" .= (1 :: Int) ] + example (ExG @Int 1 (Just 2)) $ object [ "required" .= (1 :: Int), "optional" .= (2 :: Int) ] + + example (ExTH @Int 1 Nothing) $ object [ "required" .= (1 :: Int) ] + example (ExTH @Int 1 (Just 2)) $ object [ "required" .= (1 :: Int), "optional" .= (2 :: Int) ] + + where + example :: (ToJSON1 f, FromJSON1 f, Eq (f Int), Show (f Int)) => f Int -> Value -> IO () + example x val = do + -- encoding + toJSON1 x @?= val + decode (encodingToLazyByteString (toEncoding1 x)) @?= Just val + + -- decoding + iparseEither parseJSON1 val @?= Right x diff --git a/tests/UnitTests.hs b/tests/UnitTests.hs index f133f7bf8..b208b9469 100644 --- a/tests/UnitTests.hs +++ b/tests/UnitTests.hs @@ -51,7 +51,6 @@ import Numeric.Natural (Natural) import Test.Tasty (TestTree, testGroup) import Test.Tasty.HUnit (Assertion, assertFailure, assertEqual, testCase) import Text.Printf (printf) -import qualified Data.ByteString as S import qualified Data.ByteString.Base16.Lazy as LBase16 import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.Text.Lazy as LT @@ -61,16 +60,18 @@ import qualified Data.Text.Lazy.Encoding as TLE import qualified ErrorMessages import qualified SerializationFormatSpec -import UnitTests.OptionalFields (optionalFields) -import UnitTests.NullaryConstructors (nullaryConstructors) import Regression.Issue351 import Regression.Issue571 +import Regression.Issue687 import Regression.Issue967 -import UnitTests.Hashable +import UnitTests.OmitNothingFieldsNote import UnitTests.FromJSONKey -import UnitTests.UTCTime -import UnitTests.MonadFix +import UnitTests.Hashable import UnitTests.KeyMapInsertWith +import UnitTests.MonadFix +import UnitTests.NullaryConstructors (nullaryConstructors) +import UnitTests.OptionalFields (optionalFields) +import UnitTests.UTCTime roundTripCamel :: String -> Assertion roundTripCamel name = assertEqual "" name (camelFrom '_' $ camelTo '_' name) @@ -262,7 +263,7 @@ deriveToJSON1 defaultOptions ''Foo pr455 :: Assertion pr455 = assertEqual "FooCons FooNil" - (toJSON foo) (liftToJSON undefined undefined foo) + (toJSON foo) (liftToJSON undefined undefined undefined foo) where foo :: Foo Int foo = FooCons FooNil @@ -276,6 +277,7 @@ showOptions = ++ ", constructorTagModifier =~ \"ExampleConstructor\"" ++ ", allNullaryToStringTag = True" ++ ", omitNothingFields = False" + ++ ", allowOmittedFields = True" ++ ", sumEncoding = TaggedObject {tagFieldName = \"tag\", contentsFieldName = \"contents\"}" ++ ", unwrapUnaryRecords = False" ++ ", tagSingleConstructors = False" @@ -538,12 +540,8 @@ tests = testGroup "unit" [ , hashableLaws , testGroup "Object construction" $ fmap (testCase "-") objectConstruction , testGroup "Nullary constructors" $ fmap (testCase "-") nullaryConstructors -<<<<<<< HEAD , fromJSONKeyTests -======= - , testGroup "Optional fields" optionalFields - , testGroup "FromJSONKey" $ fmap (testCase "-") fromJSONKeyAssertions ->>>>>>> 97b7bd8 (Type-directed optional fields) + , optionalFields , testCase "PR #455" pr455 , testCase "Unescape string (PR #477)" unescapeString , testCase "Show Options" showOptions @@ -567,6 +565,8 @@ tests = testGroup "unit" [ , monadFixTests , issue351 , issue571 + , issue687 , issue967 , keyMapInsertWithTests + , omitNothingFieldsNoteTests ] diff --git a/tests/UnitTests/MonadFix.hs b/tests/UnitTests/MonadFix.hs index 63a496956..f985dfccc 100644 --- a/tests/UnitTests/MonadFix.hs +++ b/tests/UnitTests/MonadFix.hs @@ -66,7 +66,7 @@ monadFixParserA = withObject "Rec" $ \obj -> mdo let p' :: Value -> Data.Aeson.Types.Parser [Char] p' v = do - (c, cs) <- liftParseJSON p'' (listParser p'') v + (c, cs) <- liftParseJSON Nothing p'' (listParser p'') v return (c : cs) foo <- explicitParseField p' obj "foo" @@ -90,7 +90,7 @@ monadFixParserB = withObject "Rec" $ \obj -> mdo let p' :: Value -> Data.Aeson.Types.Parser [Char] p' v = do - (c, cs) <- liftParseJSON p'' (listParser p'') v + (c, cs) <- liftParseJSON Nothing p'' (listParser p'') v return (c : cs) refs <- traverse p' (KM.toMap obj) diff --git a/tests/UnitTests/OmitNothingFieldsNote.hs b/tests/UnitTests/OmitNothingFieldsNote.hs new file mode 100644 index 000000000..91c13b491 --- /dev/null +++ b/tests/UnitTests/OmitNothingFieldsNote.hs @@ -0,0 +1,62 @@ +{-# LANGUAGE DeriveGeneric, OverloadedStrings #-} +module UnitTests.OmitNothingFieldsNote (omitNothingFieldsNoteTests) where + +-- prior aeson-2.2 the 'omitNothingFields' had the following note, +-- which is no longer true as these tests illustrate. + +-- Setting 'omitNothingFields' to 'True' only affects fields which are of +-- type 'Maybe' /uniformly/ in the 'ToJSON' instance. +-- In particular, if the type of a field is declared as a type variable, it +-- will not be omitted from the JSON object, unless the field is +-- specialized upfront in the instance. +-- +-- The same holds for 'Maybe' fields being optional in the 'FromJSON' instance. +-- +-- ==== __Example__ +-- +-- The generic instance for the following type @Fruit@ depends on whether +-- the instance head is @Fruit a@ or @Fruit (Maybe a)@. +-- +-- @ +-- data Fruit a = Fruit +-- { apples :: a -- A field whose type is a type variable. +-- , oranges :: 'Maybe' Int +-- } deriving 'Generic' +-- +-- -- apples required, oranges optional +-- -- Even if 'Data.Aeson.fromJSON' is then specialized to (Fruit ('Maybe' a)). +-- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit a) +-- +-- -- apples optional, oranges optional +-- -- In this instance, the field apples is uniformly of type ('Maybe' a). +-- instance 'Data.Aeson.FromJSON' a => 'Data.Aeson.FromJSON' (Fruit ('Maybe' a)) +-- +-- options :: 'Options' +-- options = 'defaultOptions' { 'omitNothingFields' = 'True' } +-- +-- -- apples always present in the output, oranges is omitted if 'Nothing' +-- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit a) where +-- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options +-- +-- -- both apples and oranges are omitted if 'Nothing' +-- instance 'Data.Aeson.ToJSON' a => 'Data.Aeson.ToJSON' (Fruit ('Maybe' a)) where +-- 'Data.Aeson.toJSON' = 'Data.Aeson.genericToJSON' options + +import Test.Tasty (TestTree) +import Test.Tasty.HUnit (testCase, (@?=)) +import GHC.Generics (Generic) +import Data.Aeson + +omitNothingFieldsNoteTests :: TestTree +omitNothingFieldsNoteTests = testCase "omitNothingFields Note" $ do + -- both fields are omitted, not only oranges! + encode (Fruit (Nothing :: Maybe Int) Nothing) @?= "{}" + +data Fruit a = Fruit + { apples :: a -- A field whose type is a type variable. + , oranges :: Maybe Int + } deriving Generic + +instance ToJSON a => ToJSON (Fruit a) where + toJSON = genericToJSON defaultOptions { omitNothingFields = True } + toEncoding = genericToEncoding defaultOptions { omitNothingFields = True } diff --git a/tests/UnitTests/OptionalFields.hs b/tests/UnitTests/OptionalFields.hs index 3fd5ea22e..6e645b55f 100644 --- a/tests/UnitTests/OptionalFields.hs +++ b/tests/UnitTests/OptionalFields.hs @@ -1,16 +1,24 @@ {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE OverloadedStrings #-} module UnitTests.OptionalFields (optionalFields) where +import GHC.Generics (Generic) import Data.Maybe (isNothing) import UnitTests.OptionalFields.Common import UnitTests.OptionalFields.Generics (omitGenerics) import UnitTests.OptionalFields.TH (omitTH) +import UnitTests.OptionalFields.Manual (omitManual) + +optionalFields :: TestTree +optionalFields = testGroup "optional fields" + [ omitGenerics + , omitTH + , omitManual + , proofOfConcept + ] -optionalFields :: [TestTree] -optionalFields = [omitGenerics, omitTH, proofOfConcept] - --- c.f. https://github.com/haskell/aeson/pull/839#issuecomment-782453()6() +-- c.f. https://github.com/haskell/aeson/pull/839#issuecomment-782453060 data P = P { x :: Nullable Int -- Field is required, but can be null. , y :: Undefineable Int -- Field is optional, but cannot be null. @@ -66,56 +74,56 @@ opts = defaultOptions { omitNothingFields = True } fullP :: P fullP = P (Nullable $ Just 0) (Undefineable $ Just 0) (NullOrUndefineable $ Just 0) -zero :: String -> (Key, Value) -zero = flip prop (0 :: Int) +zero :: Key -> (Key, Value) +zero k = k .= (0 :: Int) proofOfConcept :: TestTree proofOfConcept = testGroup "Type-directed optional fields Proof of Concept" [ testGroup "toJSON" [ testCase "x is not omitted when Nothing" $ let subject = fullP {x = Nullable Nothing} - expected = obj [prop "x" Null, zero "y", zero "z"] + expected = object ["x" .= Null, zero "y", zero "z"] in toJSON subject @?= expected , testCase "y is omitted when Nothing" $ let subject = fullP {y = Undefineable Nothing} - expected = obj [zero "x", zero "z"] + expected = object [zero "x", zero "z"] in toJSON subject @?= expected , testCase "z is omitted when Nothing" $ let subject = fullP {z = NullOrUndefineable Nothing} - expected = obj [zero "x", zero "y"] + expected = object [zero "x", zero "y"] in toJSON subject @?= expected ] , testGroup "parseJSON" [ testCase "x can be null" $ - let subject = obj [prop "x" Null, zero "y", zero "z"] + let subject = object ["x" .= Null, zero "y", zero "z"] expected = Just fullP {x = Nullable Nothing} in decode (encode subject) @?= expected , testCase "x cannot be omitted" $ - let subject = obj [zero "y", zero "z"] + let subject = object [zero "y", zero "z"] expected = Nothing :: Maybe P in decode (encode subject) @?= expected , testCase "y can be omitted" $ - let subject = obj [zero "x", zero "z"] + let subject = object [zero "x", zero "z"] expected = Just fullP {y = Undefineable Nothing} in decode (encode subject) @?= expected , testCase "y cannot be null" $ - let subject = obj [zero "x", prop "y" Null, zero "z"] + let subject = object [zero "x", "y" .= Null, zero "z"] expected = Nothing :: Maybe P in decode (encode subject) @?= expected , testCase "z can be null" $ - let subject = obj [zero "x", zero "y", prop "z" Null] + let subject = object [zero "x", zero "y", "z" .= Null] expected = Just fullP {z = NullOrUndefineable Nothing} in decode (encode subject) @?= expected , testCase "z can be omitted" $ - let subject = obj [zero "x", zero "y"] + let subject = object [zero "x", zero "y"] expected = Just fullP {z = NullOrUndefineable Nothing} in decode (encode subject) @?= expected ] diff --git a/tests/UnitTests/OptionalFields/Common.hs b/tests/UnitTests/OptionalFields/Common.hs index 90f37b4a0..542cbe5a2 100644 --- a/tests/UnitTests/OptionalFields/Common.hs +++ b/tests/UnitTests/OptionalFields/Common.hs @@ -1,75 +1,94 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE OverloadedStrings #-} module UnitTests.OptionalFields.Common ( module UnitTests.OptionalFields.Common , module Data.Aeson + , module Data.Aeson.Types , module Data.Aeson.TH - , module GHC.Generics , module Test.Tasty , module Test.Tasty.HUnit + , module Data.Proxy ) where import Data.Aeson +import Data.Aeson.Types import Data.Aeson.TH import Data.Maybe (isNothing) -import Data.Semigroup (Semigroup (..)) -import GHC.Generics +import GHC.Generics (Generic, Generic1) +import Data.Proxy import Test.Tasty import Test.Tasty.HUnit -import qualified Data.Aeson.Key as K -import qualified Data.Aeson.KeyMap as KM import qualified Data.Text as T newtype NullableNonEmptyString = NullableNonEmptyString (Maybe String) - deriving (Eq, Ord, Show, Generic, Semigroup, Monoid) + deriving (Eq, Ord, Show, Generic) + +defaultNullableNonEmptyString :: NullableNonEmptyString +defaultNullableNonEmptyString = NullableNonEmptyString Nothing instance ToJSON NullableNonEmptyString where toJSON (NullableNonEmptyString x) = toJSON x + toEncoding (NullableNonEmptyString x) = toEncoding x omitField (NullableNonEmptyString x) = isNothing x instance FromJSON NullableNonEmptyString where - parseJSON Null = pure mempty + parseJSON Null = pure defaultNullableNonEmptyString parseJSON (String x) = pure (nne $ T.unpack x) parseJSON _ = fail "NullableNonEmptyString.parseJSON: expected String or Null" - omittedField = Just mempty + omittedField = Just defaultNullableNonEmptyString + +nonOmittingOptions :: Options +nonOmittingOptions = defaultOptions { omitNothingFields = False, allowOmittedFields = False } + +omittingOptions :: Options +omittingOptions = defaultOptions { omitNothingFields = True, allowOmittedFields = True } nne :: String -> NullableNonEmptyString nne str = case filter (/= ' ') str of "" -> NullableNonEmptyString Nothing _ -> NullableNonEmptyString (Just str) -obj :: [(Key, Value)] -> Value -obj = Object . KM.fromList - -prop :: ToJSON a => String -> a -> (Key, Value) -prop k v = (K.fromString k, toJSON v) - data RecordA = RecordA { required :: String , optional :: NullableNonEmptyString } - deriving Generic + deriving (Eq, Show, Generic) data RecordB = RecordB { required :: String , optional :: NullableNonEmptyString } - deriving Generic + deriving (Eq, Show, Generic) + +data HRecordA a = HRecordA + { required :: String + , optional :: a + } + deriving (Eq, Show, Generic1) + +data HRecordB a = HRecordB + { required :: String + , optional :: a + } + deriving (Eq, Show, Generic1) encodeCase :: HasCallStack => ToJSON a => a -> Value -> IO () -encodeCase record object' = decode @Value (encode record) @?= Just object' +encodeCase record obj = do + decode @Value (encode record) @?= Just obj + decode @Value (encode (toJSON record)) @?= Just obj -decodeCase :: forall a. HasCallStack => (FromJSON a, ToJSON a) => a -> Value -> IO () -decodeCase record object' = (fmap encode . decode @a . encode) object' @?= Just (encode record) +decodeCase :: forall a. HasCallStack => (FromJSON a, Eq a, Show a) => a -> Value -> IO () +decodeCase record obj = do + decode @a (encode obj) @?= Just record counterCase :: forall a proxy. HasCallStack => (FromJSON a, ToJSON a) => proxy a -> Value -> IO () -counterCase _ object' = assertBool "decode should fail" $ (null . decode @a . encode) object' +counterCase _ obj = assertBool "decode should fail" $ isNothing (decode @a (encode obj)) helloWorldRecA :: RecordA helloWorldRecA = RecordA "hello" (nne "world") @@ -77,25 +96,37 @@ helloWorldRecA = RecordA "hello" (nne "world") helloWorldRecB :: RecordB helloWorldRecB = RecordB "hello" (nne "world") +helloWorldHRecA :: HRecordA NullableNonEmptyString +helloWorldHRecA = HRecordA "hello" (nne "world") + +helloWorldHRecB :: HRecordB NullableNonEmptyString +helloWorldHRecB = HRecordB "hello" (nne "world") + helloWorldObj :: Value -helloWorldObj = obj - [ prop "required" "hello" - , prop "optional" "world" +helloWorldObj = object + [ "required" .= String "hello" + , "optional" .= String "world" ] helloRecA :: RecordA -helloRecA = RecordA "hello" mempty +helloRecA = RecordA "hello" defaultNullableNonEmptyString helloRecB :: RecordB -helloRecB = RecordB "hello" mempty +helloRecB = RecordB "hello" defaultNullableNonEmptyString + +helloHRecA :: HRecordA NullableNonEmptyString +helloHRecA = HRecordA "hello" defaultNullableNonEmptyString + +helloHRecB :: HRecordB NullableNonEmptyString +helloHRecB = HRecordB "hello" defaultNullableNonEmptyString helloObj :: Value -helloObj = obj - [ prop "required" "hello" +helloObj = object + [ "required" .= String "hello" ] helloNullObj :: Value -helloNullObj = obj - [ prop "required" "hello" - , prop "optional" Null +helloNullObj = object + [ "required" .= String "hello" + , "optional" .= Null ] diff --git a/tests/UnitTests/OptionalFields/Generics.hs b/tests/UnitTests/OptionalFields/Generics.hs index 1513b7620..a3f9a3581 100644 --- a/tests/UnitTests/OptionalFields/Generics.hs +++ b/tests/UnitTests/OptionalFields/Generics.hs @@ -1,23 +1,98 @@ +{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-orphans #-} module UnitTests.OptionalFields.Generics (omitGenerics) where import UnitTests.OptionalFields.Common +------------------------------------------------------------------------------- +-- Ordinary +------------------------------------------------------------------------------- + instance ToJSON RecordA where - toJSON = genericToJSON defaultOptions { omitNothingFields = True } + toJSON = genericToJSON omittingOptions + toEncoding = genericToEncoding omittingOptions + +instance FromJSON RecordA where + parseJSON = genericParseJSON omittingOptions instance ToJSON RecordB where - toJSON = genericToJSON defaultOptions { omitNothingFields = False } + toJSON = genericToJSON nonOmittingOptions + toEncoding = genericToEncoding nonOmittingOptions + +instance FromJSON RecordB where + parseJSON = genericParseJSON nonOmittingOptions + + +------------------------------------------------------------------------------- +-- Higher +------------------------------------------------------------------------------- + +instance ToJSON1 HRecordA where + liftToJSON = genericLiftToJSON omittingOptions + liftToEncoding = genericLiftToEncoding omittingOptions + +instance FromJSON1 HRecordA where + liftParseJSON = genericLiftParseJSON omittingOptions + +instance ToJSON a => ToJSON (HRecordA a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON a => FromJSON (HRecordA a) where + parseJSON = parseJSON1 + + +instance ToJSON1 HRecordB where + liftToJSON = genericLiftToJSON nonOmittingOptions + liftToEncoding = genericLiftToEncoding nonOmittingOptions + +instance FromJSON1 HRecordB where + liftParseJSON = genericLiftParseJSON nonOmittingOptions + +instance ToJSON a => ToJSON (HRecordB a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON a => FromJSON (HRecordB a) where + parseJSON = parseJSON1 + +------------------------------------------------------------------------------- +-- Tests +------------------------------------------------------------------------------- omitGenerics :: TestTree omitGenerics = testGroup "Omit optional fields (Generics)" - [ testGroup "omitNothingFields = True" - [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecA helloWorldObj - , testCase "JSON should not include optional value." $ encodeCase helloRecA helloObj + [ testGroup "ordinary" + [ testGroup "omitNothingFields = True" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecA helloNullObj + ] + , testGroup "omitNothingFields = False" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @RecordB) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecB helloNullObj + ] ] - , testGroup "omitNothingFields = False" - [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecB helloWorldObj - , testCase "JSON should include optional value." $ encodeCase helloRecB helloNullObj + , testGroup "higher" + [ testGroup "omitNothingFields = True, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloHRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloHRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecA helloNullObj + ] + , testGroup "omitNothingFields = False, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloHRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @(HRecordB NullableNonEmptyString)) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecB helloNullObj + ] ] ] diff --git a/tests/UnitTests/OptionalFields/Manual.hs b/tests/UnitTests/OptionalFields/Manual.hs new file mode 100644 index 000000000..4ff76f0e1 --- /dev/null +++ b/tests/UnitTests/OptionalFields/Manual.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module UnitTests.OptionalFields.Manual (omitManual) where + +#if !MIN_VERSION_base(4,11,0) +import Data.Semigroup ((<>)) +#endif + +import UnitTests.OptionalFields.Common + +------------------------------------------------------------------------------- +-- Ordinary +------------------------------------------------------------------------------- + +instance ToJSON RecordA where + toJSON RecordA {..} = Object $ "required" .?= required <> "optional" .?= optional + toEncoding RecordA {..} = pairs $ "required" .?= required <> "optional" .?= optional + +instance FromJSON RecordA where + parseJSON = withObject "RecordA" $ \obj -> pure RecordA + <*> obj .:?= "required" + <*> obj .:?= "optional" + + +instance ToJSON RecordB where + toJSON RecordB {..} = Object $ "required" .= required <> "optional" .= optional + toEncoding RecordB {..} = pairs $ "required" .= required <> "optional" .= optional + +instance FromJSON RecordB where + parseJSON = withObject "RecordB" $ \obj -> pure RecordB + <*> obj .: "required" + <*> obj .: "optional" + +------------------------------------------------------------------------------- +-- Higher +------------------------------------------------------------------------------- + +instance ToJSON1 HRecordA where + liftToJSON o f _ HRecordA {..} = Object $ "required" .?= required <> explicitToFieldOmit o f "optional" optional + liftToEncoding o f _ HRecordA {..} = pairs $ "required" .?= required <> explicitToFieldOmit o f "optional" optional + +instance ToJSON a => ToJSON (HRecordA a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON1 HRecordA where + liftParseJSON o f _ = withObject "HRecordA" $ \obj -> pure HRecordA + <*> obj .:?= "required" + <*> explicitParseFieldOmit o f obj "optional" + +instance FromJSON a => FromJSON (HRecordA a) where + parseJSON = parseJSON1 + +instance ToJSON1 HRecordB where + liftToJSON _o f _ HRecordB {..} = Object $ "required" .?= required <> explicitToField f "optional" optional + liftToEncoding _o f _ HRecordB {..} = pairs $ "required" .?= required <> explicitToField f "optional" optional + +instance ToJSON a => ToJSON (HRecordB a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON1 HRecordB where + liftParseJSON _o f _ = withObject "HRecordB" $ \obj -> pure HRecordB + <*> obj .:?= "required" + <*> explicitParseField f obj "optional" + +instance FromJSON a => FromJSON (HRecordB a) where + parseJSON = parseJSON1 + +------------------------------------------------------------------------------- +-- Tests +------------------------------------------------------------------------------- + +omitManual :: TestTree +omitManual = testGroup "Omit optional fields (Manual)" + [ testGroup "ordinary" + [ testGroup "omitNothingFields = True" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecA helloNullObj + ] + , testGroup "omitNothingFields = False" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @RecordB) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecB helloNullObj + ] + ] + , testGroup "higher" + [ testGroup "omitNothingFields = True, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloHRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloHRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecA helloNullObj + ] + , testGroup "omitNothingFields = False, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloHRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @(HRecordB NullableNonEmptyString)) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecB helloNullObj + ] + ] + ] diff --git a/tests/UnitTests/OptionalFields/TH.hs b/tests/UnitTests/OptionalFields/TH.hs index b709e8f60..11df59c76 100644 --- a/tests/UnitTests/OptionalFields/TH.hs +++ b/tests/UnitTests/OptionalFields/TH.hs @@ -1,22 +1,63 @@ {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-orphans #-} +-- {-# OPTIONS_GHC -ddump-splices #-} module UnitTests.OptionalFields.TH (omitTH) where import UnitTests.OptionalFields.Common -$(deriveToJSON defaultOptions { omitNothingFields = True } ''RecordA) +$(deriveJSON omittingOptions ''RecordA) +$(deriveJSON nonOmittingOptions ''RecordB) +$(deriveJSON1 omittingOptions ''HRecordA) +$(deriveJSON1 nonOmittingOptions ''HRecordB) -$(deriveToJSON defaultOptions { omitNothingFields = False } ''RecordB) +instance ToJSON a => ToJSON (HRecordA a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON a => FromJSON (HRecordA a) where + parseJSON = parseJSON1 + +instance ToJSON a => ToJSON (HRecordB a) where + toJSON = toJSON1 + toEncoding = toEncoding1 + +instance FromJSON a => FromJSON (HRecordB a) where + parseJSON = parseJSON1 omitTH :: TestTree omitTH = testGroup "Omit optional fields (TH)" - [ testGroup "omitNothingFields = True" - [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecA helloWorldObj - , testCase "JSON should not include optional value." $ encodeCase helloRecA helloObj + [ testGroup "ordinary" + [ testGroup "omitNothingFields = True" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecA helloNullObj + ] + , testGroup "omitNothingFields = False" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @RecordB) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloRecB helloNullObj + ] ] - , testGroup "omitNothingFields = False" - [ testCase "JSON should include non-optional value." $ encodeCase helloWorldRecB helloWorldObj - , testCase "JSON should include optional value." $ encodeCase helloRecB helloNullObj + , testGroup "higher" + [ testGroup "omitNothingFields = True, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecA helloWorldObj + , testCase "JSON should not include optional value." $ encodeCase helloHRecA helloObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecA helloWorldObj + , testCase "JSON decode not including optional value" $ decodeCase helloHRecA helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecA helloNullObj + ] + , testGroup "omitNothingFields = False, higher" + [ testCase "JSON should include non-optional value." $ encodeCase helloWorldHRecB helloWorldObj + , testCase "JSON should include optional value." $ encodeCase helloHRecB helloNullObj + , testCase "JSON decode including non-optional value" $ decodeCase helloWorldHRecB helloWorldObj + , testCase "JSON decode not including optional value" $ counterCase (Proxy @(HRecordB NullableNonEmptyString)) helloObj + , testCase "JSON decode including optional value" $ decodeCase helloHRecB helloNullObj + ] ] ]