Skip to content

Commit 006e19b

Browse files
Lysxiabergmark
authored andcommitted
Add verboseDecode variants with accumulative errors
1 parent 186ff70 commit 006e19b

File tree

4 files changed

+66
-2
lines changed

4 files changed

+66
-2
lines changed

Data/Aeson.hs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,16 @@ module Data.Aeson
3737
, decode'
3838
, eitherDecode
3939
, eitherDecode'
40+
, verboseDecode
41+
, verboseDecode'
4042
, encode
4143
-- ** Variants for strict bytestrings
4244
, decodeStrict
4345
, decodeStrict'
4446
, eitherDecodeStrict
4547
, eitherDecodeStrict'
48+
, verboseDecodeStrict
49+
, verboseDecodeStrict'
4650
-- * Core JSON types
4751
, Value(..)
4852
, Encoding
@@ -131,9 +135,14 @@ import Prelude.Compat
131135

132136
import Data.Aeson.Types.FromJSON (ifromJSON)
133137
import Data.Aeson.Encoding (encodingToLazyByteString)
134-
import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith, eitherDecodeWith, eitherDecodeStrictWith, jsonEOF, json, jsonEOF', json')
138+
import Data.Aeson.Parser.Internal
139+
( decodeWith, decodeStrictWith
140+
, eitherDecodeWith, eitherDecodeStrictWith
141+
, verboseDecodeWith, verboseDecodeStrictWith
142+
, jsonEOF, json, jsonEOF', json')
135143
import 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)
137146
import qualified Data.ByteString as B
138147
import qualified Data.ByteString.Lazy as L
139148

@@ -221,6 +230,35 @@ eitherDecodeStrict' =
221230
eitherFormatError . eitherDecodeStrictWith jsonEOF' ifromJSON
222231
{-# INLINE eitherDecodeStrict' #-}
223232

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+
224262
-- $use
225263
--
226264
-- This section contains basic information on the different ways to

Data/Aeson/Internal.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ module Data.Aeson.Internal
1818
, JSONPath
1919
, (<?>)
2020
, formatError
21+
, formatErrors
2122
, ifromJSON
2223
, iparse
2324
) where

Data/Aeson/Parser/Internal.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ module Data.Aeson.Parser.Internal
3333
, decodeStrictWith
3434
, eitherDecodeWith
3535
, eitherDecodeStrictWith
36+
, verboseDecodeWith
37+
, verboseDecodeStrictWith
3638
) where
3739

3840
import Prelude ()
@@ -288,6 +290,24 @@ eitherDecodeStrictWith p to s =
288290
IError (e :| _) -> Left e
289291
{-# INLINE eitherDecodeStrictWith #-}
290292

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+
291311
-- $lazy
292312
--
293313
-- The 'json' and 'value' parsers decouple identification from

Data/Aeson/Types/Internal.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ module Data.Aeson.Types.Internal
4949
, parserCatchError
5050
, parserCatchErrors
5151
, formatError
52+
, formatErrors
5253
, (<?>)
5354
-- * Constructors and accessors
5455
, object
@@ -484,6 +485,10 @@ formatError path msg = "Error in " ++ format "$" path ++ ": " ++ msg
484485
escapeChar '\\' = "\\\\"
485486
escapeChar c = [c]
486487

488+
-- | Annotate a list of error messages.
489+
formatErrors :: Functor f => f (JSONPath, String) -> f String
490+
formatErrors = fmap (uncurry formatError)
491+
487492
-- | A key\/value pair for an 'Object'.
488493
type Pair = (Text, Value)
489494

0 commit comments

Comments
 (0)