packages feed

unjson 0.15.3 → 0.15.4

raw patch · 5 files changed

+205/−126 lines, 5 filesdep ~aesondep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: aeson, base

API changes (from Hackage documentation)

- Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Lazy.Text v)
- Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap Data.Text.Internal.Text v)
- Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.HashMap.Base.HashMap GHC.Base.String v)
- Data.Unjson: instance (GHC.Classes.Eq a, Data.Hashable.Class.Hashable a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.HashSet.Base.HashSet a)
+ Data.Unjson: instance (Data.Typeable.Internal.Typeable v, Data.Unjson.Unjson v) => Data.Unjson.Unjson (Data.Aeson.KeyMap.KeyMap v)
+ Data.Unjson: instance (GHC.Classes.Eq a, Data.Hashable.Class.Hashable a, Data.Unjson.Unjson a, Data.Typeable.Internal.Typeable a) => Data.Unjson.Unjson (Data.HashSet.Internal.HashSet a)
+ Data.Unjson.Internal.Aeson.Compat: convertPair :: (Text, Value) -> Pair
+ Data.Unjson.Internal.Aeson.Compat: fromString :: String -> Key
+ Data.Unjson.Internal.Aeson.Compat: fromText :: Text -> Key
+ Data.Unjson.Internal.Aeson.Compat: lazyKeyMapFromList :: [(Key, v)] -> KeyMap v
+ Data.Unjson.Internal.Aeson.Compat: testErrorMessage :: Text
+ Data.Unjson.Internal.Aeson.Compat: toString :: Key -> String
+ Data.Unjson.Internal.Aeson.Compat: toText :: Key -> Text
+ Data.Unjson.Internal.Aeson.Compat: type LazyKeyMap = KeyMap
- Data.Unjson: [MapUnjsonDef] :: Typeable k => UnjsonDef k -> (HashMap Text k -> Result v) -> (v -> HashMap Text k) -> UnjsonDef v
+ Data.Unjson: [MapUnjsonDef] :: Typeable k => UnjsonDef k -> (KeyMap k -> Result v) -> (v -> KeyMap k) -> UnjsonDef v
- Data.Unjson: mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (HashMap Text x)
+ Data.Unjson: mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (LazyKeyMap x)

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+# unjson-0.15.4 (2022-02-26)+* Add support for aeson >= 2.+* Add support for GHC 9.0 and 9.2.+* Drop support for GHC < 8.8.+ # unjson-0.15.3 (2020-05-05) * GHC 8.8 and 8.10 support 
src/Data/Unjson.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE CPP #-}- -- | @Unjson@: bidirectional JSON (de)serialization with strong error -- reporting capabilities and automatic documentation generation. --@@ -84,75 +83,87 @@ -- 'unjsonToJSON', 'unjsonToByteStringLazy', -- 'unjsonToByteStringBuilder' and 'render'. module Data.Unjson-(--- * Serialization to JSON-  unjsonToJSON-, unjsonToJSON'-, unjsonToByteStringLazy-, unjsonToByteStringLazy'-, unjsonToByteStringBuilder-, unjsonToByteStringBuilder'-, unjsonToByteStringBuilder''-, Options(..)+  ( -- * Serialization to JSON+    unjsonToJSON+  , unjsonToJSON'+  , unjsonToByteStringLazy+  , unjsonToByteStringLazy'+  , unjsonToByteStringBuilder+  , unjsonToByteStringBuilder'+  , unjsonToByteStringBuilder''+  , Options(..) --- * Data definitions-, Unjson(..)-, UnjsonDef(..)+  -- * Data definitions+  , Unjson(..)+  , UnjsonDef(..) --- ** Objects-, objectOf-, field-, fieldBy-, fieldOpt-, fieldOptBy-, fieldDef-, fieldDefBy-, fieldReadonly-, fieldReadonlyBy-, FieldDef(..)--- ** Arrays-, arrayOf-, arrayWithModeOf-, arrayWithModeOf'-, arrayWithPrimaryKeyOf-, arrayWithModeAndPrimaryKeyOf-, ArrayMode(..)--- ** Maps, enums, sums-, mapOf-, enumOf-, enumUnjsonDef-, disjointUnionOf-, unionOf--- ** Helpers-, unjsonAeson-, unjsonAesonWithDoc+  -- ** Objects+  , objectOf+  , field+  , fieldBy+  , fieldOpt+  , fieldOptBy+  , fieldDef+  , fieldDefBy+  , fieldReadonly+  , fieldReadonlyBy+  , FieldDef(..)+  -- ** Arrays+  , arrayOf+  , arrayWithModeOf+  , arrayWithModeOf'+  , arrayWithPrimaryKeyOf+  , arrayWithModeAndPrimaryKeyOf+  , ArrayMode(..)+  -- ** Maps, enums, sums+  , mapOf+  , enumOf+  , enumUnjsonDef+  , disjointUnionOf+  , unionOf+  -- ** Helpers+  , unjsonAeson+  , unjsonAesonWithDoc --- * Documentation rendering-, render-, renderForPath-, renderDoc-, renderDocForPath+  -- * Documentation rendering+  , render+  , renderForPath+  , renderDoc+  , renderDocForPath --- * Parsing and updating-, parse-, update-, Result(..)-, Anchored(..)-, Problem-, Problems-, Path(..)-, PathElem(..)+  -- * Parsing and updating+  , parse+  , update+  , Result(..)+  , Anchored(..)+  , Problem+  , Problems+  , Path(..)+  , PathElem(..) -, unjsonInvmapR-, unjsonIsConstrByName-, unjsonIPv4AsWord32-)-where+  , unjsonInvmapR+  , unjsonIsConstrByName+  , unjsonIPv4AsWord32+  ) where +import Control.Applicative.Free+import Control.Exception+import Control.Monad+import Data.Data+import Data.Fixed+import Data.Functor.Invariant+import Data.Hashable+import Data.Maybe+import Data.Monoid hiding (Ap)+import Data.Primitive.Types+import Data.Scientific+import Data.Time.Clock+import Data.Time.LocalTime+import Foreign.Storable import qualified Data.Aeson as Aeson import qualified Data.Aeson.Types as Aeson+import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Lazy as BSL-import qualified Data.ByteString.Lazy.Builder as Builder import qualified Data.HashSet as HashSet import qualified Data.IntMap as IntMap import qualified Data.IntSet as IntSet@@ -166,36 +177,21 @@ import qualified Data.Vector.Primitive import qualified Data.Vector.Storable import qualified Data.Vector.Unboxed-import Data.Typeable-import Data.Data-import Data.Maybe-import Data.Monoid hiding (Ap)-import Data.Primitive.Types-import Data.Hashable-import Data.Scientific-import Data.Time.LocalTime-import Data.Time.Clock-import Data.Fixed-import Foreign.Storable-import Control.Applicative.Free-import Data.Functor.Invariant-import qualified Data.HashMap.Strict as HashMap-import qualified Data.HashMap.Lazy as LazyHashMap-import Control.Exception -import Control.Monad hiding (fail)-import Control.Monad.Fail (MonadFail (..)) import Data.Bits-import Data.Word+import Data.Char import Data.Int-import Data.Ratio import Data.List+import Data.Ratio+import Data.Word+import qualified Data.Unjson.Internal.Aeson.Compat as AC import qualified Text.ParserCombinators.ReadP as ReadP-import Data.Char-import Prelude hiding (fail)- import qualified Text.PrettyPrint.HughesPJ as P +#if !MIN_VERSION_aeson(2,0,1)+import qualified Data.HashMap.Strict as HashMap+#endif+ -- | Describe a path from root JSON element to a specific -- position. JSON has only two types of containers: objects and -- arrays, so there are only two types of keys needed to index into@@ -379,27 +375,30 @@ mapFst f (a,b) = (f a, b)  instance (Typeable v, Unjson v) => Unjson (Map.Map String v)-  where unjsonDef = invmap (Map.fromList . map (mapFst Text.unpack) . HashMap.toList)-                                     (HashMap.fromList . map (mapFst Text.pack) . Map.toList)+  where unjsonDef = invmap (Map.fromList . map (mapFst AC.toString) . AC.toList)+                                     (AC.fromList . map (mapFst AC.fromString) . Map.toList)                                      unjsonDef instance (Typeable v, Unjson v) => Unjson (Map.Map Text.Text v)-  where unjsonDef = invmap (Map.fromList . HashMap.toList)-                                     (HashMap.fromList . Map.toList)+  where unjsonDef = invmap (Map.fromList . fmap (mapFst AC.toText) . AC.toList)+                                     (AC.fromList . fmap (mapFst AC.fromText) . Map.toList)                                      unjsonDef instance (Typeable v, Unjson v) => Unjson (Map.Map LazyText.Text v)-  where unjsonDef = invmap (Map.fromList . map (mapFst LazyText.fromStrict) . HashMap.toList)-                                     (HashMap.fromList . map (mapFst LazyText.toStrict) . Map.toList)+  where unjsonDef = invmap (Map.fromList . map (mapFst (LazyText.fromStrict . AC.toText)) . AC.toList)+                                     (AC.fromList . map (mapFst (AC.fromText . LazyText.toStrict)) . Map.toList)                                      unjsonDef+instance (Typeable v, Unjson v) => Unjson (AC.KeyMap v)+  where unjsonDef = MapUnjsonDef unjsonDef pure id++#if !MIN_VERSION_aeson(2,0,1) instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap String v)   where unjsonDef = invmap (HashMap.fromList . map (mapFst Text.unpack) . HashMap.toList)                                      (HashMap.fromList . map (mapFst Text.pack) . HashMap.toList)                                      unjsonDef-instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap Text.Text v)-  where unjsonDef = MapUnjsonDef unjsonDef pure id instance (Typeable v, Unjson v) => Unjson (HashMap.HashMap LazyText.Text v)   where unjsonDef = invmap (HashMap.fromList . map (mapFst LazyText.fromStrict) . HashMap.toList)                                      (HashMap.fromList . map (mapFst LazyText.toStrict) . HashMap.toList)                                      unjsonDef+#endif  instance (Unjson a,Unjson b) => Unjson (a,b) where   unjsonDef = TupleUnjsonDef $ fmap pure@@ -590,7 +589,7 @@   TupleUnjsonDef    :: Ap (TupleFieldDef k) (Result k) -> UnjsonDef k   DisjointUnjsonDef :: Text.Text -> [(Text.Text, k -> Bool, Ap (FieldDef k) (Result k))] -> UnjsonDef k   UnionUnjsonDef    :: [(k -> Bool, Ap (FieldDef k) (Result k))] -> UnjsonDef k-  MapUnjsonDef      :: Typeable k => UnjsonDef k -> (HashMap.HashMap Text.Text k -> Result v) -> (v -> HashMap.HashMap Text.Text k) -> UnjsonDef v+  MapUnjsonDef      :: Typeable k => UnjsonDef k -> (AC.KeyMap k -> Result v) -> (v -> AC.KeyMap k) -> UnjsonDef v  instance Invariant UnjsonDef where   invmap f g (SimpleUnjsonDef name p s) = SimpleUnjsonDef name (fmap f . p) (s . g)@@ -690,15 +689,15 @@     (ArrayModeParseAndOutputSingle,[b]) -> unjsonToJSON' opt f b     (_,c) -> Aeson.toJSON (map (unjsonToJSON' opt f) c) unjsonToJSON' opt (ObjectUnjsonDef f) a =-  Aeson.object (objectDefToArray (nulls opt) (unjsonToJSON' opt) a f)+  Aeson.object $ AC.convertPair <$> (objectDefToArray (nulls opt) (unjsonToJSON' opt) a f) unjsonToJSON' opt (TupleUnjsonDef f) a =   Aeson.toJSON (tupleDefToArray (unjsonToJSON' opt) a f) unjsonToJSON' opt (DisjointUnjsonDef k l) a =-  Aeson.object ((k,Aeson.toJSON nm) : objectDefToArray (nulls opt) (unjsonToJSON' opt) a f)+  Aeson.object $ AC.convertPair <$> ((k,Aeson.toJSON nm) : objectDefToArray (nulls opt) (unjsonToJSON' opt) a f)   where     [(nm,_,f)] = filter (\(_,is,_) -> is a) l unjsonToJSON' opt (UnionUnjsonDef l) a =-  Aeson.object (objectDefToArray (nulls opt) (unjsonToJSON' opt) a f)+  Aeson.object $ AC.convertPair <$> (objectDefToArray (nulls opt) (unjsonToJSON' opt) a f)   where     [(_,f)] = filter (\(is,_) -> is a) l unjsonToJSON' opt (MapUnjsonDef f _ g) a =@@ -794,7 +793,7 @@ unjsonToByteStringBuilder'' level opt (MapUnjsonDef f _ g) a =   unjsonGroup level opt (Builder.char8 '{') (Builder.char8 '}') serx obj   where-    obj = LazyHashMap.toList (fmap (unjsonToByteStringBuilder'' (level + indent opt) opt f) (g a))+    obj = AC.toList (fmap (unjsonToByteStringBuilder'' (level + indent opt) opt f) (g a))     serx (key,val) = Builder.lazyByteString (Aeson.encode (Aeson.toJSON key)) <> Builder.char8 ':'                      <> (if pretty opt then Builder.char8 ' ' else mempty) <> val @@ -881,7 +880,7 @@  parseUpdating (DisjointUnjsonDef k l) ov v   = case Aeson.parseEither Aeson.parseJSON v of-      Right v' -> case HashMap.lookup k v' of+      Right v' -> case AC.lookup (AC.fromText k) v' of         Just x -> case Aeson.parseEither Aeson.parseJSON x of           Right xx -> case filter (\(nm,_,_) -> nm==xx) l of             [(_,_,f)] -> join (runAp (lookupByFieldDef v' ov) f)@@ -896,7 +895,7 @@         fail e parseUpdating (UnionUnjsonDef l) ov v   = case Aeson.parseEither Aeson.parseJSON v of-      Right v' -> case filter (\(_,f) -> isJust (mapM_ (\k -> HashMap.lookup k v') (listRequiredKeys f))) l of+      Right v' -> case filter (\(_,f) -> isJust (mapM_ (\k -> AC.lookup (AC.fromText k) v') (listRequiredKeys f))) l of         ((_,f):_) -> join (runAp (lookupByFieldDef v' ov) f)         _ -> fail $ "union value type could not be recognized based on presence of keys"       Left e ->@@ -905,7 +904,7 @@   = case Aeson.parseEither Aeson.parseJSON v of       Right v' ->         let hov = fmap h ov in-        join $ fmap g $ HashMap.traverseWithKey (\k1 v1 -> resultPrependKey k1 $ parseUpdating f (join (fmap (HashMap.lookup k1) hov)) v1) v'+        join $ fmap g $ AC.traverseWithKey (\k1 v1 -> resultPrependKey (AC.toText k1) $ parseUpdating f (join (fmap (AC.lookup k1) hov)) v1) v'       Left e ->         fail e @@ -973,20 +972,20 @@  lookupByFieldDef :: Aeson.Object -> Maybe s -> FieldDef s a -> Result a lookupByFieldDef v ov (FieldReqDef name _docstring f valuedef)-  = resultPrependKey name $ case HashMap.lookup name v of+  = resultPrependKey name $ case AC.lookup (AC.fromText name) v of       Just x  -> parseUpdating valuedef (fmap f ov) x       Nothing -> case ov of                    Just xov -> Result (f xov) []                    Nothing -> fail "missing key" lookupByFieldDef v ov (FieldDefDef name _docstring def f valuedef)-  = resultPrependKey name $ case HashMap.lookup name v of+  = resultPrependKey name $ case AC.lookup (AC.fromText name) v of       Just Aeson.Null -> Result def []       Just x  -> parseUpdating valuedef (fmap f ov) x       Nothing -> case ov of                    Just xov -> Result (f xov) []                    Nothing -> Result def [] lookupByFieldDef v ov (FieldOptDef name _docstring f valuedef)-  = resultPrependKey name $ case HashMap.lookup name v of+  = resultPrependKey name $ case AC.lookup (AC.fromText name) v of       Just Aeson.Null -> Result Nothing []       Just x  -> case ov of                    Just xov -> fmap Just (parseUpdating valuedef (f xov) x)@@ -1168,7 +1167,7 @@ -- > objectOf $ pure X -- >   <*> field "xmap" xMap -- >       "Map string to Y value"-mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (LazyHashMap.HashMap Text.Text x)+mapOf :: Typeable x => UnjsonDef x -> UnjsonDef (AC.LazyKeyMap x) mapOf def = MapUnjsonDef def pure id  -- | Provide sum type support. Bidirectional case matching in Haskell
+ src/Data/Unjson/Internal/Aeson/Compat.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE CPP #-}+module Data.Unjson.Internal.Aeson.Compat+  ( module Map+#if !MIN_VERSION_aeson(2,0,1)+  , KeyMap+#endif+  , fromText+  , toText+  , convertPair+  , LazyKeyMap+  , testErrorMessage+  , fromString+  , toString+  , lazyKeyMapFromList+  ) where++import Data.Text+import qualified Data.Aeson.Types as A++#if MIN_VERSION_aeson(2,0,1)++import Data.Aeson.KeyMap as Map+import qualified Data.Aeson.Key as K++fromText :: Text -> K.Key+fromText = K.fromText++toText :: K.Key -> Text+toText = K.toText++fromString :: String -> K.Key+fromString = K.fromString++toString :: K.Key -> String+toString = K.toString++convertPair :: (Text, A.Value) -> A.Pair+convertPair (t, v) = (K.fromText t, v)++type LazyKeyMap = KeyMap++testErrorMessage :: Text+testErrorMessage = "Error in $: parsing KeyMap failed, expected Object, but encountered String"++lazyKeyMapFromList :: [(K.Key, v)] -> Map.KeyMap v+lazyKeyMapFromList = Map.fromList++#else++import Data.HashMap.Strict as Map+import Data.Hashable (Hashable)+import qualified Data.HashMap.Lazy as LazyHashMap++type KeyMap a = Map.HashMap Text a+type LazyKeyMap a = LazyHashMap.HashMap Text a++fromText :: Text -> Text+fromText = id++toText :: Text -> Text+toText = id++fromString :: String -> String+fromString = id++toString :: String -> String+toString = id++convertPair :: (Text, A.Value) -> (Text, A.Value)+convertPair = id++testErrorMessage :: Text+testErrorMessage = "Error in $: parsing HashMap ~Text failed, expected Object, but encountered String"++lazyKeyMapFromList :: (Eq k, Hashable k) => [(k, v)] -> HashMap k v+lazyKeyMapFromList = LazyHashMap.fromList+#endif
test/Test.hs view
@@ -2,24 +2,20 @@ {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Main where -import Prelude hiding (fail)-import qualified Data.Text as Text-import qualified Data.ByteString.Lazy.Char8 as BSL-import Data.Int-import Data.Typeable-import Data.Unjson-import qualified Data.Aeson as Aeson import Data.Aeson ((.=))-import Data.List import Data.Data import Data.Functor.Invariant-import Control.Monad.Fail (MonadFail(fail))+import Data.Int+import Data.List+import Data.Unjson+import System.Exit (ExitCode (..), exitWith) import Test.HUnit-import qualified Data.HashMap.Strict as HashMap-import qualified Data.HashMap.Lazy as LazyHashMap+import qualified Data.Aeson as Aeson+import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.Map as Map+import qualified Data.Text as Text -import System.Exit (ExitCode (..), exitWith)+import qualified Data.Unjson.Internal.Aeson.Compat as AC  default (Text.Text, String, Int, Double) @@ -181,8 +177,10 @@     (Anchored (Path [ PathElemKey "port" ])     "parsing Integer failed, expected Number, but encountered Object") (iss!!1)   assertEqual "Credentials must be object error info is present"-                (Anchored (Path [ PathElemKey "credentials"-                                ]) "Error in $: parsing HashMap ~Text failed, expected Object, but encountered String") (iss!!2)+                (Anchored +                  (Path [ PathElemKey "credentials" ]) +                  AC.testErrorMessage+                ) (iss!!2)   return ()  test_tuple_parsing :: Test@@ -661,7 +659,7 @@          <*> field "a_map"              id              "The only map"-  let unjsonMapByExplicit :: (Unjson a, Typeable a) => UnjsonDef (HashMap.HashMap Text.Text a)+  let unjsonMapByExplicit :: (Unjson a, Typeable a) => UnjsonDef (AC.KeyMap a)       unjsonMapByExplicit = objectOf $ pure id          <*> fieldBy "a_map"              id@@ -672,10 +670,10 @@   assertEqual "Parsing keeps proper order in Data.Map" (Map.fromList [("k1"::String, 12::Int),("k2", 1122), ("a4", 666)]) val0   let Result val1 iss1 = parse unjsonMapByInstance jsonEmbedded   assertEqual "No problems" [] iss1-  assertEqual "Parsing keeps proper order" (HashMap.fromList [("k1"::String, 12::Int),("k2", 1122), ("a4", 666)]) val1+  assertEqual "Parsing keeps proper order" (AC.fromList [(AC.fromString ("k1" :: String), 12::Int),("k2", 1122), ("a4", 666)]) val1   let Result val2 iss2 = parse unjsonMapByExplicit jsonEmbedded   assertEqual "No problems" [] iss2-  assertEqual "Parsing keeps proper order" (LazyHashMap.fromList [("k1"::Text.Text, 12::Int),("k2", 1122), ("a4", 666)]) val2+  assertEqual "Parsing keeps proper order" (AC.lazyKeyMapFromList [(AC.fromText ("k1"::Text.Text), 12::Int),("k2", 1122), ("a4", 666)]) val2   return ()  
unjson.cabal view
@@ -1,6 +1,6 @@ cabal-version:       1.18 name:                unjson-version:             0.15.3+version:             0.15.4 synopsis:            Bidirectional JSON parsing and generation. description:         Bidirectional JSON parsing and generation                      with automatic documentation support.@@ -15,7 +15,7 @@ category:            Data build-type:          Simple extra-source-files:  README.md, CHANGELOG.md-tested-with:         GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.3 || ==8.10.1+tested-with:         GHC ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.1  source-repository head   type:     git@@ -28,10 +28,10 @@  library   exposed-modules:     Data.Unjson-  -- other-modules:+                       Data.Unjson.Internal.Aeson.Compat   -- other-extensions:-  build-depends:       base >= 4.9 && < 5,-                       aeson >= 1.0 && < 1.5,+  build-depends:       base >= 4.13 && < 5,+                       aeson >= 1.0,                        attoparsec,                        bytestring >= 0.10,                        containers,