on master help sign in
Add verboseDecode variants with accumulative errors
006e19blyxia
Data/Aeson.hs+40−2
@@ -37,12 +37,16 @@ module Data.Aeson
37 , decode'
37 , decode'
38 , eitherDecode
38 , eitherDecode
39 , eitherDecode'
39 , eitherDecode'
40+ , verboseDecode
41+ , verboseDecode'
40 , encode
42 , encode
41 -- ** Variants for strict bytestrings
43 -- ** Variants for strict bytestrings
42 , decodeStrict
44 , decodeStrict
43 , decodeStrict'
45 , decodeStrict'
44 , eitherDecodeStrict
46 , eitherDecodeStrict
45 , eitherDecodeStrict'
47 , eitherDecodeStrict'
48+ , verboseDecodeStrict
49+ , verboseDecodeStrict'
46 -- * Core JSON types
50 -- * Core JSON types
47 , Value(..)
51 , Value(..)
48 , Encoding
52 , Encoding
@@ -131,9 +135,14 @@ import Prelude.Compat
131
135
132import Data.Aeson.Types.FromJSON (ifromJSON)
136import Data.Aeson.Types.FromJSON (ifromJSON)
133import Data.Aeson.Encoding (encodingToLazyByteString)
137import Data.Aeson.Encoding (encodingToLazyByteString)
138+import Data.Aeson.Parser.Internal
139+ ( decodeWith, decodeStrictWith
134−import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
140+ , eitherDecodeWith, eitherDecodeStrictWith
141+ , verboseDecodeWith, verboseDecodeStrictWith
142+ , jsonEOF, json, jsonEOF', json')
135import Data.Aeson.Types
143import Data.Aeson.Types
136−import Data.Aeson.Types.Internal (JSONPath, formatError)
144+import Data.Aeson.Types.Internal (JSONPath, formatError, formatErrors)
145+import Data.List.NonEmpty (NonEmpty)
137import qualified Data.ByteString as B
146import qualified Data.ByteString as B
138import qualified Data.ByteString.Lazy as L
147import qualified Data.ByteString.Lazy as L
139
148
@@ -221,6 +230,35 @@ eitherDecodeStrict' =
221 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
230 eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
222{-# INLINE eitherDecodeStrict' #-}
231{-# INLINE eitherDecodeStrict' #-}
223
232
233+eitherFormatErrors
234+ :: Either (NonEmpty (JSONPath, String)) a -> Either (NonEmpty String) a
235+eitherFormatErrors = either (Left . formatErrors) Right
236+{-# INLINE eitherFormatErrors #-}
237+
238+-- | Like 'decode' but returns one or more error messages when decoding fails.
239+verboseDecode :: (FromJSON a) => L.ByteString -> Either (NonEmpty String) a
240+verboseDecode = eitherFormatErrors . verboseDecodeWith jsonEOF ifromJSON
241+{-# INLINE verboseDecode #-}
242+
243+-- | Like 'decodeStrict' but returns one or more error messages when decoding
244+-- fails.
245+verboseDecodeStrict :: (FromJSON a) => B.ByteString -> Either (NonEmpty String) a
246+verboseDecodeStrict =
247+ eitherFormatErrors . verboseDecodeStrictWith jsonEOF ifromJSON
248+{-# INLINE verboseDecodeStrict #-}
249+
250+-- | Like 'decode'' but returns one or more error messages when decoding fails.
251+verboseDecode' :: (FromJSON a) => L.ByteString -> Either (NonEmpty String) a
252+verboseDecode' = eitherFormatErrors . verboseDecodeWith jsonEOF' ifromJSON
253+{-# INLINE verboseDecode' #-}
254+
255+-- | Like 'decodeStrict'' but returns one or more error messages when decoding
256+-- fails.
257+verboseDecodeStrict' :: (FromJSON a) => B.ByteString -> Either (NonEmpty String) a
258+verboseDecodeStrict' =
259+ eitherFormatErrors . verboseDecodeStrictWith jsonEOF' ifromJSON
260+{-# INLINE verboseDecodeStrict' #-}
261+
224-- $use
262-- $use
225--
263--
226-- This section contains basic information on the different ways to
264-- This section contains basic information on the different ways to
Data/Aeson/Internal.hs+1
@@ -18,6 +18,7 @@ module Data.Aeson.Internal
18 , JSONPath
18 , JSONPath
19 , (<?>)
19 , (<?>)
20 , formatError
20 , formatError
21+ , formatErrors
21 , ifromJSON
22 , ifromJSON
22 , iparse
23 , iparse
23 ) where
24 ) where
Data/Aeson/Parser/Internal.hs+20
@@ -33,6 +33,8 @@ module Data.Aeson.Parser.Internal
33 , decodeStrictWith
33 , decodeStrictWith
34 , eitherDecodeWith
34 , eitherDecodeWith
35 , eitherDecodeStrictWith
35 , eitherDecodeStrictWith
36+ , verboseDecodeWith
37+ , verboseDecodeStrictWith
36 ) where
38 ) where
37
39
38import Prelude ()
40import Prelude ()
@@ -288,6 +290,24 @@ eitherDecodeStrictWith p to s =
288 IError (e :| _) -> Left e
290 IError (e :| _) -> Left e
289{-# INLINE eitherDecodeStrictWith #-}
291{-# INLINE eitherDecodeStrictWith #-}
290
292
293+verboseDecodeWith :: Parser Value -> (Value -> IResult a) -> L.ByteString
294+ -> Either (NonEmpty (JSONPath, String)) a
295+verboseDecodeWith p to s =
296+ case L.parse p s of
297+ L.Done _ v -> case to v of
298+ ISuccess a -> Right a
299+ IError e -> Left e
300+ L.Fail _ _ msg -> Left (([], msg) :| [])
301+{-# INLINE verboseDecodeWith #-}
302+
303+verboseDecodeStrictWith :: Parser Value -> (Value -> IResult a) -> B.ByteString
304+ -> Either (NonEmpty (JSONPath, String)) a
305+verboseDecodeStrictWith p to s =
306+ case either (\e -> IError (([], e) :| [])) to (A.parseOnly p s) of
307+ ISuccess a -> Right a
308+ IError e -> Left e
309+{-# INLINE verboseDecodeStrictWith #-}
310+
291-- $lazy
311-- $lazy
292--
312--
293-- The 'json' and 'value' parsers decouple identification from
313-- The 'json' and 'value' parsers decouple identification from
Data/Aeson/Types/Internal.hs+5
@@ -49,6 +49,7 @@ module Data.Aeson.Types.Internal
49 , parserCatchError
49 , parserCatchError
50 , parserCatchErrors
50 , parserCatchErrors
51 , formatError
51 , formatError
52+ , formatErrors
52 , (<?>)
53 , (<?>)
53 -- * Constructors and accessors
54 -- * Constructors and accessors
54 , object
55 , object
@@ -484,6 +485,10 @@ formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
484 escapeChar '\\' = "\\\\"
485 escapeChar '\\' = "\\\\"
485 escapeChar c = [c]
486 escapeChar c = [c]
486
487
488+-- | Annotate a list of error messages.
489+formatErrors :: Functor f => f (JSONPath, String) -> f String
490+formatErrors = fmap (uncurry formatError)
491+
487-- | A key\/value pair for an 'Object'.
492-- | A key\/value pair for an 'Object'.
488type Pair = (Text, Value)
493type Pair = (Text, Value)
489
494