Skip to content

Commit 0611427

Browse files
authored
Merge pull request #868 from haskell/key-type
Data.Aeson.Key
2 parents 8673387 + 2719321 commit 0611427

18 files changed

+275
-143
lines changed

aeson.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ library
6868
Data.Aeson.Internal
6969
Data.Aeson.Internal.Time
7070
Data.Aeson.Parser.Internal
71+
Data.Aeson.Key
7172
Data.Aeson.KeyMap
7273

7374
-- Deprecated modules

benchmarks/aeson-benchmarks.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ library
8181
Data.Aeson.Internal
8282
Data.Aeson.Internal.Functions
8383
Data.Aeson.Internal.Time
84+
Data.Aeson.Key
8485
Data.Aeson.KeyMap
8586
Data.Aeson.Parser
8687
Data.Aeson.Parser.Internal

src/Data/Aeson.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ module Data.Aeson
5555
, fromEncoding
5656
, Array
5757
, Object
58+
, Key
5859
-- * Convenience types
5960
, DotNetTime(..)
6061
-- * Type conversion

src/Data/Aeson/Encoding/Builder.hs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ module Data.Aeson.Encoding.Builder
4141
import Prelude.Compat
4242

4343
import Data.Aeson.Internal.Time
44-
import Data.Aeson.Types.Internal (Value (..))
44+
import Data.Aeson.Types.Internal (Value (..), Key)
45+
import qualified Data.Aeson.Key as Key
4546
import qualified Data.Aeson.KeyMap as KM
4647
import Data.ByteString.Builder as B
4748
import Data.ByteString.Builder.Prim as BP
@@ -96,7 +97,11 @@ object m = case KM.toList m of
9697
_ -> emptyObject_
9798
where
9899
withComma a z = B.char8 ',' <> one a <> z
99-
one (k,v) = text k <> B.char8 ':' <> encodeToBuilder v
100+
one (k,v) = key k <> B.char8 ':' <> encodeToBuilder v
101+
102+
-- | Encode a JSON key.
103+
key :: Key -> Builder
104+
key = text . Key.toText
100105

101106
-- | Encode a JSON string.
102107
text :: T.Text -> Builder

src/Data/Aeson/Encoding/Internal.hs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ module Data.Aeson.Encoding.Internal
2626
, wrapArray
2727
, null_
2828
, bool
29+
, key
2930
, text
3031
, lazyText
3132
, string
@@ -61,8 +62,9 @@ module Data.Aeson.Encoding.Internal
6162

6263
import Prelude.Compat
6364

