aeson 2.3.0.0 → 2.3.1.0
raw patch · 17 files changed
+101/−42 lines, 17 filesdep ~text-iso8601PVP ok
version bump matches the API change (PVP)
Dependency ranges changed: text-iso8601
API changes (from Hackage documentation)
+ Data.Aeson.Types: isEmptyArray :: Value -> Bool
Files
- LICENSE +1/−1
- README.markdown +0/−3
- aeson.cabal +11/−9
- changelog.md +10/−1
- src/Data/Aeson.hs +8/−1
- src/Data/Aeson/Encoding/Internal.hs +1/−2
- src/Data/Aeson/Internal/Prelude.hs +0/−1
- src/Data/Aeson/Key.hs +1/−2
- src/Data/Aeson/KeyMap.hs +2/−3
- src/Data/Aeson/Types.hs +1/−0
- src/Data/Aeson/Types/FromJSON.hs +27/−3
- src/Data/Aeson/Types/Internal.hs +11/−7
- src/Data/Aeson/Types/ToJSON.hs +14/−0
- tests/PropUtils.hs +1/−1
- tests/PropertyKeys.hs +3/−0
- tests/PropertyRoundTrip.hs +3/−0
- tests/Types.hs +7/−8
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2011, MailRank, Inc. 2014-2021 Aeson project contributors+Copyright (c) 2011, MailRank, Inc. 2014-2026 Aeson project contributors All rights reserved.
README.markdown view
@@ -20,9 +20,6 @@ * https://github.com/haskell/aeson/blob/master/changelog.md -(You can create and contribute changes using either git or Mercurial.)-- # Authors This library was originally written by Bryan O'Sullivan.
aeson.cabal view
@@ -1,16 +1,18 @@ cabal-version: 2.2 name: aeson-version: 2.3.0.0+version: 2.3.1.0 license: BSD-3-Clause license-file: LICENSE category: Text, Web, JSON copyright:- (c) 2011-2016 Bryan O'Sullivan- (c) 2011 MailRank, Inc.-+ 2014-2026 Aeson project contributors,+ 2011-2016 Bryan O'Sullivan,+ 2011 MailRank, Inc. author: Bryan O'Sullivan <bos@serpentine.com>-maintainer: Adam Bergmark <adam@bergmark.nl>-stability: experimental+maintainer:+ Li-yao Xia <lysxia@gmail.com>,+ Core Libraries Committee+stability: stable tested-with: GHC ==8.6.5 || ==8.8.4@@ -36,12 +38,12 @@ below. . (A note on naming: in Greek mythology, Aeson was the father of Jason.)-+extra-doc-files:+ README.markdown+ changelog.md extra-source-files: *.yaml benchmarks/json-data/*.json- changelog.md- README.markdown tests/golden/*.expected tests/JSONTestSuite/results/*.tok tests/JSONTestSuite/results/*.txt
changelog.md view
@@ -1,5 +1,12 @@ 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.3.1.0++* Add `FromJSONKey` instance for `Data.Fixed`.+* Add `ToJSON` and `FromJSON` instances for `Data.Complex`.+* Export `isEmptyArray` from `Data.Aeson.Types` module.+* Document that file decoding functions throw an exception when the file is missing.+ ### 2.3.0.0 - 2026-05-21 * Fix parsing of fractional numbers to reject exponents smaller than -1024.@@ -7,9 +14,11 @@ rejecting more inputs. Error messages for `Ratio` and integral types are also slightly different due to reusing the same bounding logic. * Fix typo in error message: "~~Unespected~~ Unexpected control character while parsing string literal".-* Accept 24:00:00 time of day. * Support nothunks 0.3. * Unset executable permissions in some test files and remove a broken symlink.+* In `text-iso8601-0.2.0.0`:+ - Reject years of more than 15 digits.+ - Accept 24:00:00 time of day. ### 2.2.5.0
src/Data/Aeson.hs view
@@ -199,6 +199,8 @@ -- -- This function parses immediately, but defers conversion. See -- 'json' for details.+--+-- Throws an 'Exception' when the file is missing. decodeFileStrict :: (FromJSON a) => FilePath -> IO (Maybe a) decodeFileStrict = fmap decodeStrict . B.readFile @@ -226,12 +228,15 @@ -- If this fails due to incomplete or invalid input, 'Nothing' is -- returned. ----- Since @2.2.0.0@ an alias for 'decodeFileStrict'.+-- Throws an 'Exception' when the file is missing. --+-- Since @2.2.0.0@ an alias for 'decodeFileStrict'. decodeFileStrict' :: (FromJSON a) => FilePath -> IO (Maybe a) decodeFileStrict' = decodeFileStrict -- | Like 'decodeFileStrict' but returns an error message when decoding fails.+--+-- Throws an 'Exception' when the file is missing. eitherDecodeFileStrict :: (FromJSON a) => FilePath -> IO (Either String a) eitherDecodeFileStrict = fmap eitherDecodeStrict . B.readFile@@ -252,6 +257,8 @@ {-# INLINE eitherDecodeStrict' #-} -- | Like 'decodeFileStrict'' but returns an error message when decoding fails.+--+-- Throws an 'Exception' when the file is missing. -- -- Since @2.2.0.0@ an alias for 'eitherDecodeFileStrict'. eitherDecodeFileStrict' :: (FromJSON a) => FilePath -> IO (Either String a)
src/Data/Aeson/Encoding/Internal.hs view
@@ -82,7 +82,7 @@ newtype Encoding' tag = Encoding { fromEncoding :: Builder -- ^ Acquire the underlying bytestring builder.- } deriving (Typeable)+ } -- | Often used synonym for 'Encoding''. type Encoding = Encoding' Value@@ -128,7 +128,6 @@ -- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age) data Series = Empty | Value (Encoding' Series)- deriving (Typeable) pair :: Key -> Encoding -> Series pair name val = pair' (key name) val
src/Data/Aeson/Internal/Prelude.hs view
@@ -21,7 +21,6 @@ import Data.String as X (IsString(..)) import Data.Text as X (Text) import Data.Time as X (UTCTime)-import Data.Typeable as X (Typeable) import Data.Vector as X (Vector) import Data.Void as X (Void, absurd) import Data.Word as X (Word8, Word16, Word32, Word64)
src/Data/Aeson/Key.hs view
@@ -32,7 +32,6 @@ import Data.Semigroup (Semigroup((<>))) import Data.Text (Text) import Data.Type.Coercion (Coercion (..))-import Data.Typeable (Typeable) import Text.Read (Read (..)) import qualified Data.String@@ -42,7 +41,7 @@ import qualified Test.QuickCheck as QC newtype Key = Key { unKey :: Text }- deriving (Typeable, Data)+ deriving (Data) fromString :: String -> Key fromString = Key . T.pack
src/Data/Aeson/KeyMap.hs view
@@ -114,7 +114,6 @@ import Data.Text (Text) import Data.These (These (..)) import Data.Type.Coercion (Coercion (..))-import Data.Typeable (Typeable) import Text.Read (Read (..), Lexeme(..), readListPrecDefault, prec, lexP, parens) import qualified Data.Aeson.Key as Key@@ -141,7 +140,7 @@ -- | A map from JSON key type 'Key' to 'v'. newtype KeyMap v = KeyMap { unKeyMap :: Map Key v }- deriving (Eq, Ord, Typeable, Data, Functor)+ deriving (Eq, Ord, Data, Functor) -- | Construct an empty map.@@ -355,7 +354,7 @@ -- | A map from JSON key type 'Key' to 'v'. newtype KeyMap v = KeyMap { unKeyMap :: HashMap Key v }- deriving (Eq, Ord, Typeable, Data, Functor)+ deriving (Eq, Ord, Data, Functor) -- | Construct an empty map. empty :: KeyMap v
src/Data/Aeson/Types.hs view
@@ -20,6 +20,7 @@ , Series , Array , emptyArray+ , isEmptyArray , Pair , Object , emptyObject
src/Data/Aeson/Types/FromJSON.hs view
@@ -95,6 +95,7 @@ import Data.Aeson.Types.Generic import Data.Aeson.Types.Internal import Data.Bits (unsafeShiftR)+import Data.Complex (Complex(..)) import Data.Fixed (Fixed, HasResolution (resolution), Nano) import Data.Functor.Compose (Compose(..)) import Data.Functor.Identity (Identity(..))@@ -216,16 +217,22 @@ (\sci rest -> if T.null rest then return sci else fail $ "Expecting end-of-input, got " ++ show (T.take 10 rest)) fail -parseIntegralText :: Integral a => String -> Text -> Parser a-parseIntegralText name t =+parseBoundedScientificTextTo :: (Scientific -> Parser a) -> String -> Text -> Parser a+parseBoundedScientificTextTo toResultType name t = prependContext name $ parseScientificText t >>= rejectLargeExponent- >>= parseIntegralFromScientific+ >>= toResultType where rejectLargeExponent :: Scientific -> Parser Scientific rejectLargeExponent s = withBoundedScientific' pure (Number s) +parseBoundedScientificText :: String -> Text -> Parser Scientific+parseBoundedScientificText = parseBoundedScientificTextTo pure++parseIntegralText :: Integral a => String -> Text -> Parser a+parseIntegralText = parseBoundedScientificTextTo parseIntegralFromScientific+ parseBoundedIntegralText :: (Bounded a, Integral a) => String -> Text -> Parser a parseBoundedIntegralText name t = prependContext name $@@ -1718,12 +1725,29 @@ then fail "Ratio denominator was 0" else pure $ numerator % denominator +-- | A complex number @x+iy@ is encoded as an array @[x, y]@.+--+-- @since 2.3.1.0+instance FromJSON a => FromJSON (Complex a) where+ parseJSON = withArray "Complex" $ \c ->+ let n = V.length c+ in if n == 2+ then (:+) <$> parseJSONElemAtIndex parseJSON 0 c+ <*> parseJSONElemAtIndex parseJSON 1 c+ else fail $ "cannot unpack array of length " ++ show n ++ "into a Complex"+ -- | This instance includes a bounds check to prevent maliciously -- large inputs to fill up the memory of the target system. You can -- newtype 'Scientific' and provide your own instance using -- 'withScientific' if you want to allow larger inputs. instance HasResolution a => FromJSON (Fixed a) where parseJSON = prependContext "Fixed" . withBoundedScientific' (pure . realToFrac)++-- |+-- @since 2.3.1.0+instance HasResolution a => FromJSONKey (Fixed a) where+ fromJSONKey = FromJSONKeyTextParser $ \t ->+ realToFrac <$> parseBoundedScientificText "Fixed" t instance FromJSON Int where parseJSON = parseBoundedIntegral "Int"
src/Data/Aeson/Types/Internal.hs view
@@ -112,18 +112,18 @@ | Index {-# UNPACK #-} !Int -- ^ JSON path element of an index into an -- array, \"array[index]\".- deriving (Eq, Show, Typeable, Ord)+ deriving (Eq, Show, Ord) type JSONPath = [JSONPathElement] -- | The internal result of running a 'Parser'. data IResult a = IError JSONPath String | ISuccess a- deriving (Eq, Show, Typeable)+ deriving (Eq, Show) -- | The result of running a 'Parser'. data Result a = Error String | Success a- deriving (Eq, Show, Typeable)+ deriving (Eq, Show) instance NFData JSONPathElement where rnf (Key t) = rnf t@@ -369,7 +369,7 @@ | Number !Scientific | Bool !Bool | Null- deriving (Eq, Read, Typeable, Data, Generic)+ deriving (Eq, Read, Data, Generic) -- | Since version 1.5.6.0 version object values are printed in lexicographic key order --@@ -505,7 +505,7 @@ newtype DotNetTime = DotNetTime { fromDotNetTime :: UTCTime -- ^ Acquire the underlying value.- } deriving (Eq, Ord, Read, Show, Typeable, FormatTime)+ } deriving (Eq, Ord, Read, Show, FormatTime) instance NFData Value where rnf (Object o) = rnf o@@ -552,8 +552,12 @@ emptyArray :: Value emptyArray = Array V.empty --- | Determines if the 'Value' is an empty 'Array'.--- Note that: @isEmptyArray 'emptyArray'@.+-- | Determines whether the 'Value' is an empty 'Array'.+--+-- Do note that if this is `False`, the `Value` may be a non-empty+-- array, or it may not even be an array.+--+-- @since 2.3.1.0 isEmptyArray :: Value -> Bool isEmptyArray (Array arr) = V.null arr isEmptyArray _ = False
src/Data/Aeson/Types/ToJSON.hs view
@@ -69,6 +69,7 @@ import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM import Data.Bits (unsafeShiftR)+import Data.Complex (Complex(..)) import Data.DList (DList) import Data.Fixed (Fixed, HasResolution, Nano) import Data.Foldable (toList)@@ -1424,6 +1425,19 @@ "numerator" .= numerator r <> "denominator" .= denominator r +-- | A complex number @x+iy@ is encoded as an array @[x, y]@.+--+-- @since 2.3.1.0+instance ToJSON a => ToJSON (Complex a) where+ toJSON (i :+ q) = Array $ V.create $ do+ mv <- VM.unsafeNew 2+ VM.unsafeWrite mv 0 (toJSON i)+ VM.unsafeWrite mv 1 (toJSON q)+ return mv+ toEncoding (i :+ q) = E.list id+ [ toEncoding i+ , toEncoding q+ ] instance HasResolution a => ToJSON (Fixed a) where toJSON = Number . realToFrac
tests/PropUtils.hs view
@@ -31,7 +31,7 @@ import Data.Aeson (eitherDecode, encode) import Data.Aeson.Encoding (encodingToLazyByteString)-import Data.Aeson.Types+import Data.Aeson.Types hiding (isEmptyArray) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM import Data.HashMap.Strict (HashMap)
tests/PropertyKeys.hs view
@@ -6,6 +6,7 @@ import Prelude.Compat import Control.Applicative (Const)+import Data.Fixed (Fixed, E3, E6) import Data.Time.Compat (Day, LocalTime, TimeOfDay, UTCTime) import Data.Time.Calendar.Compat (DayOfWeek) import Data.Time.Calendar.Month.Compat (Month)@@ -47,4 +48,6 @@ , testProperty "Lazy Text" $ roundTripKey @LT.Text , testProperty "UUID" $ roundTripKey @UUID.UUID , testProperty "Const Text" $ roundTripKey @(Const T.Text ())+ , testProperty "Fixed E3" $ roundTripKey @(Fixed E3)+ , testProperty "Fixed E6" $ roundTripKey @(Fixed E6) ]
tests/PropertyRoundTrip.hs view
@@ -7,6 +7,7 @@ import Control.Applicative (Const) import Data.Aeson.Types+import Data.Complex (Complex(..)) import Data.DList (DList) import Data.List.NonEmpty (NonEmpty) import Data.Map (Map)@@ -81,6 +82,8 @@ , testProperty "Seq" $ roundTripEq @(Seq Int) , testProperty "Rational" $ roundTripEq @Rational , testProperty "Ratio Int" $ roundTripEq @(Ratio Int)+ , testProperty "Complex Float" $ roundTripEq @(Complex Float)+ , testProperty "Complex Double" $ roundTripEq @(Complex Double) , testProperty "UUID" $ roundTripEq @UUID.UUID , testProperty "These" $ roundTripEq @(These Char Bool) , testProperty "Fix" $ roundTripEq @(F.Fix (These Char))
tests/Types.hs view
@@ -41,32 +41,32 @@ -- This definition causes an infinite loop in genericTo and genericFrom! -- , fooMap :: Map.Map String Foo , fooMap :: Map.Map String (Text,Int)- } deriving (Show, Typeable, Data)+ } deriving (Show, Data) data UFoo = UFoo { _UFooInt :: Int , uFooInt :: Int- } deriving (Show, Eq, Data, Typeable)+ } deriving (Show, Eq, Data) data NoConstructors data OneConstructor = OneConstructor- deriving (Show, Eq, Typeable, Data)+ deriving (Show, Eq, Data) data Product2 a b = Product2 a b- deriving (Show, Eq, Typeable, Data)+ deriving (Show, Eq, Data) data Product6 a b c d e f = Product6 a b c d e f- deriving (Show, Eq, Typeable, Data)+ deriving (Show, Eq, Data) data Sum4 a b c d = Alt1 a | Alt2 b | Alt3 c | Alt4 d- deriving (Show, Eq, Typeable, Data)+ deriving (Show, Eq, Data) class ApproxEq a where (=~) :: a -> a -> Bool newtype Approx a = Approx { fromApprox :: a }- deriving (Show, Data, Typeable, ApproxEq, Num)+ deriving (Show, Data, ApproxEq, Num) instance (ApproxEq a) => Eq (Approx a) where Approx a == Approx b = a =~ b@@ -93,7 +93,6 @@ data GADT a where GADT :: { gadt :: String } -> GADT String- deriving Typeable deriving instance Data (GADT String) deriving instance Eq (GADT a)