@@ -45,6 +45,9 @@ module Data.Aeson.Types.FromJSON
45 , coerceFromJSONKeyFunction
45 , coerceFromJSONKeyFunction
46 , mapFromJSONKeyFunction
46 , mapFromJSONKeyFunction
48 -- * List functions
51 -- * List functions
49 , listParser
52 , listParser
@@ -406,6 +409,16 @@ class FromJSON a where
406-- > newtype SomeId = SomeId { getSomeId :: Text }
409-- > newtype SomeId = SomeId { getSomeId :: Text }
407-- > deriving (Eq,Ord,Hashable,FromJSONKey)
410-- > deriving (Eq,Ord,Hashable,FromJSONKey)
412+-- If you have a sum of nullary constructors, you may use the generic
416+-- data Color = Red | Green | Blue
419+-- instance 'FromJSONKey' Color where
420+-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
409class FromJSONKey a where
422class FromJSONKey a where
410 -- | Strategy for parsing the key of a map-like container.
423 -- | Strategy for parsing the key of a map-like container.
411 fromJSONKey :: FromJSONKeyFunction a
424 fromJSONKey :: FromJSONKeyFunction a
@@ -504,6 +517,35 @@ coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fma
504mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
517mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
505mapFromJSONKeyFunction = fmap
518mapFromJSONKeyFunction = fmap
520+-- | 'fromJSONKey' for 'Generic' types.
521+-- These types must be sums of nullary constructors, whose names will be used
522+-- as keys for JSON objects.
524+-- See also 'genericToJSONKey'.
529+-- data Color = Red | Green | Blue
530+-- deriving 'Generic'
532+-- instance 'FromJSONKey' Color where
533+-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
535+genericFromJSONKey :: forall a. (Generic a, GFromJSONKey (Rep a))
537+ -> FromJSONKeyFunction a
538+genericFromJSONKey opts = FromJSONKeyTextParser $ \t ->
539+ case parseSumFromString (keyModifier opts) t of
541+ "invalid key " ++ show t ++ ", expected one of " ++ show cnames
542+ Just k -> pure (to k)
544+ cnames = unTagged2 (constructorTags (keyModifier opts) :: Tagged2 (Rep a) [String])
546+class (ConstructorNames f, SumFromString f) => GFromJSONKey f where
547+instance (ConstructorNames f, SumFromString f) => GFromJSONKey f where
507-------------------------------------------------------------------------------
549-------------------------------------------------------------------------------
508-- Functions needed for documentation
550-- Functions needed for documentation
509-------------------------------------------------------------------------------
551-------------------------------------------------------------------------------
@@ -1020,41 +1062,46 @@ parseAllNullarySum :: (SumFromString f, ConstructorNames f)
1020parseAllNullarySum tname opts =
1062parseAllNullarySum tname opts =
1021 withText tname $ \tag ->
1063 withText tname $ \tag ->
1022 maybe (badTag tag) return $
1064 maybe (badTag tag) return $
1023− parseSumFromString opts tag
1065+ parseSumFromString modifier tag
1025− badTag tag = failWithCTags tname opts $ \cnames ->
1067+ badTag tag = failWithCTags tname modifier $ \cnames ->
1026 "expected one of the tags " ++ show cnames ++
1068 "expected one of the tags " ++ show cnames ++
1027 ", but found tag " ++ show tag
1069 ", but found tag " ++ show tag
1070+ modifier = constructorTagModifier opts
1029-- | Fail with an informative error message about a mismatched tag.
1072-- | Fail with an informative error message about a mismatched tag.
1030-- The error message is parameterized by the list of expected tags,
1073-- The error message is parameterized by the list of expected tags,
1031-- to be inferred from the result type of the parser.
1074-- to be inferred from the result type of the parser.
1032failWithCTags
1075failWithCTags
1033− :: forall f a. ConstructorNames f
1076+ :: forall f a t. ConstructorNames f
1034− => TypeName -> Options -> ([String] -> String) -> Parser (f a)
1077+ => TypeName -> (String -> t) -> ([t] -> String) -> Parser (f a)
1035−failWithCTags tname opts f =
1078+failWithCTags tname modifier f =
1036 contextType tname . fail $ f cnames
1079 contextType tname . fail $ f cnames
1038− cnames = unTagged2 (constructorTags opts :: Tagged2 f [String])
1081+ cnames = unTagged2 (constructorTags modifier :: Tagged2 f [t])
1040class SumFromString f where
1083class SumFromString f where
1041− parseSumFromString :: Options -> Text -> Maybe (f a)
1084+ parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
1043instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1086instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1044 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1087 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1045 (R1 <$> parseSumFromString opts key)
1088 (R1 <$> parseSumFromString opts key)
1047instance (Constructor c) => SumFromString (C1 c U1) where
1090instance (Constructor c) => SumFromString (C1 c U1) where
1048− parseSumFromString opts key | key == name = Just $ M1 U1
1091+ parseSumFromString modifier key
1092+ | key == name = Just $ M1 U1
1049− | otherwise = Nothing
1093+ | otherwise = Nothing
1051− name = pack $ constructorTagModifier opts $
1052− conName (undefined :: M1 _i c _f _p)
1095+ name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
1097+-- For genericFromJSONKey
1098+instance SumFromString a => SumFromString (D1 d a) where
1099+ parseSumFromString modifier key = M1 <$> parseSumFromString modifier key
1054-- | List of all constructor tags.
1101-- | List of all constructor tags.
1055−constructorTags :: ConstructorNames a => Options -> Tagged2 a [String]
1102+constructorTags :: ConstructorNames a => (String -> t) -> Tagged2 a [t]
1056−constructorTags opts =
1103+constructorTags modifier =
1057− fmap DList.toList (constructorNames' (constructorTagModifier opts))
1104+ fmap DList.toList (constructorNames' modifier)
1059-- | List of all constructor names of an ADT, after a given conversion
1106-- | List of all constructor names of an ADT, after a given conversion
1060-- function. (Better inlining.)
1107-- function. (Better inlining.)
@@ -1075,6 +1122,13 @@ instance Constructor c => ConstructorNames (C1 c a) where
1076 cname = conName (undefined :: M1 _i c _f _p)
1123 cname = conName (undefined :: M1 _i c _f _p)
1125+-- For genericFromJSONKey
1126+instance ConstructorNames a => ConstructorNames (D1 d a) where
1127+ constructorNames' = retag . constructorNames'
1129+ retag :: Tagged2 a u -> Tagged2 (D1 d a) u
1130+ retag (Tagged2 x) = Tagged2 x
1078--------------------------------------------------------------------------------
1132--------------------------------------------------------------------------------
1080parseNonAllNullarySum :: ( FromPair arity f
1134parseNonAllNullarySum :: ( FromPair arity f
@@ -1126,7 +1180,7 @@ parseNonAllNullarySum p@(tname :* opts :* _) =
1127 UntaggedValue -> parseUntaggedValue p
1181 UntaggedValue -> parseUntaggedValue p
1129− failWith_ = failWithCTags tname opts
1183+ failWith_ = failWithCTags tname (constructorTagModifier opts)
1131--------------------------------------------------------------------------------
1185--------------------------------------------------------------------------------