on master help sign in
Add support for HashMap from the unordered-containers package.
5b07f02Bryan O'Sullivan
Data/Aeson/Functions.hs+19−3
@@ -1,13 +1,29 @@
1module Data.Aeson.Functions
1module Data.Aeson.Functions
2 (
2 (
3+ hashMap
4+ , mapHash
3− transformMap
5+ , transformMap
4 ) where
6 ) where
5
7
6−import Control.Arrow ((***))
8+import Control.Arrow ((***), first)
9+import Data.Hashable (Hashable)
10+import qualified Data.HashMap.Strict as H
7import qualified Data.Map as M
11import qualified Data.Map as M
8
12
9-- | Transform one map into another. The ordering of keys must be
13-- | Transform one map into another. The ordering of keys must be
10−-- preserved.
14+-- preserved by the key transformation function.
11transformMap :: (Ord k2) => (k1 -> k2) -> (v1 -> v2)
15transformMap :: (Ord k2) => (k1 -> k2) -> (v1 -> v2)
12 -> M.Map k1 v1 -> M.Map k2 v2
16 -> M.Map k1 v1 -> M.Map k2 v2
13transformMap fk fv = M.fromAscList . map (fk *** fv) . M.toAscList
17transformMap fk fv = M.fromAscList . map (fk *** fv) . M.toAscList
18+{-# INLINE transformMap #-}
19+
20+-- | Transform a 'H.HashMap' into a 'M.Map'.
21+hashMap :: (Ord k2) => (k1 -> k2) -> (v1 -> v2)
22+ -> H.HashMap k1 v1 -> M.Map k2 v2
23+hashMap fk kv = M.fromList . map (fk *** kv) . H.toList
24+{-# INLINE hashMap #-}
25+
26+-- | Transform a 'M.Map' into a 'H.HashMap'.
27+mapHash :: (Eq k2, Hashable k2) => (k1 -> k2) -> M.Map k1 v -> H.HashMap k2 v
28+mapHash fk = H.fromList . map (first fk) . M.toList
29+{-# INLINE mapHash #-}
Data/Aeson/Generic.hs+15−3
@@ -22,7 +22,7 @@ module Data.Aeson.Generic
22import Control.Applicative ((<$>))
22import Control.Applicative ((<$>))
23import Control.Arrow (first)
23import Control.Arrow (first)
24import Control.Monad.State.Strict
24import Control.Monad.State.Strict
25−import Data.Aeson.Functions (transformMap)
25+import Data.Aeson.Functions (hashMap, transformMap)
26import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..), fromJSON)
26import Data.Aeson.Types hiding (FromJSON(..), ToJSON(..), fromJSON)
27import Data.Attoparsec.Number (Number)
27import Data.Attoparsec.Number (Number)
28import Data.Generics
28import Data.Generics
@@ -35,6 +35,7 @@ import Data.Word (Word, Word8, Word16, Word32, Word64)
35import qualified Data.Aeson.Types as T
35import qualified Data.Aeson.Types as T
36import qualified Data.ByteString as B
36import qualified Data.ByteString as B
37import qualified Data.ByteString.Lazy as L
37import qualified Data.ByteString.Lazy as L
38+import qualified Data.HashMap.Strict as H
38import qualified Data.Map as Map
39import qualified Data.Map as Map
39import qualified Data.Set as Set
40import qualified Data.Set as Set
40import qualified Data.Text as DT
41import qualified Data.Text as DT
@@ -50,6 +51,7 @@ toJSON = toJSON_generic
50 `ext1Q` vector
51 `ext1Q` vector
51 `ext1Q` set
52 `ext1Q` set
52 `ext2Q'` mapAny
53 `ext2Q'` mapAny
54+ `ext2Q'` hashMapAny
53 -- Use the standard encoding for all base types.
55 -- Use the standard encoding for all base types.
54 `extQ` (T.toJSON :: T Integer)
56 `extQ` (T.toJSON :: T Integer)
55 `extQ` (T.toJSON :: T Int)
57 `extQ` (T.toJSON :: T Int)
@@ -90,9 +92,18 @@ toJSON = toJSON_generic
90 | tyrep == typeOf LT.empty = remap LT.toStrict
92 | tyrep == typeOf LT.empty = remap LT.toStrict
91 | otherwise = modError "toJSON" $
93 | otherwise = modError "toJSON" $
92 "cannot convert map keyed by type " ++ show tyrep
94 "cannot convert map keyed by type " ++ show tyrep
93− where tyrep = typeOf $ head $ Map.keys m
95+ where tyrep = typeOf . head . Map.keys $ m
94 remap f = Object . transformMap (f . fromJust . cast) toJSON $ m
96 remap f = Object . transformMap (f . fromJust . cast) toJSON $ m
95
97
98+ hashMapAny m
99+ | tyrep == typeOf "" = remap pack
100+ | tyrep == typeOf DT.empty = remap id
101+ | tyrep == typeOf LT.empty = remap LT.toStrict
102+ | otherwise = modError "toJSON" $
103+ "cannot convert map keyed by type " ++ show tyrep
104+ where tyrep = typeOf . head . H.keys $ m
105+ remap f = Object . hashMap (f . fromJust . cast) toJSON $ m
106+
96
107
97toJSON_generic :: (Data a) => a -> Value
108toJSON_generic :: (Data a) => a -> Value
98toJSON_generic = generic
109toJSON_generic = generic
@@ -143,6 +154,7 @@ parseJSON j = parseJSON_generic j
143 `ext1R` list
154 `ext1R` list
144 `ext1R` vector
155 `ext1R` vector
145 `ext2R'` mapAny
156 `ext2R'` mapAny
157+ -- Don't know how to support parsing HashMaps :-(
146 -- Use the standard encoding for all base types.
158 -- Use the standard encoding for all base types.
147 `extR` (value :: F Integer)
159 `extR` (value :: F Integer)
148 `extR` (value :: F Int)
160 `extR` (value :: F Int)
@@ -186,7 +198,7 @@ parseJSON j = parseJSON_generic j
186 | otherwise = myFail
198 | otherwise = myFail
187 where res = case j of
199 where res = case j of
188 Object js -> Map.mapKeysMonotonic trans <$> T.mapM parseJSON js
200 Object js -> Map.mapKeysMonotonic trans <$> T.mapM parseJSON js
189− _ -> myFail
201+ _ -> myFail
190 trans
202 trans
191 | tyrep == typeOf DT.empty = fromJust . cast . id
203 | tyrep == typeOf DT.empty = fromJust . cast . id
192 | tyrep == typeOf LT.empty = fromJust . cast . LT.fromStrict
204 | tyrep == typeOf LT.empty = fromJust . cast . LT.fromStrict
Data/Aeson/Types.hs+29−5
@@ -36,32 +36,33 @@ module Data.Aeson.Types
36 ) where
36 ) where
37
37
38import Control.Applicative
38import Control.Applicative
39−import Control.Monad (MonadPlus(..))
40−import Data.Monoid (Monoid(..))
41import Control.DeepSeq (NFData(..))
39import Control.DeepSeq (NFData(..))
40+import Control.Monad (MonadPlus(..))
41+import Data.Aeson.Functions (hashMap, mapHash, transformMap)
42+import Data.Attoparsec.Char8 (Number(..))
42import Data.Data (Data)
43import Data.Data (Data)
43import Data.Int (Int8, Int16, Int32, Int64)
44import Data.Int (Int8, Int16, Int32, Int64)
44−import qualified Data.IntSet as IntSet
45import Data.Map (Map)
45import Data.Map (Map)
46import Data.Monoid (Dual(..), First(..), Last(..))
46import Data.Monoid (Dual(..), First(..), Last(..))
47+import Data.Monoid (Monoid(..))
47import Data.Ratio (Ratio)
48import Data.Ratio (Ratio)
48import Data.Text (Text, pack, unpack)
49import Data.Text (Text, pack, unpack)
49import Data.Text.Encoding (decodeUtf8, encodeUtf8)
50import Data.Text.Encoding (decodeUtf8, encodeUtf8)
50import Data.Time.Clock (UTCTime)
51import Data.Time.Clock (UTCTime)
51import Data.Time.Format (FormatTime, formatTime, parseTime)
52import Data.Time.Format (FormatTime, formatTime, parseTime)
52−import Data.Attoparsec.Char8 (Number(..))
53import Data.Typeable (Typeable)
53import Data.Typeable (Typeable)
54import Data.Vector (Vector)
54import Data.Vector (Vector)
55import Data.Word (Word, Word8, Word16, Word32, Word64)
55import Data.Word (Word, Word8, Word16, Word32, Word64)
56import System.Locale (defaultTimeLocale)
56import System.Locale (defaultTimeLocale)
57import qualified Data.ByteString as B
57import qualified Data.ByteString as B
58import qualified Data.ByteString.Lazy as LB
58import qualified Data.ByteString.Lazy as LB
59+import qualified Data.HashMap.Strict as H
60+import qualified Data.IntSet as IntSet
59import qualified Data.Map as M
61import qualified Data.Map as M
60import qualified Data.Set as Set
62import qualified Data.Set as Set
61import qualified Data.Text as T
63import qualified Data.Text as T
62import qualified Data.Text.Lazy as LT
64import qualified Data.Text.Lazy as LT
63import qualified Data.Vector as V
65import qualified Data.Vector as V
64−import Data.Aeson.Functions
65
66
66-- | The result of running a 'Parser'.
67-- | The result of running a 'Parser'.
67data Result a = Error String
68data Result a = Error String
@@ -526,6 +527,29 @@ instance (ToJSON v) => ToJSON (M.Map String v) where
526instance (FromJSON v) => FromJSON (M.Map String v) where
527instance (FromJSON v) => FromJSON (M.Map String v) where
527 parseJSON = fmap (M.mapKeysMonotonic unpack) . parseJSON
528 parseJSON = fmap (M.mapKeysMonotonic unpack) . parseJSON
528
529
530+instance (ToJSON v) => ToJSON (H.HashMap Text v) where
531+ toJSON = Object . hashMap id toJSON
532+ {-# INLINE toJSON #-}
533+
534+instance (FromJSON v) => FromJSON (H.HashMap Text v) where
535+ parseJSON (Object o) = H.fromList <$> go (M.toList o)
536+ where
537+ go ((k,v):kvs) = ((:) . (,) k) <$> parseJSON v <*> go kvs
538+ go _ = pure []
539+ parseJSON _ = empty
540+
541+instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
542+ toJSON = Object . M.fromList . H.foldrWithKey (\k v -> ((LT.toStrict k,toJSON v) :)) []
543+
544+instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
545+ parseJSON = fmap (mapHash LT.fromStrict) . parseJSON
546+
547+instance (ToJSON v) => ToJSON (H.HashMap String v) where
548+ toJSON = Object . hashMap pack toJSON
549+
550+instance (FromJSON v) => FromJSON (H.HashMap String v) where
551+ parseJSON = fmap (mapHash unpack) . parseJSON
552+
529instance ToJSON Value where
553instance ToJSON Value where
530 toJSON a = a
554 toJSON a = a
531 {-# INLINE toJSON #-}
555 {-# INLINE toJSON #-}
aeson.cabal+2
@@ -78,11 +78,13 @@ library
78 bytestring,
78 bytestring,
79 containers,
79 containers,
80 deepseq,
80 deepseq,
81+ hashable,
81 monads-fd,
82 monads-fd,
82 old-locale,
83 old-locale,
83 syb,
84 syb,
84 text >= 0.11.0.2,
85 text >= 0.11.0.2,
85 time,
86 time,
87+ unordered-containers,
86 vector >= 0.7
88 vector >= 0.7
87
89
88 if flag(developer)
90 if flag(developer)