@@ -5,20 +5,16 @@
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NamedFieldPuns #-}
9{-# LANGUAGE NoImplicitPrelude #-}
8{-# LANGUAGE NoImplicitPrelude #-}
10{-# LANGUAGE OverloadedStrings #-}
9{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE RecordWildCards #-}
11{-# LANGUAGE RecordWildCards #-}
12{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE TypeOperators #-}
13{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE TypeSynonymInstances #-}
14+{-# LANGUAGE TupleSections #-}
15{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE ViewPatterns #-}
16{-# LANGUAGE ViewPatterns #-}
18−#if __GLASGOW_HASKELL__ >= 706
19−{-# LANGUAGE PolyKinds #-}
22#include "incoherent-compat.h"
18#include "incoherent-compat.h"
23#include "overlapping-compat.h"
19#include "overlapping-compat.h"
@@ -46,6 +42,9 @@ module Data.Aeson.Types.FromJSON
46 , coerceFromJSONKeyFunction
42 , coerceFromJSONKeyFunction
47 , mapFromJSONKeyFunction
43 , mapFromJSONKeyFunction
49 -- * List functions
48 -- * List functions
50 , listParser
49 , listParser
@@ -88,7 +87,7 @@ import Data.Aeson.Parser.Internal (eitherDecodeWith, jsonEOF)
88import Data.Aeson.Types.Generic
87import Data.Aeson.Types.Generic
89import Data.Aeson.Types.Internal
88import Data.Aeson.Types.Internal
90import Data.Bits (unsafeShiftR)
89import Data.Bits (unsafeShiftR)
91−import Data.Fixed (Fixed, HasResolution)
90+import Data.Fixed (Fixed, HasResolution (resolution), Nano)
92import Data.Functor.Compose (Compose(..))
91import Data.Functor.Compose (Compose(..))
93import Data.Functor.Identity (Identity(..))
92import Data.Functor.Identity (Identity(..))
94import Data.Functor.Product (Product(..))
93import Data.Functor.Product (Product(..))
@@ -104,8 +103,10 @@ import Data.Scientific (Scientific, base10Exponent)
104import Data.Tagged (Tagged(..))
103import Data.Tagged (Tagged(..))
105import Data.Text (Text, pack, unpack)
104import Data.Text (Text, pack, unpack)
106import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
105import Data.Time (Day, DiffTime, LocalTime, NominalDiffTime, TimeOfDay, UTCTime, ZonedTime)
107−import Data.Time.Format (parseTime)
106+import Data.Time.Calendar.Compat (CalendarDiffDays (..), DayOfWeek (..))
107+import Data.Time.LocalTime.Compat (CalendarDiffTime (..))
108+import Data.Time.Clock.System.Compat (SystemTime (..))
108−import Data.Time.Locale.Compat (defaultTimeLocale)
109+import Data.Time.Format.Compat (parseTimeM, defaultTimeLocale)
109import Data.Traversable as Tr (sequence)
110import Data.Traversable as Tr (sequence)
110import Data.Vector (Vector)
111import Data.Vector (Vector)
111import Data.Version (Version, parseVersion)
112import Data.Version (Version, parseVersion)
@@ -117,9 +118,9 @@ import GHC.Generics
117import Numeric.Natural (Natural)
118import Numeric.Natural (Natural)
118import Text.ParserCombinators.ReadP (readP_to_S)
119import Text.ParserCombinators.ReadP (readP_to_S)
119import Unsafe.Coerce (unsafeCoerce)
120import Unsafe.Coerce (unsafeCoerce)
120−import qualified Data.Aeson.Compat as Compat
121import qualified Data.Aeson.Parser.Time as Time
121import qualified Data.Aeson.Parser.Time as Time
122import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
122import qualified Data.Attoparsec.ByteString.Char8 as A (endOfInput, parseOnly, scientific)
123+import qualified Data.ByteString.Lazy as L
123import qualified Data.DList as DList
124import qualified Data.DList as DList
124import qualified Data.HashMap.Strict as H
125import qualified Data.HashMap.Strict as H
125import qualified Data.HashSet as HashSet
126import qualified Data.HashSet as HashSet
@@ -148,22 +149,14 @@ import qualified Data.Primitive.SmallArray as PM
148import qualified Data.Primitive.Types as PM
149import qualified Data.Primitive.Types as PM
150#if MIN_VERSION_primitive(0,6,4)
151#if MIN_VERSION_primitive(0,6,4)
152+#if !MIN_VERSION_primitive(0,7,0)
151import qualified Data.Primitive.UnliftedArray as PM
153import qualified Data.Primitive.UnliftedArray as PM
152import qualified Data.Primitive.PrimArray as PM
155import qualified Data.Primitive.PrimArray as PM
155−#ifndef HAS_COERCIBLE
156−#define HAS_COERCIBLE (__GLASGOW_HASKELL__ >= 707)
160import Data.Coerce (Coercible, coerce)
159import Data.Coerce (Coercible, coerce)
161−coerce' :: Coercible a b => a -> b
165−coerce' = unsafeCoerce
168parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
161parseIndexedJSON :: (Value -> Parser a) -> Int -> Value -> Parser a
169parseIndexedJSON p idx value = p value <?> Index idx
162parseIndexedJSON p idx value = p value <?> Index idx
@@ -403,6 +396,16 @@ class FromJSON a where
403-- > newtype SomeId = SomeId { getSomeId :: Text }
396-- > newtype SomeId = SomeId { getSomeId :: Text }
404-- > deriving (Eq,Ord,Hashable,FromJSONKey)
397-- > deriving (Eq,Ord,Hashable,FromJSONKey)
399+-- If you have a sum of nullary constructors, you may use the generic
403+-- data Color = Red | Green | Blue
406+-- instance 'FromJSONKey' Color where
407+-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
406class FromJSONKey a where
409class FromJSONKey a where
407 -- | Strategy for parsing the key of a map-like container.
410 -- | Strategy for parsing the key of a map-like container.
408 fromJSONKey :: FromJSONKeyFunction a
411 fromJSONKey :: FromJSONKeyFunction a
@@ -425,11 +428,7 @@ class FromJSONKey a where
425-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
428-- At the moment this type is intentionally not exported. 'FromJSONKeyFunction'
426-- can be inspected, but cannot be constructed.
429-- can be inspected, but cannot be constructed.
427data CoerceText a where
430data CoerceText a where
429 CoerceText :: Coercible Text a => CoerceText a
431 CoerceText :: Coercible Text a => CoerceText a
431− CoerceText :: CoerceText a
434-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
433-- | This type is related to 'ToJSONKeyFunction'. If 'FromJSONKeyValue' is used in the
435-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
434-- 'FromJSONKey' instance, then 'ToJSONKeyValue' should be used in the 'ToJSONKey'
@@ -449,7 +448,7 @@ data FromJSONKeyFunction a
450-- | Only law abiding up to interpretation
449-- | Only law abiding up to interpretation
451instance Functor FromJSONKeyFunction where
450instance Functor FromJSONKeyFunction where
452− fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce')
451+ fmap h (FromJSONKeyCoerce CoerceText) = FromJSONKeyText (h . coerce)
453 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
452 fmap h (FromJSONKeyText f) = FromJSONKeyText (h . f)
454 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
453 fmap h (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap h . f)
455 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
454 fmap h (FromJSONKeyValue f) = FromJSONKeyValue (fmap h . f)
@@ -463,9 +462,7 @@ instance Functor FromJSONKeyFunction where
464-- On pre GHC 7.8 this is unconstrainted function.
463-- On pre GHC 7.8 this is unconstrainted function.
465fromJSONKeyCoerce ::
464fromJSONKeyCoerce ::
467 Coercible Text a =>
465 Coercible Text a =>
469 FromJSONKeyFunction a
466 FromJSONKeyFunction a
470fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
467fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
@@ -473,34 +470,52 @@ fromJSONKeyCoerce = FromJSONKeyCoerce CoerceText
474-- See note on 'fromJSONKeyCoerce'.
471-- See note on 'fromJSONKeyCoerce'.
475coerceFromJSONKeyFunction ::
472coerceFromJSONKeyFunction ::
477 Coercible a b =>
473 Coercible a b =>
479 FromJSONKeyFunction a -> FromJSONKeyFunction b
474 FromJSONKeyFunction a -> FromJSONKeyFunction b
481coerceFromJSONKeyFunction = coerce
475coerceFromJSONKeyFunction = coerce
483−coerceFromJSONKeyFunction (FromJSONKeyCoerce CoerceText) = FromJSONKeyCoerce CoerceText
484−coerceFromJSONKeyFunction (FromJSONKeyText f) = FromJSONKeyText (coerce' . f)
485−coerceFromJSONKeyFunction (FromJSONKeyTextParser f) = FromJSONKeyTextParser (fmap coerce' . f)
486−coerceFromJSONKeyFunction (FromJSONKeyValue f) = FromJSONKeyValue (fmap coerce' . f)
490 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
478 "FromJSONKeyCoerce: fmap id" forall (x :: FromJSONKeyFunction a).
491 fmap id x = x
479 fmap id x = x
495 "FromJSONKeyCoerce: fmap coerce" forall x .
482 "FromJSONKeyCoerce: fmap coerce" forall x .
496 fmap coerce x = coerceFromJSONKeyFunction x
483 fmap coerce x = coerceFromJSONKeyFunction x
500-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
486-- | Same as 'fmap'. Provided for the consistency with 'ToJSONKeyFunction'.
501mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
487mapFromJSONKeyFunction :: (a -> b) -> FromJSONKeyFunction a -> FromJSONKeyFunction b
502mapFromJSONKeyFunction = fmap
488mapFromJSONKeyFunction = fmap
490+-- | 'fromJSONKey' for 'Generic' types.
491+-- These types must be sums of nullary constructors, whose names will be used
492+-- as keys for JSON objects.
494+-- See also 'genericToJSONKey'.
499+-- data Color = Red | Green | Blue
500+-- deriving 'Generic'
502+-- instance 'FromJSONKey' Color where
503+-- 'fromJSONKey' = 'genericFromJSONKey' 'defaultJSONKeyOptions'
505+genericFromJSONKey :: forall a. (Generic a, GFromJSONKey (Rep a))
507+ -> FromJSONKeyFunction a
508+genericFromJSONKey opts = FromJSONKeyTextParser $ \t ->
509+ case parseSumFromString (keyModifier opts) t of
511+ "invalid key " ++ show t ++ ", expected one of " ++ show cnames
512+ Just k -> pure (to k)
514+ cnames = unTagged2 (constructorTags (keyModifier opts) :: Tagged2 (Rep a) [String])
516+class (ConstructorNames f, SumFromString f) => GFromJSONKey f where
517+instance (ConstructorNames f, SumFromString f) => GFromJSONKey f where
504-------------------------------------------------------------------------------
519-------------------------------------------------------------------------------
505-- Functions needed for documentation
520-- Functions needed for documentation
506-------------------------------------------------------------------------------
521-------------------------------------------------------------------------------
@@ -753,13 +768,13 @@ withBoundedScientific' f v = withBoundedScientific_ id f v
753-- | A variant of 'withBoundedScientific_' parameterized by a function to apply
768-- | A variant of 'withBoundedScientific_' parameterized by a function to apply
754-- to the 'Parser' in case of failure.
769-- to the 'Parser' in case of failure.
755withBoundedScientific_ :: (Parser a -> Parser a) -> (Scientific -> Parser a) -> Value -> Parser a
770withBoundedScientific_ :: (Parser a -> Parser a) -> (Scientific -> Parser a) -> Value -> Parser a
756−withBoundedScientific_ whenFail f v@(Number scientific) =
771+withBoundedScientific_ whenFail f (Number scientific) =
757− if exponent > 1024
772+ if exp10 > 1024
758 then whenFail (fail msg)
773 then whenFail (fail msg)
759 else f scientific
774 else f scientific
761− exponent = base10Exponent scientific
776+ exp10 = base10Exponent scientific
762− msg = "found a number with exponent " ++ show exponent ++ ", but it must not be greater than 1024"
777+ msg = "found a number with exponent " ++ show exp10 ++ ", but it must not be greater than 1024"
763withBoundedScientific_ whenFail _ v =
778withBoundedScientific_ whenFail _ v =
764 whenFail (typeMismatch "Number" v)
779 whenFail (typeMismatch "Number" v)
765{-# INLINE withBoundedScientific_ #-}
780{-# INLINE withBoundedScientific_ #-}
@@ -779,7 +794,7 @@ withBool name _ v = prependContext name (typeMismatch "Boolean" v)
779-- | Decode a nested JSON-encoded string.
794-- | Decode a nested JSON-encoded string.
780withEmbeddedJSON :: String -> (Value -> Parser a) -> Value -> Parser a
795withEmbeddedJSON :: String -> (Value -> Parser a) -> Value -> Parser a
781withEmbeddedJSON _ innerParser (String txt) =
796withEmbeddedJSON _ innerParser (String txt) =
782− either fail innerParser $ eitherDecode (Compat.fromStrict $ T.encodeUtf8 txt)
797+ either fail innerParser $ eitherDecode (L.fromStrict $ T.encodeUtf8 txt)
784 eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON
799 eitherDecode = eitherFormatError . eitherDecodeWith jsonEOF ifromJSON
785 eitherFormatError = either (Left . uncurry formatError) Right
800 eitherFormatError = either (Left . uncurry formatError) Right
@@ -848,7 +863,7 @@ parseFieldMaybe' = (.:!)
848-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
863-- E.g. @'explicitParseField' 'parseJSON1' :: ('FromJSON1' f, 'FromJSON' a) -> 'Object' -> 'Text' -> 'Parser' (f a)@
849explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
864explicitParseField :: (Value -> Parser a) -> Object -> Text -> Parser a
850explicitParseField p obj key = case H.lookup key obj of
865explicitParseField p obj key = case H.lookup key obj of
851− Nothing -> fail $ "key " ++ show key ++ " not present"
866+ Nothing -> fail $ "key " ++ show key ++ " not found"
852 Just v -> p v <?> Key key
867 Just v -> p v <?> Key key
853{-# INLINE explicitParseField #-}
868{-# INLINE explicitParseField #-}
@@ -907,6 +922,16 @@ type ConName = String
907contextType :: TypeName -> Parser a -> Parser a
922contextType :: TypeName -> Parser a -> Parser a
908contextType = prependContext
923contextType = prependContext
925+-- | Add the tagKey that will be looked up while building an ADT
926+-- | Produce the error equivalent to
927+-- | Left "Error in $: parsing T failed, expected an object with keys "tag" and
928+-- | "contents", where "tag" i-- |s associated to one of ["Foo", "Bar"],
929+-- | The parser returned error was: could not find key "tag"
930+contextTag :: Text -> [String] -> Parser a -> Parser a
931+contextTag tagKey cnames = prependFailure
932+ ("expected Object with key \"" ++ unpack tagKey ++ "\"" ++
933+ " containing one of " ++ show cnames ++ ", ")
910-- | Add the name of the constructor being parsed to a parser's error messages.
935-- | Add the name of the constructor being parsed to a parser's error messages.
911contextCons :: ConName -> TypeName -> Parser a -> Parser a
936contextCons :: ConName -> TypeName -> Parser a -> Parser a
912contextCons cname tname = prependContext (showCons cname tname)
937contextCons cname tname = prependContext (showCons cname tname)
@@ -1017,41 +1042,46 @@ parseAllNullarySum :: (SumFromString f, ConstructorNames f)
1017parseAllNullarySum tname opts =
1042parseAllNullarySum tname opts =
1018 withText tname $ \tag ->
1043 withText tname $ \tag ->
1019 maybe (badTag tag) return $
1044 maybe (badTag tag) return $
1020− parseSumFromString opts tag
1045+ parseSumFromString modifier tag
1022− badTag tag = failWithCTags tname opts $ \cnames ->
1047+ badTag tag = failWithCTags tname modifier $ \cnames ->
1023 "expected one of the tags " ++ show cnames ++
1048 "expected one of the tags " ++ show cnames ++
1024 ", but found tag " ++ show tag
1049 ", but found tag " ++ show tag
1050+ modifier = constructorTagModifier opts
1026-- | Fail with an informative error message about a mismatched tag.
1052-- | Fail with an informative error message about a mismatched tag.
1027-- The error message is parameterized by the list of expected tags,
1053-- The error message is parameterized by the list of expected tags,
1028-- to be inferred from the result type of the parser.
1054-- to be inferred from the result type of the parser.
1029failWithCTags
1055failWithCTags
1030− :: forall f a. ConstructorNames f
1056+ :: forall f a t. ConstructorNames f
1031− => TypeName -> Options -> ([String] -> String) -> Parser (f a)
1057+ => TypeName -> (String -> t) -> ([t] -> String) -> Parser (f a)
1032−failWithCTags tname opts f =
1058+failWithCTags tname modifier f =
1033 contextType tname . fail $ f cnames
1059 contextType tname . fail $ f cnames
1035− cnames = unTagged2 (constructorTags opts :: Tagged2 f [String])
1061+ cnames = unTagged2 (constructorTags modifier :: Tagged2 f [t])
1037class SumFromString f where
1063class SumFromString f where
1038− parseSumFromString :: Options -> Text -> Maybe (f a)
1064+ parseSumFromString :: (String -> String) -> Text -> Maybe (f a)
1040instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1066instance (SumFromString a, SumFromString b) => SumFromString (a :+: b) where
1041 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1067 parseSumFromString opts key = (L1 <$> parseSumFromString opts key) <|>
1042 (R1 <$> parseSumFromString opts key)
1068 (R1 <$> parseSumFromString opts key)
1044instance (Constructor c) => SumFromString (C1 c U1) where
1070instance (Constructor c) => SumFromString (C1 c U1) where
1045− parseSumFromString opts key | key == name = Just $ M1 U1
1071+ parseSumFromString modifier key
1072+ | key == name = Just $ M1 U1
1046− | otherwise = Nothing
1073+ | otherwise = Nothing
1048− name = pack $ constructorTagModifier opts $
1049− conName (undefined :: M1 _i c _f _p)
1075+ name = pack $ modifier $ conName (undefined :: M1 _i c _f _p)
1077+-- For genericFromJSONKey
1078+instance SumFromString a => SumFromString (D1 d a) where
1079+ parseSumFromString modifier key = M1 <$> parseSumFromString modifier key
1051-- | List of all constructor tags.
1081-- | List of all constructor tags.
1052−constructorTags :: ConstructorNames a => Options -> Tagged2 a [String]
1082+constructorTags :: ConstructorNames a => (String -> t) -> Tagged2 a [t]
1053−constructorTags opts =
1083+constructorTags modifier =
1054− fmap DList.toList (constructorNames' (constructorTagModifier opts))
1084+ fmap DList.toList (constructorNames' modifier)
1056-- | List of all constructor names of an ADT, after a given conversion
1086-- | List of all constructor names of an ADT, after a given conversion
1057-- function. (Better inlining.)
1087-- function. (Better inlining.)
@@ -1072,9 +1102,16 @@ instance Constructor c => ConstructorNames (C1 c a) where
1073 cname = conName (undefined :: M1 _i c _f _p)
1103 cname = conName (undefined :: M1 _i c _f _p)
1075−--------------------------------------------------------------------------------
1105+-- For genericFromJSONKey
1106+instance ConstructorNames a => ConstructorNames (D1 d a) where
1107+ constructorNames' = retag . constructorNames'
1109+ retag :: Tagged2 a u -> Tagged2 (D1 d a) u
1110+ retag (Tagged2 x) = Tagged2 x
1112+--------------------------------------------------------------------------------
1077−parseNonAllNullarySum :: ( FromPair arity f
1113+parseNonAllNullarySum :: forall f c arity.
1078 , FromTaggedObject arity f
1115 , FromTaggedObject arity f
1079 , FromUntaggedValue arity f
1116 , FromUntaggedValue arity f
1080 , ConstructorNames f
1117 , ConstructorNames f
@@ -1084,7 +1121,7 @@ parseNonAllNullarySum p@(tname :* opts :* _) =
1084 case sumEncoding opts of
1121 case sumEncoding opts of
1085 TaggedObject{..} ->
1122 TaggedObject{..} ->
1086 withObject tname $ \obj -> do
1123 withObject tname $ \obj -> do
1087− tag <- contextType tname $ obj .: tagKey
1124+ tag <- contextType tname . contextTag tagKey cnames_ $ obj .: tagKey
1088 fromMaybe (badTag tag <?> Key tagKey) $
1125 fromMaybe (badTag tag <?> Key tagKey) $
1089 parseFromTaggedObject (tag :* contentsFieldName :* p) obj
1126 parseFromTaggedObject (tag :* contentsFieldName :* p) obj
@@ -1092,6 +1129,7 @@ parseNonAllNullarySum p@(tname :* opts :* _) =
1092 badTag tag = failWith_ $ \cnames ->
1129 badTag tag = failWith_ $ \cnames ->
1093 "expected tag field to be one of " ++ show cnames ++
1130 "expected tag field to be one of " ++ show cnames ++
1094 ", but found tag " ++ show tag
1131 ", but found tag " ++ show tag
1132+ cnames_ = unTagged2 (constructorTags (constructorTagModifier opts) :: Tagged2 f [String])
1096 ObjectWithSingleField ->
1134 ObjectWithSingleField ->
1097 withObject tname $ \obj -> case H.toList obj of
1135 withObject tname $ \obj -> case H.toList obj of
@@ -1123,7 +1161,7 @@ parseNonAllNullarySum p@(tname :* opts :* _) =
1124 UntaggedValue -> parseUntaggedValue p
1162 UntaggedValue -> parseUntaggedValue p
1126− failWith_ = failWithCTags tname opts
1164+ failWith_ = failWithCTags tname (constructorTagModifier opts)
1128--------------------------------------------------------------------------------
1166--------------------------------------------------------------------------------
@@ -1163,9 +1201,9 @@ class FromTaggedObject' arity f isRecord where
1163 :: String :* ConName :* TypeName :* Options :* FromArgs arity a
1201 :: String :* ConName :* TypeName :* Options :* FromArgs arity a
1164 -> Object -> Tagged isRecord (Parser (f a))
1202 -> Object -> Tagged isRecord (Parser (f a))
1166−instance (RecordFromJSON arity f) => FromTaggedObject' arity f True where
1204+instance (RecordFromJSON arity f, FieldNames f) => FromTaggedObject' arity f True where
1167 -- Records are unpacked in the tagged object
1205 -- Records are unpacked in the tagged object
1168− parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON p
1206+ parseFromTaggedObject' (_ :* p) = Tagged . recordParseJSON (True :* p)
1170instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
1208instance (ConsFromJSON arity f) => FromTaggedObject' arity f False where
1171 -- Nonnullary nonrecords are encoded in the contents field
1209 -- Nonnullary nonrecords are encoded in the contents field
@@ -1206,11 +1244,11 @@ instance OVERLAPPING_
1206 ) => ConsFromJSON' arity (S1 s a) True where
1244 ) => ConsFromJSON' arity (S1 s a) True where
1207 consParseJSON' p@(cname :* tname :* opts :* fargs)
1245 consParseJSON' p@(cname :* tname :* opts :* fargs)
1208 | unwrapUnaryRecords opts = Tagged . fmap M1 . gParseJSON opts fargs
1246 | unwrapUnaryRecords opts = Tagged . fmap M1 . gParseJSON opts fargs
1209− | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON p)
1247+ | otherwise = Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1211instance RecordFromJSON arity f => ConsFromJSON' arity f True where
1249instance RecordFromJSON arity f => ConsFromJSON' arity f True where
1212 consParseJSON' p@(cname :* tname :* _) =
1250 consParseJSON' p@(cname :* tname :* _) =
1213− Tagged . withObject (showCons cname tname) (recordParseJSON p)
1251+ Tagged . withObject (showCons cname tname) (recordParseJSON (False :* p))
1215instance OVERLAPPING_
1253instance OVERLAPPING_
1216 ConsFromJSON' arity U1 False where
1254 ConsFromJSON' arity U1 False where
@@ -1236,21 +1274,55 @@ instance (ProductFromJSON arity f, ProductSize f
1237--------------------------------------------------------------------------------
1275--------------------------------------------------------------------------------
1277+class FieldNames f where
1278+ fieldNames :: f a -> [Text] -> [Text]
1280+instance (FieldNames a, FieldNames b) => FieldNames (a :*: b) where
1282+ fieldNames (undefined :: a x) .
1283+ fieldNames (undefined :: b y)
1285+instance (Selector s) => FieldNames (S1 s f) where
1286+ fieldNames _ = (pack (selName (undefined :: M1 _i s _f _p)) :)
1239class RecordFromJSON arity f where
1288class RecordFromJSON arity f where
1240 recordParseJSON
1289 recordParseJSON
1290+ :: Bool :* ConName :* TypeName :* Options :* FromArgs arity a
1291+ -> Object -> Parser (f a)
1293+instance ( FieldNames f
1294+ , RecordFromJSON' arity f
1295+ ) => RecordFromJSON arity f where
1296+ recordParseJSON (fromTaggedSum :* p@(cname :* tname :* opts :* _)) =
1297+ \obj -> checkUnknown obj >> recordParseJSON' p obj
1299+ knownFields :: H.HashMap Text ()
1300+ knownFields = H.fromList $ map (,()) $
1301+ fieldNames (undefined :: f a)
1302+ [pack (tagFieldName (sumEncoding opts)) | fromTaggedSum]
1304+ if not (rejectUnknownFields opts)
1305+ then \_ -> return ()
1306+ else \obj -> case H.keys (H.difference obj knownFields) of
1308+ unknownFields -> contextCons cname tname $
1309+ fail ("unknown fields: " ++ show unknownFields)
1311+class RecordFromJSON' arity f where
1241 :: ConName :* TypeName :* Options :* FromArgs arity a
1313 :: ConName :* TypeName :* Options :* FromArgs arity a
1242 -> Object -> Parser (f a)
1314 -> Object -> Parser (f a)
1244−instance ( RecordFromJSON arity a
1316+instance ( RecordFromJSON' arity a
1245− , RecordFromJSON arity b
1317+ , RecordFromJSON' arity b
1246− ) => RecordFromJSON arity (a :*: b) where
1318+ ) => RecordFromJSON' arity (a :*: b) where
1247− recordParseJSON p obj =
1319+ recordParseJSON' p obj =
1248− (:*:) <$> recordParseJSON p obj
1320+ (:*:) <$> recordParseJSON' p obj
1249− <*> recordParseJSON p obj
1321+ <*> recordParseJSON' p obj
1251instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1323instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1252− RecordFromJSON arity (S1 s a) where
1324+ RecordFromJSON' arity (S1 s a) where
1253− recordParseJSON (cname :* tname :* opts :* fargs) obj = do
1325+ recordParseJSON' (cname :* tname :* opts :* fargs) obj = do
1254 fv <- contextCons cname tname (obj .: label)
1326 fv <- contextCons cname tname (obj .: label)
1255 M1 <$> gParseJSON opts fargs fv <?> Key label
1327 M1 <$> gParseJSON opts fargs fv <?> Key label
@@ -1258,16 +1330,16 @@ instance OVERLAPPABLE_ (Selector s, GFromJSON arity a) =>
1258 sname = selName (undefined :: M1 _i s _f _p)
1330 sname = selName (undefined :: M1 _i s _f _p)
1260instance INCOHERENT_ (Selector s, FromJSON a) =>
1332instance INCOHERENT_ (Selector s, FromJSON a) =>
1261− RecordFromJSON arity (S1 s (K1 i (Maybe a))) where
1333+ RecordFromJSON' arity (S1 s (K1 i (Maybe a))) where
1262− recordParseJSON (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1334+ recordParseJSON' (_ :* _ :* opts :* _) obj = M1 . K1 <$> obj .:? pack label
1264 label = fieldLabelModifier opts sname
1336 label = fieldLabelModifier opts sname
1265 sname = selName (undefined :: M1 _i s _f _p)
1337 sname = selName (undefined :: M1 _i s _f _p)
1267-- Parse an Option like a Maybe.
1339-- Parse an Option like a Maybe.
1268instance INCOHERENT_ (Selector s, FromJSON a) =>
1340instance INCOHERENT_ (Selector s, FromJSON a) =>
1269− RecordFromJSON arity (S1 s (K1 i (Semigroup.Option a))) where
1341+ RecordFromJSON' arity (S1 s (K1 i (Semigroup.Option a))) where
1270− recordParseJSON p obj = wrap <$> recordParseJSON p obj
1342+ recordParseJSON' p obj = wrap <$> recordParseJSON' p obj
1272 wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
1344 wrap :: S1 s (K1 i (Maybe a)) p -> S1 s (K1 i (Semigroup.Option a)) p
1273 wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
1345 wrap (M1 (K1 a)) = M1 (K1 (Semigroup.Option a))
@@ -1504,12 +1576,22 @@ instance FromJSONKey Float where
1504 _ -> Scientific.toRealFloat <$> parseScientificText t
1576 _ -> Scientific.toRealFloat <$> parseScientificText t
1506instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1578instance (FromJSON a, Integral a) => FromJSON (Ratio a) where
1579+ parseJSON (Number x)
1581+ , exp10 >= -1024 = return $! realToFrac x
1582+ | otherwise = prependContext "Ratio" $ fail msg
1584+ exp10 = base10Exponent x
1585+ msg = "found a number with exponent " ++ show exp10
1586+ ++ ", but it must not be greater than 1024 or less than -1024"
1587+ parseJSON o = objParser o
1507− parseJSON = withObject "Rational" $ \obj -> do
1589+ objParser = withObject "Rational" $ \obj -> do
1508− numerator <- obj .: "numerator"
1590+ numerator <- obj .: "numerator"
1509− denominator <- obj .: "denominator"
1591+ denominator <- obj .: "denominator"
1510− if denominator == 0
1592+ if denominator == 0
1511− then fail "Ratio denominator was 0"
1593+ then fail "Ratio denominator was 0"
1512− else pure $ numerator % denominator
1594+ else pure $ numerator % denominator
1513 {-# INLINE parseJSON #-}
1595 {-# INLINE parseJSON #-}
1515-- | This instance includes a bounds check to prevent maliciously
1597-- | This instance includes a bounds check to prevent maliciously
@@ -1660,7 +1742,7 @@ instance FromJSON1 NonEmpty where
1660 liftParseJSON p _ = withArray "NonEmpty" $
1742 liftParseJSON p _ = withArray "NonEmpty" $
1661 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1743 (>>= ne) . Tr.sequence . zipWith (parseIndexedJSON p) [0..] . V.toList
1663− ne [] = fail "parsing NonEmpty failed, unpexpected empty list"
1745+ ne [] = fail "parsing NonEmpty failed, unexpected empty list"
1664 ne (x:xs) = pure (x :| xs)
1746 ne (x:xs) = pure (x :| xs)
1665 {-# INLINE liftParseJSON #-}
1747 {-# INLINE liftParseJSON #-}
@@ -1915,7 +1997,7 @@ instance FromJSON DotNetTime where
1915 parseJSON = withText "DotNetTime" $ \t ->
1997 parseJSON = withText "DotNetTime" $ \t ->
1916 let (s,m) = T.splitAt (T.length t - 5) t
1998 let (s,m) = T.splitAt (T.length t - 5) t
1917 t' = T.concat [s,".",m]
1999 t' = T.concat [s,".",m]
1918− in case parseTime defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
2000+ in case parseTimeM True defaultTimeLocale "/Date(%s%Q)/" (unpack t') of
1919 Just d -> pure (DotNetTime d)
2001 Just d -> pure (DotNetTime d)
1920 _ -> fail "could not parse .NET time"
2002 _ -> fail "could not parse .NET time"
1921 {-# INLINE parseJSON #-}
2003 {-# INLINE parseJSON #-}
@@ -1924,7 +2006,6 @@ instance FromJSON DotNetTime where
1924-- primitive
2006-- primitive
1925-------------------------------------------------------------------------------
2007-------------------------------------------------------------------------------
1927−#if MIN_VERSION_base(4,7,0)
1928instance FromJSON a => FromJSON (PM.Array a) where
2009instance FromJSON a => FromJSON (PM.Array a) where
1929 -- note: we could do better than this if vector exposed the data
2010 -- note: we could do better than this if vector exposed the data
1930 -- constructor in Data.Vector.
2011 -- constructor in Data.Vector.
@@ -1937,6 +2018,7 @@ instance FromJSON a => FromJSON (PM.SmallArray a) where
1937instance (PM.Prim a,FromJSON a) => FromJSON (PM.PrimArray a) where
2018instance (PM.Prim a,FromJSON a) => FromJSON (PM.PrimArray a) where
1938 parseJSON = fmap Exts.fromList . parseJSON
2019 parseJSON = fmap Exts.fromList . parseJSON
2021+#if !MIN_VERSION_primitive(0,7,0)
1940instance (PM.PrimUnlifted a,FromJSON a) => FromJSON (PM.UnliftedArray a) where
2022instance (PM.PrimUnlifted a,FromJSON a) => FromJSON (PM.UnliftedArray a) where
1941 parseJSON = fmap Exts.fromList . parseJSON
2023 parseJSON = fmap Exts.fromList . parseJSON
@@ -2009,6 +2091,40 @@ instance FromJSON DiffTime where
2009 parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
2091 parseJSON = withBoundedScientific "DiffTime" $ pure . realToFrac
2010 {-# INLINE parseJSON #-}
2092 {-# INLINE parseJSON #-}
2094+instance FromJSON SystemTime where
2095+ parseJSON v = prependContext "SystemTime" $ do
2097+ let n' = floor (n * fromInteger (resolution n) :: Nano)
2098+ let (secs, nano) = n' `divMod` resolution n
2099+ return (MkSystemTime (fromInteger secs) (fromInteger nano))
2101+instance FromJSON CalendarDiffTime where
2102+ parseJSON = withObject "CalendarDiffTime" $ \obj -> CalendarDiffTime
2103+ <$> obj .: "months"
2106+instance FromJSON CalendarDiffDays where
2107+ parseJSON = withObject "CalendarDiffDays" $ \obj -> CalendarDiffDays
2108+ <$> obj .: "months"
2111+instance FromJSON DayOfWeek where
2112+ parseJSON = withText "DaysOfWeek" parseDayOfWeek
2114+parseDayOfWeek :: T.Text -> Parser DayOfWeek
2115+parseDayOfWeek t = case T.toLower t of
2116+ "monday" -> return Monday
2117+ "tuesday" -> return Tuesday
2118+ "wednesday" -> return Wednesday
2119+ "thursday" -> return Thursday
2120+ "friday" -> return Friday
2121+ "saturday" -> return Saturday
2122+ "sunday" -> return Sunday
2123+ _ -> fail "Invalid week day"
2125+instance FromJSONKey DayOfWeek where
2126+ fromJSONKey = FromJSONKeyTextParser parseDayOfWeek
2012-------------------------------------------------------------------------------
2128-------------------------------------------------------------------------------
2013-- base Monoid/Semigroup
2129-- base Monoid/Semigroup
2014-------------------------------------------------------------------------------
2130-------------------------------------------------------------------------------