on master help sign in
Introduce Data.Aeson.Text and tidy up modules a bit
commit4097dc9bfbf3494c593df93622034d93111b751a authorOleg Grenrus <oleg.grenrus@iki.fi> dateWed 8 Jun 2016, 14:09 (10 years ago) parentf81a260
16 files changed+179−171
Data/Aeson.hs+7−1 Data/Aeson/Encode.hs+12−94 Data/Aeson/Encode/Functions.hs−14 Data/Aeson/Encoding.hs+1 Data/Aeson/Encode/Builder.hs → Data/Aeson/Encoding/Builder.hs+2−2 Data/Aeson/Encoding/Internal.hs+9−4 Data/Aeson/Functions.hs → Data/Aeson/Internal/Functions.hs+1−8 Data/Aeson/Text.hs+97 Data/Aeson/Types/FromJSON.hs+1−1 Data/Aeson/Types/ToJSON.hs+26−30 aeson.cabal+11−6 benchmarks/CompareWithJSON.hs+1−1 benchmarks/Micro.hs+1−1 benchmarks/aeson-benchmarks.cabal+7−6 tests/Properties.hs+1−2 tests/UnitTests.hs+2−1
Data/Aeson.hs+7−1
@@ -109,7 +109,7 @@ module Data.Aeson
109109 ) where
110110
111111import Data.Aeson.Types.FromJSON (ifromJSON)
112−import Data.Aeson.Encode (encode)
112+import Data.Aeson.Encoding (encodingToLazyByteString)
113113import Data.Aeson.Parser.Internal (decodeWith, decodeStrictWith,
114114 eitherDecodeWith, eitherDecodeStrictWith,
115115 jsonEOF, json, jsonEOF', json')
@@ -118,6 +118,12 @@ import Data.Aeson.Types.Internal (JSONPath, formatError)
118118import qualified Data.ByteString as B
119119import qualified Data.ByteString.Lazy as L
120120
121+-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
122+--
123+-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
124+encode :: (ToJSON a) => a -> L.ByteString
125+encode = encodingToLazyByteString . toEncoding
126+
121127-- | Efficiently deserialize a JSON value from a lazy 'L.ByteString'.
122128-- If this fails due to incomplete or invalid input, 'Nothing' is
123129-- returned.
Data/Aeson/Encode.hs+12−94
@@ -1,5 +1,3 @@
1−{-# LANGUAGE BangPatterns, OverloadedStrings #-}
2−
31-- |
42-- Module: Data.Aeson.Encode
53-- Copyright: (c) 2012-2016 Bryan O'Sullivan
@@ -9,102 +7,22 @@
97-- Stability: experimental
108-- Portability: portable
119--
12−-- Efficiently serialize a JSON value.
13−--
14−-- Most frequently, you'll probably want to encode straight to UTF-8
15−-- (the standard JSON encoding) using 'encode'.
16−--
17−-- You can use the conversions to 'Builder's when embedding JSON messages as
18−-- parts of a protocol.
19−module Data.Aeson.Encode
10+-- This module is left to supply limited backwards-compatibility.
11+module Data.Aeson.Encode {-# DEPRECATED "Use Data.Aeson or Data.Aeson.Text instead" #-}
2012 ( encode
21−
22− -- * Encoding to Builders
23− , encodeToBuilder
2413 , encodeToTextBuilder
25−
26− -- * Deprecated
27− , fromValue
2814 ) where
2915
30−import Data.Aeson.Encode.Builder (encodeToBuilder)
31−import Data.Aeson.Types (Value(..), ToJSON (..))
32−import Data.Aeson.Encoding (fromEncoding)
33−import Data.Monoid ((<>))
34−import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
35−import Data.Text.Lazy.Builder
36−import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
37−
38−import Numeric (showHex)
39−import qualified Data.ByteString.Builder as B
40−import qualified Data.HashMap.Strict as H
41−import qualified Data.Text as T
42−import qualified Data.Vector as V
43−import qualified Data.ByteString.Lazy as L
44−
45−-- | Efficiently serialize a JSON value as a lazy 'L.ByteString'.
46−--
47−-- This is implemented in terms of the 'ToJSON' class's 'toEncoding' method.
48−encode :: ToJSON a => a -> L.ByteString
49−encode = B.toLazyByteString . fromEncoding . toEncoding
50−{-# INLINE encode #-}
51−
52−-- | Encode a JSON 'Value' to a "Data.Text" 'Builder', which can be
53−-- embedded efficiently in a text-based protocol.
54−--
55−-- If you are going to immediately encode straight to a
56−-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
57−-- instead.
58−encodeToTextBuilder :: Value -> Builder
59−encodeToTextBuilder =
60− go
61− where
62− go Null = {-# SCC "go/Null" #-} "null"
63− go (Bool b) = {-# SCC "go/Bool" #-} if b then "true" else "false"
64− go (Number s) = {-# SCC "go/Number" #-} fromScientific s
65− go (String s) = {-# SCC "go/String" #-} string s
66− go (Array v)
67− | V.null v = {-# SCC "go/Array" #-} "[]"
68− | otherwise = {-# SCC "go/Array" #-}
69− singleton '[' <>
70− go (V.unsafeHead v) <>
71− V.foldr f (singleton ']') (V.unsafeTail v)
72− where f a z = singleton ',' <> go a <> z
73− go (Object m) = {-# SCC "go/Object" #-}
74− case H.toList m of
75− (x:xs) -> singleton '{' <> one x <> foldr f (singleton '}') xs
76− _ -> "{}"
77− where f a z = singleton ',' <> one a <> z
78− one (k,v) = string k <> singleton ':' <> go v
79−
80−{-# DEPRECATED fromValue "Use 'encodeToTextBuilder' instead" #-}
81−fromValue :: Value -> Builder
82−fromValue = encodeToTextBuilder
16+import Data.ByteString.Lazy (ByteString)
17+import Data.Text.Lazy.Builder (Builder)
8318
84−string :: T.Text -> Builder
85−string s = {-# SCC "string" #-} singleton '"' <> quote s <> singleton '"'
86− where
87− quote q = case T.uncons t of
88− Nothing -> fromText h
89− Just (!c,t') -> fromText h <> escape c <> quote t'
90− where (h,t) = {-# SCC "break" #-} T.break isEscape q
91− isEscape c = c == '\"' ||
92− c == '\\' ||
93− c < '\x20'
94− escape '\"' = "\\\""
95− escape '\\' = "\\\\"
96− escape '\n' = "\\n"
97− escape '\r' = "\\r"
98− escape '\t' = "\\t"
19+import qualified Data.Aeson as A
20+import qualified Data.Aeson.Text as A
9921
100− escape c
101− | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
102− | otherwise = singleton c
103− where h = showHex (fromEnum c) ""
22+encode :: A.ToJSON a => a -> ByteString
23+encode = A.encode
24+{-# DEPRECATED encode "Use encode from Data.Aeson" #-}
10425
105−fromScientific :: Scientific -> Builder
106−fromScientific s = formatScientificBuilder format prec s
107− where
108− (format, prec)
109− | base10Exponent s < 0 = (Generic, Nothing)
110− | otherwise = (Fixed, Just 0)
26+encodeToTextBuilder :: A.Value -> Builder
27+encodeToTextBuilder = A.encodeToTextBuilder
28+{-# DEPRECATED encodeToTextBuilder "Use encodeTotextBuilder from Data.Aeson.Text" #-}
Data/Aeson/Encode/Functions.hsdeleted−14
@@ -1,14 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−
3−module Data.Aeson.Encode.Functions
4− (
5− builder
6− ) where
7−
8−import Data.Aeson.Encoding
9−import Data.Aeson.Types.Class
10−import Data.ByteString.Builder (Builder)
11−import Data.Monoid ((<>))
12−
13−
14−{-# INLINE builder #-}
Data/Aeson/Encoding.hs+1
@@ -2,6 +2,7 @@ module Data.Aeson.Encoding
22 (
33 -- * Encoding
44 Encoding
5+ , encodingToLazyByteString
56 , fromEncoding
67 , unsafeToEncoding
78 , Series
Data/Aeson/Encode/Builder.hs → Data/Aeson/Encoding/Builder.hsrenamed+2−2
@@ -1,7 +1,7 @@
11{-# LANGUAGE BangPatterns, OverloadedStrings #-}
22
33-- |
4−-- Module: Data.Aeson.Encode.Builder
4+-- Module: Data.Aeson.Encoding.Builder
55-- Copyright: (c) 2011 MailRank, Inc.
66-- (c) 2013 Simon Meier <iridcode@gmail.com>
77-- License: BSD3
@@ -11,7 +11,7 @@
1111--
1212-- Efficiently serialize a JSON value using the UTF-8 encoding.
1313
14−module Data.Aeson.Encode.Builder
14+module Data.Aeson.Encoding.Builder
1515 (
1616 encodeToBuilder
1717 , null_
Data/Aeson/Encoding/Internal.hs+9−4
@@ -6,6 +6,7 @@ module Data.Aeson.Encoding.Internal
66 (
77 -- * Encoding
88 Encoding (..) -- TODO: export fromEncoding for now
9+ , encodingToLazyByteString
910 , unsafeToEncoding
1011 , Series (..) -- TODO: don't export constructor
1112 , pairs
@@ -62,10 +63,10 @@ import Data.Word
6263
6364import Data.Aeson.Types.Internal (Value)
6465
65−import qualified Data.Aeson.Encode.Builder as EB
66−import qualified Data.ByteString.Builder as B
67−import qualified Data.ByteString.Lazy as BSL
68−import qualified Data.Text.Lazy as LT
66+import qualified Data.Aeson.Encoding.Builder as EB
67+import qualified Data.ByteString.Builder as B
68+import qualified Data.ByteString.Lazy as BSL
69+import qualified Data.Text.Lazy as LT
6970
7071-- | An encoding of a JSON value.
7172newtype Encoding = Encoding {
@@ -80,6 +81,10 @@ newtype Encoding = Encoding {
8081unsafeToEncoding :: Builder -> Encoding
8182unsafeToEncoding = Encoding
8283
84+encodingToLazyByteString :: Encoding -> BSL.ByteString
85+encodingToLazyByteString = toLazyByteString . fromEncoding
86+{-# INLINE encodingToLazyByteString #-}
87+
8388-------------------------------------------------------------------------------
8489-- Encoding instances
8590-------------------------------------------------------------------------------
Data/Aeson/Functions.hs → Data/Aeson/Internal/Functions.hsrenamed+1−8
@@ -7,9 +7,8 @@
77-- Stability: experimental
88-- Portability: portable
99
10−module Data.Aeson.Functions
10+module Data.Aeson.Internal.Functions
1111 ( mapHashKeyVal
12− , hashMapKey
1312 , mapKeyVal
1413 , mapKey
1514 ) where
@@ -24,12 +23,6 @@ mapHashKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
2423mapHashKeyVal fk kv = M.foldrWithKey (\k v -> H.insert (fk k) (kv v)) H.empty
2524{-# INLINE mapHashKeyVal #-}
2625
27−-- | Transform a 'M.Map' into a 'H.HashMap' while transforming the keys.
28−hashMapKey :: (Ord k2) => (k1 -> k2)
29− -> H.HashMap k1 v -> M.Map k2 v
30−hashMapKey kv = H.foldrWithKey (M.insert . kv) M.empty
31−{-# INLINE hashMapKey #-}
32−
3326-- | Transform the keys and values of a 'H.HashMap'.
3427mapKeyVal :: (Eq k2, Hashable k2) => (k1 -> k2) -> (v1 -> v2)
3528 -> H.HashMap k1 v1 -> H.HashMap k2 v2
Data/Aeson/Text.hsnew file+97
@@ -0,0 +1,97 @@
1+{-# LANGUAGE BangPatterns, OverloadedStrings #-}
2+-- |
3+-- Module: Data.Aeson.Text
4+-- Copyright: (c) 2012-2016 Bryan O'Sullivan
5+-- (c) 2011 MailRank, Inc.
6+-- License: BSD3
7+-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
8+-- Stability: experimental
9+-- Portability: portable
10+--
11+-- Most frequently, you'll probably want to encode straight to UTF-8
12+-- (the standard JSON encoding) using 'encode'.
13+--
14+-- You can use the conversions to 'Builder's when embedding JSON messages as
15+-- parts of a protocol.
16+module Data.Aeson.Text
17+ ( encodeToLazyText
18+ , encodeToTextBuilder
19+ ) where
20+
21+import Data.Aeson.Types (Value(..), ToJSON(..))
22+import Data.Aeson.Encoding (encodingToLazyByteString)
23+import Data.Monoid ((<>))
24+import Data.Scientific (FPFormat(..), Scientific, base10Exponent)
25+import Data.Text.Lazy.Builder
26+import Data.Text.Lazy.Builder.Scientific (formatScientificBuilder)
27+
28+import Numeric (showHex)
29+import qualified Data.HashMap.Strict as H
30+import qualified Data.Text as T
31+import qualified Data.Text.Lazy as LT
32+import qualified Data.Text.Lazy.Encoding as LT
33+import qualified Data.Vector as V
34+
35+-- | Encode a JSON 'Value' to a "Data.Text.Lazy"
36+--
37+-- /Note:/ uses 'toEncoding'
38+encodeToLazyText :: ToJSON a => a -> LT.Text
39+encodeToLazyText = LT.decodeUtf8 . encodingToLazyByteString . toEncoding
40+
41+-- | Encode a JSON 'Value' to a "Data.Text" 'Builder', which can be
42+-- embedded efficiently in a text-based protocol.
43+--
44+-- If you are going to immediately encode straight to a
45+-- 'L.ByteString', it is more efficient to use 'encodeToBuilder'
46+-- instead.
47+--
48+-- /Note:/ Uses 'toJSON'
49+encodeToTextBuilder :: ToJSON a => a -> Builder
50+encodeToTextBuilder =
51+ go . toJSON
52+ where
53+ go Null = {-# SCC "go/Null" #-} "null"
54+ go (Bool b) = {-# SCC "go/Bool" #-} if b then "true" else "false"
55+ go (Number s) = {-# SCC "go/Number" #-} fromScientific s
56+ go (String s) = {-# SCC "go/String" #-} string s
57+ go (Array v)
58+ | V.null v = {-# SCC "go/Array" #-} "[]"
59+ | otherwise = {-# SCC "go/Array" #-}
60+ singleton '[' <>
61+ go (V.unsafeHead v) <>
62+ V.foldr f (singleton ']') (V.unsafeTail v)
63+ where f a z = singleton ',' <> go a <> z
64+ go (Object m) = {-# SCC "go/Object" #-}
65+ case H.toList m of
66+ (x:xs) -> singleton '{' <> one x <> foldr f (singleton '}') xs
67+ _ -> "{}"
68+ where f a z = singleton ',' <> one a <> z
69+ one (k,v) = string k <> singleton ':' <> go v
70+
71+string :: T.Text -> Builder
72+string s = {-# SCC "string" #-} singleton '"' <> quote s <> singleton '"'
73+ where
74+ quote q = case T.uncons t of
75+ Nothing -> fromText h
76+ Just (!c,t') -> fromText h <> escape c <> quote t'
77+ where (h,t) = {-# SCC "break" #-} T.break isEscape q
78+ isEscape c = c == '\"' ||
79+ c == '\\' ||
80+ c < '\x20'
81+ escape '\"' = "\\\""
82+ escape '\\' = "\\\\"
83+ escape '\n' = "\\n"
84+ escape '\r' = "\\r"
85+ escape '\t' = "\\t"
86+
87+ escape c
88+ | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
89+ | otherwise = singleton c
90+ where h = showHex (fromEnum c) ""
91+
92+fromScientific :: Scientific -> Builder
93+fromScientific s = formatScientificBuilder format prec s
94+ where
95+ (format, prec)
96+ | base10Exponent s < 0 = (Generic, Nothing)
97+ | otherwise = (Fixed, Just 0)
Data/Aeson/Types/FromJSON.hs+1−1
@@ -69,7 +69,7 @@ module Data.Aeson.Types.FromJSON (
6969import Prelude ()
7070import Prelude.Compat hiding (foldr)
7171
72−import Data.Aeson.Functions (mapKey)
72+import Data.Aeson.Internal.Functions (mapKey)
7373import Data.Aeson.Types.Generic
7474import Data.Aeson.Types.Internal
7575
Data/Aeson/Types/ToJSON.hs+26−30
@@ -55,9 +55,9 @@ module Data.Aeson.Types.ToJSON (
5555import Prelude ()
5656import Prelude.Compat
5757
58−import Data.Aeson.Encoding.Internal (Encoding (..), Series, dict, emptyArray_,
59− tuple, (>*<))
60−import Data.Aeson.Functions (mapHashKeyVal, mapKeyVal)
58+import Data.Aeson.Encoding.Internal (Encoding (..), Series, dict, emptyArray_,
59+ tuple, (>*<))
60+import Data.Aeson.Internal.Functions (mapHashKeyVal, mapKeyVal)
6161import Data.Aeson.Types.Generic
6262import Data.Aeson.Types.Internal
6363
@@ -92,35 +92,31 @@ import Foreign.Storable (Storable)
9292import GHC.Generics
9393import Numeric.Natural (Natural)
9494
95−import qualified Data.ByteString.Builder as B (toLazyByteString)
96−import qualified Data.ByteString.Lazy as L
97−import qualified Data.DList as DList
98−import qualified Data.HashMap.Strict as H
99−import qualified Data.HashSet as HashSet
100−import qualified Data.IntMap as IntMap
101−import qualified Data.IntSet as IntSet
102−import qualified Data.List.NonEmpty as NE
103−import qualified Data.Map as M
104−import qualified Data.Scientific as Scientific
105−import qualified Data.Sequence as Seq
106−import qualified Data.Set as Set
107−import qualified Data.Text as T
108−import qualified Data.Text.Encoding as T
109−import qualified Data.Text.Lazy as LT
110−import qualified Data.Tree as Tree
111−import qualified Data.Vector as V
112−import qualified Data.Vector.Generic as VG
113−import qualified Data.Vector.Mutable as VM
114−import qualified Data.Vector.Primitive as VP
115−import qualified Data.Vector.Storable as VS
116−import qualified Data.Vector.Unboxed as VU
95+import qualified Data.ByteString.Builder as B (toLazyByteString)
96+import qualified Data.ByteString.Lazy as L
97+import qualified Data.DList as DList
98+import qualified Data.HashMap.Strict as H
99+import qualified Data.HashSet as HashSet
100+import qualified Data.IntMap as IntMap
101+import qualified Data.IntSet as IntSet
102+import qualified Data.List.NonEmpty as NE
103+import qualified Data.Map as M
104+import qualified Data.Scientific as Scientific
105+import qualified Data.Sequence as Seq
106+import qualified Data.Set as Set
107+import qualified Data.Text as T
108+import qualified Data.Text.Encoding as T
109+import qualified Data.Text.Lazy as LT
110+import qualified Data.Tree as Tree
111+import qualified Data.Vector as V
112+import qualified Data.Vector.Generic as VG
113+import qualified Data.Vector.Mutable as VM
114+import qualified Data.Vector.Primitive as VP
115+import qualified Data.Vector.Storable as VS
116+import qualified Data.Vector.Unboxed as VU
117117
118118toJSONPair :: (a -> Value) -> (b -> Value) -> (a, b) -> Value
119−toJSONPair keySerialiser valSerializer (a, b) = Array $ V.create $ do
120− mv <- VM.unsafeNew 2
121− VM.unsafeWrite mv 0 (keySerialiser a)
122− VM.unsafeWrite mv 1 (valSerializer b)
123− return mv
119+toJSONPair a b = liftToJSON2 a (listValue a) b (listValue b)
124120{-# INLINE toJSONPair #-}
125121
126122realFloatToJSON :: RealFloat a => a -> Value
aeson.cabal+11−6
@@ -63,18 +63,23 @@ library
6363
6464 exposed-modules:
6565 Data.Aeson
66− Data.Aeson.Encode
6766 Data.Aeson.Encoding
68− Data.Aeson.Encoding.Internal
69− Data.Aeson.Internal
70− Data.Aeson.Internal.Time
7167 Data.Aeson.Parser
68+ Data.Aeson.Text
7269 Data.Aeson.Types
7370 Data.Aeson.TH
7471
72+ Data.Aeson.Encoding.Internal
73+ Data.Aeson.Internal
74+ Data.Aeson.Internal.Time
75+
76+ -- Deprecated modules
77+ exposed-modules:
78+ Data.Aeson.Encode
79+
7580 other-modules:
76− Data.Aeson.Encode.Builder
77− Data.Aeson.Functions
81+ Data.Aeson.Encoding.Builder
82+ Data.Aeson.Internal.Functions
7883 Data.Aeson.Parser.Internal
7984 Data.Aeson.Parser.Time
8085 Data.Aeson.Types.FromJSON
benchmarks/CompareWithJSON.hs+1−1
@@ -4,8 +4,8 @@ import Blaze.ByteString.Builder (toLazyByteString)
44import Blaze.ByteString.Builder.Char.Utf8 (fromString)
55import Control.DeepSeq (NFData(rnf))
66import Criterion.Main
7−import qualified Data.Aeson.Encode as A
87import qualified Data.Aeson as A
8+import qualified Data.Aeson.Text as A
99import qualified Data.ByteString.Lazy as BL
1010import qualified Text.JSON as J
1111import qualified Data.Text.Lazy as TL
benchmarks/Micro.hs+1−1
@@ -1,7 +1,7 @@
11module Main (main) where
22
33import Criterion.Main
4−import qualified Data.Aeson.Encode.Builder as AB
4+import qualified Data.Aeson.Encoding.Builder as AB
55import qualified Data.ByteString.Builder as B
66import qualified Data.Text as T
77
benchmarks/aeson-benchmarks.cabal+7−6
@@ -9,17 +9,18 @@ library
99
1010 exposed-modules:
1111 Data.Aeson
12− Data.Aeson.Encode
1312 Data.Aeson.Encoding
14− Data.Aeson.Encoding.Internal
15− Data.Aeson.Internal
16− Data.Aeson.Internal.Time
1713 Data.Aeson.Parser
14+ Data.Aeson.Text
1815 Data.Aeson.Types
1916 Data.Aeson.TH
2017
21− Data.Aeson.Encode.Builder
22− Data.Aeson.Functions
18+ Data.Aeson.Encoding.Internal
19+ Data.Aeson.Internal
20+ Data.Aeson.Internal.Time
21+
22+ Data.Aeson.Encoding.Builder
23+ Data.Aeson.Internal.Functions
2324 Data.Aeson.Parser.Internal
2425 Data.Aeson.Parser.Time
2526 Data.Aeson.Types.FromJSON
tests/Properties.hs+1−2
@@ -6,8 +6,7 @@ import Prelude ()
66import Prelude.Compat
77
88import Control.Applicative (Const)
9−import Data.Aeson (eitherDecode)
10−import Data.Aeson.Encode (encode)
9+import Data.Aeson (eitherDecode, encode)
1110import Data.Aeson.Internal (IResult(..), formatError, ifromJSON, iparse)
1211import Data.Aeson.Parser (value)
1312import Data.Aeson.Types
tests/UnitTests.hs+2−1
@@ -13,7 +13,8 @@ import Prelude.Compat
1313import Control.Applicative (Const(..))
1414import Control.Monad (forM)
1515import Data.Aeson (FromJSONKeyFunction(..), FromJSONKey(..), decode, eitherDecode, encode, genericToJSON, genericToEncoding, object, FromJSON(..), withObject, (.=), (.:), (.:?), (.:!))
16−import Data.Aeson.Encode (encodeToTextBuilder)
16+import Data.Aeson (decode, eitherDecode, encode, genericToJSON, genericToEncoding, object, FromJSON(..), withObject, (.=), (.:), (.:?), (.:!))
17+import Data.Aeson.Text (encodeToTextBuilder)
1718import Data.Aeson.Internal (JSONPathElement(..), formatError)
1819import Data.Aeson.TH (deriveJSON)
1920import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)