64-
import Data.Aeson.Types.Internal (Value)
65+
import Data.Aeson.Types.Internal (Value, Key)
6566
import Data.ByteString.Builder (Builder, char7, toLazyByteString)
67+
import qualified Data.Aeson.Key as Key
6668
import Data.Int
6769
import Data.Scientific (Scientific)
6870
import Data.Text (Text)
@@ -127,15 +129,15 @@ data Series = Empty
127129
| Value (Encoding' Series)
128130
deriving (Typeable)
129131

130-
pair :: Text -> Encoding -> Series
131-
pair name val = pair' (text name) val
132+
pair :: Key -> Encoding -> Series
133+
pair name val = pair' (key name) val
132134
{-# INLINE pair #-}
133135

134136
pairStr :: String -> Encoding -> Series
135137
pairStr name val = pair' (string name) val
136138
{-# INLINE pairStr #-}
137139

138-
pair' :: Encoding' Text -> Encoding -> Series
140+
pair' :: Encoding' Key -> Encoding -> Series
139141
pair' name val = Value $ retagEncoding $ retagEncoding name >< colon >< val
140142

141143
instance Semigroup Series where
@@ -184,7 +186,7 @@ list to' (x:xs) = openBracket >< to' x >< commas xs >< closeBracket
184186

185187
-- | Encode as JSON object
186188
dict
187-
:: (k -> Encoding' Text) -- ^ key encoding
189+
:: (k -> Encoding' Key) -- ^ key encoding
188190
-> (v -> Encoding) -- ^ value encoding
189191
-> (forall a. (k -> v -> a -> a) -> a -> m -> a) -- ^ @foldrWithKey@ - indexed fold
190192
-> m -- ^ container
@@ -226,6 +228,9 @@ tuple :: Encoding' InArray -> Encoding
226228
tuple b = retagEncoding $ openBracket >< b >< closeBracket
227229
{-# INLINE tuple #-}
228230

231+
key :: Key -> Encoding' a
232+
key = text . Key.toText
233+
229234
text :: Text -> Encoding' a
230235
text = Encoding . EB.text
231236

src/Data/Aeson/Internal/Functions.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ module Data.Aeson.Internal.Functions
1717
import Prelude.Compat
1818

1919
import Data.Hashable (Hashable)
20+
import Data.Aeson.Key (Key)
2021
import qualified Data.Aeson.KeyMap as KM
2122
import qualified Data.HashMap.Strict as H
2223
import qualified Data.Map as M
23-
import qualified Data.Text as T
2424

2525
-- | Transform a 'M.Map' into a 'KM.KeyMap' while transforming the keys.
26-
mapTextKeyVal :: (k -> T.Text) -> (v1 -> v2)
26+
mapTextKeyVal :: (k -> Key) -> (v1 -> v2)
2727
-> M.Map k v1 -> KM.KeyMap v2
2828
mapTextKeyVal fk kv = M.foldrWithKey (\k v -> KM.insert (fk k) (kv v)) KM.empty
2929
{-# INLINE mapTextKeyVal #-}

src/Data/Aeson/Key.hs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE DeriveDataTypeable #-}
3+
4+
#if __GLASGOW_HASKELL__ >= 800
5+
-- a) THQ works on cross-compilers and unregisterised GHCs
6+
-- b) may make compilation faster as no dynamic loading is ever needed (not sure about this)
7+
-- c) removes one hindrance to have code inferred as SafeHaskell safe
8+
{-# LANGUAGE TemplateHaskellQuotes #-}
9+
#else
10+
{-# LANGUAGE TemplateHaskell #-}
11+
#endif
12+
13+
module Data.Aeson.Key (
14+
Key,
15+
fromString,
16+
toString,
17+
toText,
18+
fromText,
19+
) where
20+
21+
import Prelude (Eq, Ord, (.), Show (..), String)
22+
23+
import Control.Applicative ((<$>))
24+
import Control.DeepSeq (NFData(..))
25+
import Data.Data (Data)
26+
import Data.Hashable (Hashable(..))
27+
import Data.Monoid (Monoid(mempty, mappend))
28+
import Data.Semigroup (Semigroup((<>)))
29+
import Data.Text (Text)
30+
import Data.Typeable (Typeable)
31+
import Text.Read (Read (..))
32+
33+
import qualified Data.String
34+
import qualified Data.Text as T
35+
import qualified Language.Haskell.TH.Syntax as TH
36+
37+
newtype Key = Key { unKey :: Text }
38+
deriving (Eq, Ord, Typeable, Data)
39+
40+
fromString :: String -> Key
41+
fromString = Key . T.pack
42+
43+
toString :: Key -> String
44+
toString (Key k) = T.unpack k
45+
46+
fromText :: Text -> Key
47+
fromText = Key
48+
49+
toText :: Key -> Text
50+
toText = unKey
51+
52+
-------------------------------------------------------------------------------
53+
-- instances
54+
-------------------------------------------------------------------------------
55+
56+
instance Read Key where
57+
readPrec = fromString <$> readPrec
58+
59+
instance Show Key where
60+
showsPrec d (Key k) = showsPrec d k
61+
62+
instance Data.String.IsString Key where
63+
fromString = fromString
64+
65+
instance Hashable Key where
66+
hashWithSalt salt (Key k) = hashWithSalt salt k
67+
68+
instance NFData Key where
69+
rnf (Key k) = rnf k
70+
71+
instance Semigroup Key where
72+
Key x <> Key y = Key (x <> y)
73+
74+
instance Monoid Key where
75+
mempty = Key mempty
76+
mappend = (<>)
77+
78+
instance TH.Lift Key where
79+
#if MIN_VERSION_text(1,2,4)
80+
lift (Key k) = [| Key k |]
81+
#else
82+
lift k = [| fromString k' |] where k' = toString k
83+
#endif
84+
85+
#if MIN_VERSION_template_haskell(2,17,0)
86+
liftTyped = TH.unsafeCodeCoerce . TH.lift
87+
#elif MIN_VERSION_template_haskell(2,16,0)
88+
liftTyped = TH.unsafeTExpCoerce . TH.lift
89+
#endif

0 commit comments

Comments
 (0)