on master help sign in
Add direct encoding support

This comes via a new ToJSON method, toEncoding. No type safety or tests yet... eek!

commit692ed3c52356841553e8e2979a2348ed27d00b32 authorBryan O'Sullivan <bos@serpentine.com> dateMon 25 May 2015, 21:46 (11 years ago) parent7c8bbca
6 files changed+610−74
Data/Aeson/Encode/ByteString.hs+48−10 Data/Aeson/Types.hs+1 Data/Aeson/Types/Class.hs+21−4 Data/Aeson/Types/Generic.hs+182−43 Data/Aeson/Types/Instances.hs+338−15 Data/Aeson/Types/Internal.hs+20−2
Data/Aeson/Encode/ByteString.hs+48−10
@@ -18,16 +18,28 @@ module Data.Aeson.Encode.ByteString
1818 , null_
1919 , bool
2020 , array
21+ , emptyArray_
22+ , emptyObject_
2123 , object
2224 , text
25+ , unquoted
2326 , number
27+ , foldable
28+ , series
29+ , ascii2
30+ , ascii4
31+ , ascii5
32+ , ascii6
33+ , ascii7
2434 ) where
2535
26−import Data.Aeson.Types (ToJSON(..), Value(..))
36+import Data.Aeson.Types.Class (ToJSON(..))
37+import Data.Aeson.Types.Internal (Value(..), Series(..))
2738import Data.ByteString.Builder as B
2839import Data.ByteString.Builder.Prim as BP
2940import Data.ByteString.Builder.Scientific (scientificBuilder)
3041import Data.Char (ord)
42+import Data.Foldable (Foldable, foldMap)
3143import Data.Monoid ((<>))
3244import Data.Scientific (Scientific, base10Exponent, coefficient)
3345import Data.Word (Word8)
@@ -86,8 +98,11 @@ object m = case HMS.toList m of
8698
8799-- | Encode a JSON string.
88100text :: T.Text -> Builder
89−text t =
90− B.char8 '"' <> TE.encodeUtf8BuilderEscaped escapeAscii t <> B.char8 '"'
101+text t = B.char8 '"' <> unquoted t <> B.char8 '"'
102+
103+-- | Encode a JSON string, without enclosing quotes.
104+unquoted :: T.Text -> Builder
105+unquoted t = TE.encodeUtf8BuilderEscaped escapeAscii t
91106 where
92107 escapeAscii :: BP.BoundedPrim Word8
93108 escapeAscii =
@@ -103,7 +118,7 @@ text t =
103118
104119 hexEscape :: BP.FixedPrim Word8
105120 hexEscape = (\c -> ('\\', ('u', fromIntegral c))) BP.>$<
106− BP.char8 BP.>*< BP.char8 BP.>*< BP.word16HexFixed
121+ BP.char8 >*< BP.char8 >*< BP.word16HexFixed
107122
108123-- | Encode a JSON number.
109124number :: Scientific -> Builder
@@ -113,17 +128,40 @@ number s
113128 where
114129 e = base10Exponent s
115130
131+foldable :: (Foldable t, ToJSON a) => t a -> Builder
132+foldable = series '[' ']' . foldMap (Value . toEncoding)
133+
134+series :: Char -> Char -> Series -> Builder
135+series begin end (Value v) = B.char7 begin <> v <> B.char7 end
136+series begin end Empty = BP.primBounded (ascii2 (begin,end)) ()
137+
138+emptyArray_ :: Builder
139+emptyArray_ = BP.primBounded (ascii2 ('[',']')) ()
140+
141+emptyObject_ :: Builder
142+emptyObject_ = BP.primBounded (ascii2 ('{','}')) ()
116143
117−{-# INLINE ascii2 #-}
118144ascii2 :: (Char, Char) -> BP.BoundedPrim a
119−ascii2 cs = BP.liftFixedToBounded $ (const cs) BP.>$< BP.char7 BP.>*< BP.char7
145+ascii2 cs = BP.liftFixedToBounded $ (const cs) BP.>$< BP.char7 >*< BP.char7
146+{-# INLINE ascii2 #-}
120147
121−{-# INLINE ascii4 #-}
122148ascii4 :: (Char, (Char, (Char, Char))) -> BP.BoundedPrim a
123149ascii4 cs = BP.liftFixedToBounded $ (const cs) >$<
124− BP.char7 BP.>*< BP.char7 BP.>*< BP.char7 BP.>*< BP.char7
150+ BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
151+{-# INLINE ascii4 #-}
125152
126−{-# INLINE ascii5 #-}
127153ascii5 :: (Char, (Char, (Char, (Char, Char)))) -> BP.BoundedPrim a
128154ascii5 cs = BP.liftFixedToBounded $ (const cs) >$<
129− BP.char7 BP.>*< BP.char7 BP.>*< BP.char7 BP.>*< BP.char7 BP.>*< BP.char7
155+ BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
156+{-# INLINE ascii5 #-}
157+
158+ascii6 :: (Char, (Char, (Char, (Char, (Char, Char))))) -> BP.BoundedPrim a
159+ascii6 cs = BP.liftFixedToBounded $ (const cs) >$<
160+ BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7
161+{-# INLINE ascii6 #-}
162+
163+ascii7 :: (Char, (Char, (Char, (Char, (Char, (Char, Char)))))) -> BP.BoundedPrim a
164+ascii7 cs = BP.liftFixedToBounded $ (const cs) >$<
165+ BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*< BP.char7 >*<
166+ BP.char7 >*< BP.char7
167+{-# INLINE ascii7 #-}
Data/Aeson/Types.hs+1
@@ -13,6 +13,7 @@ module Data.Aeson.Types
1313 (
1414 -- * Core JSON types
1515 Value(..)
16+ , Encoding
1617 , Array
1718 , emptyArray
1819 , Pair
Data/Aeson/Types/Class.hs+21−4
@@ -2,7 +2,7 @@
22
33-- |
44-- Module: Data.Aeson.Types.Class
5−-- Copyright: (c) 2011-2013 Bryan O'Sullivan
5+-- Copyright: (c) 2011-2015 Bryan O'Sullivan
66-- (c) 2011 MailRank, Inc.
77-- License: Apache
88-- Maintainer: Bryan O'Sullivan <bos@serpentine.com>
@@ -21,6 +21,7 @@ module Data.Aeson.Types.Class
2121 , GFromJSON(..)
2222 , GToJSON(..)
2323 , genericToJSON
24+ , genericToEncoding
2425 , genericParseJSON
2526 ) where
2627
@@ -33,18 +34,28 @@ class GToJSON f where
3334 -- default generic implementation of 'toJSON'.
3435 gToJSON :: Options -> f a -> Value
3536
37+ -- | This method (applied to 'defaultOptions') is used as the
38+ -- default generic implementation of 'toEncoding'.
39+ gToEncoding :: Options -> f a -> Encoding
40+
3641-- | Class of generic representation types ('Rep') that can be converted from JSON.
3742class GFromJSON f where
3843 -- | This method (applied to 'defaultOptions') is used as the
3944 -- default generic implementation of 'parseJSON'.
4045 gParseJSON :: Options -> Value -> Parser (f a)
4146
42−-- | A configurable generic JSON encoder. This function applied to
47+-- | A configurable generic JSON creator. This function applied to
4348-- 'defaultOptions' is used as the default for 'toJSON' when the type
4449-- is an instance of 'Generic'.
4550genericToJSON :: (Generic a, GToJSON (Rep a)) => Options -> a -> Value
4651genericToJSON opts = gToJSON opts . from
4752
53+-- | A configurable generic JSON encoder. This function applied to
54+-- 'defaultOptions' is used as the default for 'toEncoding' when the type
55+-- is an instance of 'Generic'.
56+genericToEncoding :: (Generic a, GToJSON (Rep a)) => Options -> a -> Encoding
57+genericToEncoding opts = gToEncoding opts . from
58+
4859-- | A configurable generic JSON decoder. This function applied to
4960-- 'defaultOptions' is used as the default for 'parseJSON' when the
5061-- type is an instance of 'Generic'.
@@ -101,14 +112,20 @@ genericParseJSON opts = fmap to . gParseJSON opts
101112--
102113-- @
103114-- instance ToJSON Coord where
104−-- toJSON = 'genericToJSON' 'defaultOptions'
115+-- toJSON = 'genericToJSON' 'defaultOptions'
116+-- toEncoding = 'genericToEncoding' 'defaultOptions'
105117-- @
106118class ToJSON a where
107− toJSON :: a -> Value
119+ toJSON :: a -> Value
108120
109121 default toJSON :: (Generic a, GToJSON (Rep a)) => a -> Value
110122 toJSON = genericToJSON defaultOptions
111123
124+ toEncoding :: a -> Encoding
125+
126+ default toEncoding :: (Generic a, GToJSON (Rep a)) => a -> Encoding
127+ toEncoding = genericToEncoding defaultOptions
128+
112129-- | A type that can be converted from JSON, with the possibility of
113130-- failure.
114131--
Data/Aeson/Types/Generic.hs+182−43
@@ -25,9 +25,11 @@ import Control.Monad.ST (ST)
2525import Data.Aeson.Types.Instances
2626import Data.Aeson.Types.Internal
2727import Data.Bits
28+import Data.Aeson.Encode.ByteString (emptyArray_)
29+import Data.ByteString.Builder as B
30+import Data.Monoid ((<>), mempty)
2831import Data.DList (DList, toList, empty)
2932import Data.Maybe (fromMaybe)
30−import Data.Monoid (mappend)
3133import Data.Text (Text, pack, unpack)
3234import GHC.Generics
3335import qualified Data.HashMap.Strict as H
@@ -42,22 +44,34 @@ instance (GToJSON a) => GToJSON (M1 i c a) where
4244 gToJSON opts = gToJSON opts . unM1
4345 {-# INLINE gToJSON #-}
4446
47+ gToEncoding opts = gToEncoding opts . unM1
48+ {-# INLINE gToEncoding #-}
49+
4550instance (ToJSON a) => GToJSON (K1 i a) where
4651 -- Constant values are encoded using their ToJSON instance:
4752 gToJSON _opts = toJSON . unK1
4853 {-# INLINE gToJSON #-}
4954
55+ gToEncoding _opts = toEncoding . unK1
56+ {-# INLINE gToEncoding #-}
57+
5058instance GToJSON U1 where
5159 -- Empty constructors are encoded to an empty array:
5260 gToJSON _opts _ = emptyArray
5361 {-# INLINE gToJSON #-}
5462
63+ gToEncoding _opts _ = emptyArray_
64+ {-# INLINE gToEncoding #-}
65+
5566instance (ConsToJSON a) => GToJSON (C1 c a) where
5667 -- Constructors need to be encoded differently depending on whether they're
57− -- a record or not. This distinction is made by 'constToJSON':
68+ -- a record or not. This distinction is made by 'consToJSON':
5869 gToJSON opts = consToJSON opts . unM1
5970 {-# INLINE gToJSON #-}
6071
72+ gToEncoding opts = consToEncoding opts . unM1
73+ {-# INLINE gToEncoding #-}
74+
6175instance ( WriteProduct a, WriteProduct b
6276 , ProductSize a, ProductSize b ) => GToJSON (a :*: b) where
6377 -- Products are encoded to an array. Here we allocate a mutable vector of
@@ -73,6 +87,8 @@ instance ( WriteProduct a, WriteProduct b
7387 productSize
7488 {-# INLINE gToJSON #-}
7589
90+ gToEncoding opts p = B.char7 '[' <> encodeProduct opts p <> B.char7 ']'
91+
7692instance ( AllNullary (a :+: b) allNullary
7793 , SumToJSON (a :+: b) allNullary ) => GToJSON (a :+: b) where
7894 -- If all constructors of a sum datatype are nullary and the
@@ -82,13 +98,18 @@ instance ( AllNullary (a :+: b) allNullary
8298 . sumToJSON opts
8399 {-# INLINE gToJSON #-}
84100
101+ gToEncoding opts = (unTagged :: Tagged allNullary B.Builder -> B.Builder)
102+ . sumToEncoding opts
103+ {-# INLINE gToEncoding #-}
104+
85105--------------------------------------------------------------------------------
86106
87107class SumToJSON f allNullary where
88108 sumToJSON :: Options -> f a -> Tagged allNullary Value
109+ sumToEncoding :: Options -> f a -> Tagged allNullary B.Builder
89110
90111instance ( GetConName f
91− , TaggedObject f
112+ , TaggedObject f
92113 , ObjectWithSingleField f
93114 , TwoElemArray f ) => SumToJSON f True where
94115 sumToJSON opts
@@ -97,11 +118,19 @@ instance ( GetConName f
97118 | otherwise = Tagged . nonAllNullarySumToJSON opts
98119 {-# INLINE sumToJSON #-}
99120
121+ sumToEncoding opts
122+ | allNullaryToStringTag opts = Tagged . toEncoding .
123+ constructorTagModifier opts . getConName
124+ | otherwise = Tagged . nonAllNullarySumToEncoding opts
125+ {-# INLINE sumToEncoding #-}
126+
100127instance ( TwoElemArray f
101128 , TaggedObject f
102129 , ObjectWithSingleField f ) => SumToJSON f False where
103130 sumToJSON opts = Tagged . nonAllNullarySumToJSON opts
104131 {-# INLINE sumToJSON #-}
132+ sumToEncoding opts = Tagged . nonAllNullarySumToEncoding opts
133+ {-# INLINE sumToEncoding #-}
105134
106135nonAllNullarySumToJSON :: ( TwoElemArray f
107136 , TaggedObject f
@@ -109,46 +138,85 @@ nonAllNullarySumToJSON :: ( TwoElemArray f
109138 ) => Options -> f a -> Value
110139nonAllNullarySumToJSON opts =
111140 case sumEncoding opts of
112− TaggedObject{..} -> object . taggedObject opts tagFieldName
113− contentsFieldName
114− ObjectWithSingleField -> Object . objectWithSingleField opts
115− TwoElemArray -> Array . twoElemArray opts
141+ TaggedObject{..} ->
142+ object . taggedObjectPairs opts tagFieldName contentsFieldName
143+ ObjectWithSingleField -> Object . objectWithSingleFieldObj opts
144+ TwoElemArray -> Array . twoElemArrayObj opts
116145{-# INLINE nonAllNullarySumToJSON #-}
117146
147+nonAllNullarySumToEncoding :: ( TwoElemArray f
148+ , TaggedObject f
149+ , ObjectWithSingleField f
150+ ) => Options -> f a -> B.Builder
151+nonAllNullarySumToEncoding opts =
152+ case sumEncoding opts of
153+ TaggedObject{..} ->
154+ taggedObjectEnc opts tagFieldName contentsFieldName
155+ ObjectWithSingleField -> objectWithSingleFieldEnc opts
156+ TwoElemArray -> twoElemArrayEnc opts
157+{-# INLINE nonAllNullarySumToEncoding #-}
158+
118159--------------------------------------------------------------------------------
119160
120161class TaggedObject f where
121− taggedObject :: Options -> String -> String -> f a -> [Pair]
162+ taggedObjectPairs :: Options -> String -> String -> f a -> [Pair]
163+ taggedObjectEnc :: Options -> String -> String -> f a -> B.Builder
122164
123165instance ( TaggedObject a
124166 , TaggedObject b ) => TaggedObject (a :+: b) where
125− taggedObject opts tagFieldName contentsFieldName (L1 x) =
126− taggedObject opts tagFieldName contentsFieldName x
127− taggedObject opts tagFieldName contentsFieldName (R1 x) =
128− taggedObject opts tagFieldName contentsFieldName x
129− {-# INLINE taggedObject #-}
167+ taggedObjectPairs opts tagFieldName contentsFieldName (L1 x) =
168+ taggedObjectPairs opts tagFieldName contentsFieldName x
169+ taggedObjectPairs opts tagFieldName contentsFieldName (R1 x) =
170+ taggedObjectPairs opts tagFieldName contentsFieldName x
171+ {-# INLINE taggedObjectPairs #-}
172+
173+ taggedObjectEnc opts tagFieldName contentsFieldName (L1 x) =
174+ taggedObjectEnc opts tagFieldName contentsFieldName x
175+ taggedObjectEnc opts tagFieldName contentsFieldName (R1 x) =
176+ taggedObjectEnc opts tagFieldName contentsFieldName x
177+ {-# INLINE taggedObjectEnc #-}
130178
131179instance ( IsRecord a isRecord
132180 , TaggedObject' a isRecord
133181 , Constructor c ) => TaggedObject (C1 c a) where
134− taggedObject opts tagFieldName contentsFieldName =
182+ taggedObjectPairs opts tagFieldName contentsFieldName =
135183 (pack tagFieldName .= constructorTagModifier opts
136184 (conName (undefined :: t c a p)) :) .
137185 (unTagged :: Tagged isRecord [Pair] -> [Pair]) .
138− taggedObject' opts contentsFieldName . unM1
139− {-# INLINE taggedObject #-}
186+ taggedObjectPairs' opts contentsFieldName . unM1
187+ {-# INLINE taggedObjectPairs #-}
188+
189+ taggedObjectEnc opts tagFieldName contentsFieldName v =
190+ B.char7 '{' <>
191+ (toEncoding tagFieldName <>
192+ B.char7 ':' <>
193+ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p)))) <>
194+ B.char7 ',' <>
195+ ((unTagged :: Tagged isRecord B.Builder -> B.Builder) .
196+ taggedObjectEnc' opts contentsFieldName . unM1 $ v) <>
197+ B.char7 '}'
198+ {-# INLINE taggedObjectEnc #-}
140199
141200class TaggedObject' f isRecord where
142− taggedObject' :: Options -> String -> f a -> Tagged isRecord [Pair]
201+ taggedObjectPairs' :: Options -> String -> f a -> Tagged isRecord [Pair]
202+ taggedObjectEnc' :: Options -> String -> f a -> Tagged isRecord B.Builder
203+
204+instance (RecordTo f) => TaggedObject' f True where
205+ taggedObjectPairs' opts _ = Tagged . toList . recordToPairs opts
206+ {-# INLINE taggedObjectPairs' #-}
143207
144−instance (RecordToPairs f) => TaggedObject' f True where
145− taggedObject' opts _ = Tagged . toList . recordToPairs opts
146− {-# INLINE taggedObject' #-}
208+ taggedObjectEnc' opts _ = Tagged . recordToEncoding opts
209+ {-# INLINE taggedObjectEnc' #-}
147210
148211instance (GToJSON f) => TaggedObject' f False where
149− taggedObject' opts contentsFieldName =
212+ taggedObjectPairs' opts contentsFieldName =
150213 Tagged . (:[]) . (pack contentsFieldName .=) . gToJSON opts
151− {-# INLINE taggedObject' #-}
214+ {-# INLINE taggedObjectPairs' #-}
215+
216+ taggedObjectEnc' opts contentsFieldName =
217+ Tagged . (\z -> toEncoding contentsFieldName <> B.char7 ':' <> z) .
218+ gToEncoding opts
219+ {-# INLINE taggedObjectEnc' #-}
152220
153221--------------------------------------------------------------------------------
154222
@@ -168,30 +236,45 @@ instance (Constructor c, GToJSON a, ConsToJSON a) => GetConName (C1 c a) where
168236--------------------------------------------------------------------------------
169237
170238class TwoElemArray f where
171− twoElemArray :: Options -> f a -> V.Vector Value
239+ twoElemArrayObj :: Options -> f a -> V.Vector Value
240+ twoElemArrayEnc :: Options -> f a -> B.Builder
172241
173242instance (TwoElemArray a, TwoElemArray b) => TwoElemArray (a :+: b) where
174− twoElemArray opts (L1 x) = twoElemArray opts x
175− twoElemArray opts (R1 x) = twoElemArray opts x
176− {-# INLINE twoElemArray #-}
243+ twoElemArrayObj opts (L1 x) = twoElemArrayObj opts x
244+ twoElemArrayObj opts (R1 x) = twoElemArrayObj opts x
245+ {-# INLINE twoElemArrayObj #-}
246+
247+ twoElemArrayEnc opts (L1 x) = twoElemArrayEnc opts x
248+ twoElemArrayEnc opts (R1 x) = twoElemArrayEnc opts x
249+ {-# INLINE twoElemArrayEnc #-}
177250
178251instance ( GToJSON a, ConsToJSON a
179252 , Constructor c ) => TwoElemArray (C1 c a) where
180− twoElemArray opts x = V.create $ do
253+ twoElemArrayObj opts x = V.create $ do
181254 mv <- VM.unsafeNew 2
182255 VM.unsafeWrite mv 0 $ String $ pack $ constructorTagModifier opts
183256 $ conName (undefined :: t c a p)
184257 VM.unsafeWrite mv 1 $ gToJSON opts x
185258 return mv
186− {-# INLINE twoElemArray #-}
259+ {-# INLINE twoElemArrayObj #-}
260+
261+ twoElemArrayEnc opts x =
262+ B.char7 '[' <>
263+ toEncoding (constructorTagModifier opts (conName (undefined :: t c a p))) <>
264+ B.char7 ',' <>
265+ gToEncoding opts x <>
266+ B.char7 ']'
267+ {-# INLINE twoElemArrayEnc #-}
187268
188269--------------------------------------------------------------------------------
189270
190271class ConsToJSON f where
191− consToJSON :: Options -> f a -> Value
272+ consToJSON :: Options -> f a -> Value
273+ consToEncoding :: Options -> f a -> B.Builder
192274
193275class ConsToJSON' f isRecord where
194− consToJSON' :: Options -> f a -> Tagged isRecord Value
276+ consToJSON' :: Options -> f a -> Tagged isRecord Value
277+ consToEncoding' :: Options -> f a -> Tagged isRecord B.Builder
195278
196279instance ( IsRecord f isRecord
197280 , ConsToJSON' f isRecord ) => ConsToJSON f where
@@ -199,40 +282,73 @@ instance ( IsRecord f isRecord
199282 . consToJSON' opts
200283 {-# INLINE consToJSON #-}
201284
202−instance (RecordToPairs f) => ConsToJSON' f True where
285+ consToEncoding opts = (unTagged :: Tagged isRecord B.Builder -> B.Builder)
286+ . consToEncoding' opts
287+ {-# INLINE consToEncoding #-}
288+
289+instance (RecordTo f) => ConsToJSON' f True where
203290 consToJSON' opts = Tagged . object . toList . recordToPairs opts
204291 {-# INLINE consToJSON' #-}
205292
293+ consToEncoding' opts x = Tagged $
294+ B.char7 '{' <>
295+ recordToEncoding opts x <>
296+ B.char7 '}'
297+ {-# INLINE consToEncoding' #-}
298+
206299instance GToJSON f => ConsToJSON' f False where
207300 consToJSON' opts = Tagged . gToJSON opts
208301 {-# INLINE consToJSON' #-}
302+ consToEncoding' opts = Tagged . gToEncoding opts
303+ {-# INLINE consToEncoding' #-}
209304
210305--------------------------------------------------------------------------------
211306
212−class RecordToPairs f where
213− recordToPairs :: Options -> f a -> DList Pair
307+class RecordTo f where
308+ recordToPairs :: Options -> f a -> DList Pair
309+ recordToEncoding :: Options -> f a -> B.Builder
214310
215−instance (RecordToPairs a, RecordToPairs b) => RecordToPairs (a :*: b) where
216− recordToPairs opts (a :*: b) = recordToPairs opts a `mappend`
311+instance (RecordTo a, RecordTo b) => RecordTo (a :*: b) where
312+ recordToPairs opts (a :*: b) = recordToPairs opts a <>
217313 recordToPairs opts b
218314 {-# INLINE recordToPairs #-}
219315
220−instance (Selector s, GToJSON a) => RecordToPairs (S1 s a) where
316+ recordToEncoding opts (a :*: b) = recordToEncoding opts a <>
317+ B.char7 ',' <>
318+ recordToEncoding opts b
319+ {-# INLINE recordToEncoding #-}
320+
321+instance (Selector s, GToJSON a) => RecordTo (S1 s a) where
221322 recordToPairs = fieldToPair
222323 {-# INLINE recordToPairs #-}
223324
224−instance (Selector s, ToJSON a) => RecordToPairs (S1 s (K1 i (Maybe a))) where
325+ recordToEncoding = fieldToEncoding
326+ {-# INLINE recordToEncoding #-}
327+
328+instance (Selector s, ToJSON a) => RecordTo (S1 s (K1 i (Maybe a))) where
225329 recordToPairs opts (M1 k1) | omitNothingFields opts
226330 , K1 Nothing <- k1 = empty
227331 recordToPairs opts m1 = fieldToPair opts m1
228332 {-# INLINE recordToPairs #-}
229333
334+ recordToEncoding opts (M1 k1) | omitNothingFields opts
335+ , K1 Nothing <- k1 = mempty
336+ recordToEncoding opts m1 = fieldToEncoding opts m1
337+ {-# INLINE recordToEncoding #-}
338+
230339fieldToPair :: (Selector s, GToJSON a) => Options -> S1 s a p -> DList Pair
231340fieldToPair opts m1 = pure ( pack $ fieldLabelModifier opts $ selName m1
232341 , gToJSON opts (unM1 m1)
233342 )
234343{-# INLINE fieldToPair #-}
235344
345+fieldToEncoding :: (Selector s, GToJSON a) => Options -> S1 s a p -> B.Builder
346+fieldToEncoding opts m1 =
347+ toEncoding (fieldLabelModifier opts $ selName m1) <>
348+ B.char7 ':' <>
349+ gToEncoding opts (unM1 m1)
350+{-# INLINE fieldToEncoding #-}
351+
236352--------------------------------------------------------------------------------
237353
238354class WriteProduct f where
@@ -242,6 +358,7 @@ class WriteProduct f where
242358 -> Int -- ^ length
243359 -> f a
244360 -> ST s ()
361+ encodeProduct :: Options -> f a -> B.Builder
245362
246363instance ( WriteProduct a
247364 , WriteProduct b ) => WriteProduct (a :*: b) where
@@ -254,28 +371,50 @@ instance ( WriteProduct a
254371 ixR = ix + lenL
255372 {-# INLINE writeProduct #-}
256373
374+ encodeProduct opts (a :*: b) = encodeProduct opts a <>
375+ B.char7 ',' <>
376+ encodeProduct opts b
377+ {-# INLINE encodeProduct #-}
378+
257379instance (GToJSON a) => WriteProduct a where
258380 writeProduct opts mv ix _ = VM.unsafeWrite mv ix . gToJSON opts
259381 {-# INLINE writeProduct #-}
260382
383+ encodeProduct opts = gToEncoding opts
384+ {-# INLINE encodeProduct #-}
385+
261386--------------------------------------------------------------------------------
262387
263388class ObjectWithSingleField f where
264− objectWithSingleField :: Options -> f a -> Object
389+ objectWithSingleFieldObj :: Options -> f a -> Object
390+ objectWithSingleFieldEnc :: Options -> f a -> B.Builder
265391
266392instance ( ObjectWithSingleField a
267393 , ObjectWithSingleField b ) => ObjectWithSingleField (a :+: b) where
268− objectWithSingleField opts (L1 x) = objectWithSingleField opts x
269− objectWithSingleField opts (R1 x) = objectWithSingleField opts x
270− {-# INLINE objectWithSingleField #-}
394+ objectWithSingleFieldObj opts (L1 x) = objectWithSingleFieldObj opts x
395+ objectWithSingleFieldObj opts (R1 x) = objectWithSingleFieldObj opts x
396+ {-# INLINE objectWithSingleFieldObj #-}
397+
398+ objectWithSingleFieldEnc opts (L1 x) = objectWithSingleFieldEnc opts x
399+ objectWithSingleFieldEnc opts (R1 x) = objectWithSingleFieldEnc opts x
400+ {-# INLINE objectWithSingleFieldEnc #-}
271401
272402instance ( GToJSON a, ConsToJSON a
273403 , Constructor c ) => ObjectWithSingleField (C1 c a) where
274− objectWithSingleField opts = H.singleton typ . gToJSON opts
404+ objectWithSingleFieldObj opts = H.singleton typ . gToJSON opts
275405 where
276406 typ = pack $ constructorTagModifier opts $
277407 conName (undefined :: t c a p)
278− {-# INLINE objectWithSingleField #-}
408+ {-# INLINE objectWithSingleFieldObj #-}
409+
410+ objectWithSingleFieldEnc opts v =
411+ B.char7 '{' <>
412+ toEncoding (constructorTagModifier opts
413+ (conName (undefined :: t c a p))) <>
414+ B.char7 ':' <>
415+ gToEncoding opts v <>
416+ B.char7 '}'
417+ {-# INLINE objectWithSingleFieldEnc #-}
279418
280419--------------------------------------------------------------------------------
281420-- Generic parseJSON
Data/Aeson/Types/Instances.hs+338−15
@@ -51,14 +51,18 @@ module Data.Aeson.Types.Instances
5151 ) where
5252
5353import Control.Applicative ((<$>), (<*>), (<|>), pure, empty)
54+import qualified Data.Aeson.Encode.ByteString as E
55+import qualified Data.ByteString.Builder as B
56+import qualified Data.ByteString.Builder.Prim as BP
5457import Data.Aeson.Functions
58+import Data.Monoid ((<>), mempty)
5559import Data.Aeson.Types.Class
5660import Data.Aeson.Types.Internal
5761import Data.Scientific (Scientific)
5862import qualified Data.Scientific as Scientific (coefficient, base10Exponent, fromFloatDigits, toRealFloat)
5963import Data.Attoparsec.Number (Number(..))
6064import Data.Fixed
61−import Data.Foldable (toList)
65+import Data.Foldable (Foldable, toList)
6266import Data.Functor.Identity (Identity(..))
6367import Data.Hashable (Hashable(..))
6468import Data.Int (Int8, Int16, Int32, Int64)
@@ -72,6 +76,7 @@ import Data.Traversable (traverse)
7276import Data.Vector (Vector)
7377import Data.Word (Word, Word8, Word16, Word32, Word64)
7478import Foreign.Storable (Storable)
79+import Prelude hiding (foldr)
7580import qualified Data.HashMap.Strict as H
7681import qualified Data.HashSet as HashSet
7782import qualified Data.IntMap as IntMap
@@ -88,6 +93,7 @@ import qualified Data.Vector.Primitive as VP
8893import qualified Data.Vector.Storable as VS
8994import qualified Data.Vector.Unboxed as VU
9095import qualified Data.Vector.Mutable as VM ( unsafeNew, unsafeWrite )
96+import qualified Prelude as P
9197
9298#if MIN_VERSION_time(1,5,0)
9399import Data.Time.Format (defaultTimeLocale, dateTimeFmt)
@@ -99,6 +105,9 @@ instance (ToJSON a) => ToJSON (Identity a) where
99105 toJSON (Identity a) = toJSON a
100106 {-# INLINE toJSON #-}
101107
108+ toEncoding (Identity a) = toEncoding a
109+ {-# INLINE toEncoding #-}
110+
102111instance (FromJSON a) => FromJSON (Identity a) where
103112 parseJSON a = Identity <$> parseJSON a
104113 {-# INLINE parseJSON #-}
@@ -108,6 +117,9 @@ instance (ToJSON a) => ToJSON (Maybe a) where
108117 toJSON Nothing = Null
109118 {-# INLINE toJSON #-}
110119
120+ toEncoding (Just a) = toEncoding a
121+ toEncoding Nothing = E.null_
122+
111123instance (FromJSON a) => FromJSON (Maybe a) where
112124 parseJSON Null = pure Nothing
113125 parseJSON a = Just <$> parseJSON a
@@ -118,6 +130,11 @@ instance (ToJSON a, ToJSON b) => ToJSON (Either a b) where
118130 toJSON (Right b) = object [right .= b]
119131 {-# INLINE toJSON #-}
120132
133+ toEncoding (Left a) =
134+ B.shortByteString "{\"left\":" <> toEncoding a <> B.char7 '}'
135+ toEncoding (Right a) =
136+ B.shortByteString "{\"right\":" <> toEncoding a <> B.char7 '}'
137+
121138instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
122139 parseJSON (Object (H.toList -> [(key, value)]))
123140 | key == left = Left <$> parseJSON value
@@ -136,6 +153,8 @@ instance ToJSON Bool where
136153 toJSON = Bool
137154 {-# INLINE toJSON #-}
138155
156+ toEncoding v = E.bool v
157+
139158instance FromJSON Bool where
140159 parseJSON = withBool "Bool" pure
141160 {-# INLINE parseJSON #-}
@@ -144,6 +163,8 @@ instance ToJSON () where
144163 toJSON _ = emptyArray
145164 {-# INLINE toJSON #-}
146165
166+ toEncoding _ = E.emptyArray_
167+
147168instance FromJSON () where
148169 parseJSON = withArray "()" $ \v ->
149170 if V.null v
@@ -155,6 +176,8 @@ instance ToJSON [Char] where
155176 toJSON = String . T.pack
156177 {-# INLINE toJSON #-}
157178
179+ toEncoding = E.text . T.pack
180+
158181instance FromJSON [Char] where
159182 parseJSON = withText "String" $ pure . T.unpack
160183 {-# INLINE parseJSON #-}
@@ -163,6 +186,8 @@ instance ToJSON Char where
163186 toJSON = String . T.singleton
164187 {-# INLINE toJSON #-}
165188
189+ toEncoding = E.text . T.singleton
190+
166191instance FromJSON Char where
167192 parseJSON = withText "Char" $ \t ->
168193 if T.compareLength t 1 == EQ
@@ -174,6 +199,9 @@ instance ToJSON Scientific where
174199 toJSON = Number
175200 {-# INLINE toJSON #-}
176201
202+ toEncoding v = E.number v
203+ {-# INLINE toEncoding #-}
204+
177205instance FromJSON Scientific where
178206 parseJSON = withScientific "Scientific" pure
179207 {-# INLINE parseJSON #-}
@@ -182,6 +210,9 @@ instance ToJSON Double where
182210 toJSON = realFloatToJSON
183211 {-# INLINE toJSON #-}
184212
213+ toEncoding = realFloatToEncoding
214+ {-# INLINE toEncoding #-}
215+
185216instance FromJSON Double where
186217 parseJSON = parseRealFloat "Double"
187218 {-# INLINE parseJSON #-}
@@ -191,6 +222,10 @@ instance ToJSON Number where
191222 toJSON (I i) = toJSON i
192223 {-# INLINE toJSON #-}
193224
225+ toEncoding (D d) = toEncoding d
226+ toEncoding (I i) = toEncoding i
227+ {-# INLINE toEncoding #-}
228+
194229instance FromJSON Number where
195230 parseJSON (Number s) = pure $ scientificToNumber s
196231 parseJSON Null = pure (D (0/0))
@@ -201,6 +236,9 @@ instance ToJSON Float where
201236 toJSON = realFloatToJSON
202237 {-# INLINE toJSON #-}
203238
239+ toEncoding = realFloatToEncoding
240+ {-# INLINE toEncoding #-}
241+
204242instance FromJSON Float where
205243 parseJSON = parseRealFloat "Float"
206244 {-# INLINE parseJSON #-}
@@ -209,7 +247,11 @@ instance ToJSON (Ratio Integer) where
209247 toJSON r = object [ "numerator" .= numerator r
210248 , "denominator" .= denominator r
211249 ]
212− {-# INLINE toJSON #-}
250+
251+ toEncoding r =
252+ B.shortByteString "{\"numerator\":" <> toEncoding (numerator r) <>
253+ B.shortByteString ",\"denominator\":" <> toEncoding (denominator r) <>
254+ B.char7 '}'
213255
214256instance FromJSON (Ratio Integer) where
215257 parseJSON = withObject "Rational" $ \obj ->
@@ -221,6 +263,8 @@ instance HasResolution a => ToJSON (Fixed a) where
221263 toJSON = Number . realToFrac
222264 {-# INLINE toJSON #-}
223265
266+ toEncoding = E.number . realToFrac
267+
224268-- | /WARNING:/ Only parse fixed-precision numbers from trusted input
225269-- since an attacker could easily fill up the memory of the target
226270-- system by specifying a scientific number with a big exponent like
@@ -233,6 +277,9 @@ instance ToJSON Int where
233277 toJSON = Number . fromIntegral
234278 {-# INLINE toJSON #-}
235279
280+ toEncoding = B.intDec
281+ {-# INLINE toEncoding #-}
282+
236283instance FromJSON Int where
237284 parseJSON = parseIntegral "Int"
238285 {-# INLINE parseJSON #-}
@@ -241,6 +288,8 @@ instance ToJSON Integer where
241288 toJSON = Number . fromInteger
242289 {-# INLINE toJSON #-}
243290
291+ toEncoding = B.integerDec
292+
244293-- | /WARNING:/ Only parse Integers from trusted input since an
245294-- attacker could easily fill up the memory of the target system by
246295-- specifying a scientific number with a big exponent like
@@ -253,6 +302,8 @@ instance ToJSON Int8 where
253302 toJSON = Number . fromIntegral
254303 {-# INLINE toJSON #-}
255304
305+ toEncoding = B.int8Dec
306+
256307instance FromJSON Int8 where
257308 parseJSON = parseIntegral "Int8"
258309 {-# INLINE parseJSON #-}
@@ -261,6 +312,8 @@ instance ToJSON Int16 where
261312 toJSON = Number . fromIntegral
262313 {-# INLINE toJSON #-}
263314
315+ toEncoding = B.int16Dec
316+
264317instance FromJSON Int16 where
265318 parseJSON = parseIntegral "Int16"
266319 {-# INLINE parseJSON #-}
@@ -269,6 +322,8 @@ instance ToJSON Int32 where
269322 toJSON = Number . fromIntegral
270323 {-# INLINE toJSON #-}
271324
325+ toEncoding = B.int32Dec
326+
272327instance FromJSON Int32 where
273328 parseJSON = parseIntegral "Int32"
274329 {-# INLINE parseJSON #-}
@@ -277,6 +332,8 @@ instance ToJSON Int64 where
277332 toJSON = Number . fromIntegral
278333 {-# INLINE toJSON #-}
279334
335+ toEncoding = B.int64Dec
336+
280337instance FromJSON Int64 where
281338 parseJSON = parseIntegral "Int64"
282339 {-# INLINE parseJSON #-}
@@ -285,6 +342,8 @@ instance ToJSON Word where
285342 toJSON = Number . fromIntegral
286343 {-# INLINE toJSON #-}
287344
345+ toEncoding = B.wordDec
346+
288347instance FromJSON Word where
289348 parseJSON = parseIntegral "Word"
290349 {-# INLINE parseJSON #-}
@@ -293,6 +352,8 @@ instance ToJSON Word8 where
293352 toJSON = Number . fromIntegral
294353 {-# INLINE toJSON #-}
295354
355+ toEncoding = B.word8Dec
356+
296357instance FromJSON Word8 where
297358 parseJSON = parseIntegral "Word8"
298359 {-# INLINE parseJSON #-}
@@ -301,6 +362,8 @@ instance ToJSON Word16 where
301362 toJSON = Number . fromIntegral
302363 {-# INLINE toJSON #-}
303364
365+ toEncoding = B.word16Dec
366+
304367instance FromJSON Word16 where
305368 parseJSON = parseIntegral "Word16"
306369 {-# INLINE parseJSON #-}
@@ -309,6 +372,8 @@ instance ToJSON Word32 where
309372 toJSON = Number . fromIntegral
310373 {-# INLINE toJSON #-}
311374
375+ toEncoding = B.word32Dec
376+
312377instance FromJSON Word32 where
313378 parseJSON = parseIntegral "Word32"
314379 {-# INLINE parseJSON #-}
@@ -317,6 +382,8 @@ instance ToJSON Word64 where
317382 toJSON = Number . fromIntegral
318383 {-# INLINE toJSON #-}
319384
385+ toEncoding = B.word64Dec
386+
320387instance FromJSON Word64 where
321388 parseJSON = parseIntegral "Word64"
322389 {-# INLINE parseJSON #-}
@@ -325,6 +392,8 @@ instance ToJSON Text where
325392 toJSON = String
326393 {-# INLINE toJSON #-}
327394
395+ toEncoding = E.text
396+
328397instance FromJSON Text where
329398 parseJSON = withText "Text" pure
330399 {-# INLINE parseJSON #-}
@@ -333,6 +402,11 @@ instance ToJSON LT.Text where
333402 toJSON = String . LT.toStrict
334403 {-# INLINE toJSON #-}
335404
405+ toEncoding t =
406+ B.char7 '"' <>
407+ LT.foldrChunks (\x xs -> E.unquoted x <> xs) mempty t <>
408+ B.char7 '"'
409+
336410instance FromJSON LT.Text where
337411 parseJSON = withText "Lazy Text" $ pure . LT.fromStrict
338412 {-# INLINE parseJSON #-}
@@ -341,14 +415,20 @@ instance (ToJSON a) => ToJSON [a] where
341415 toJSON = Array . V.fromList . map toJSON
342416 {-# INLINE toJSON #-}
343417
418+ toEncoding [] = E.emptyArray_
419+ toEncoding (x:xs) = B.char7 '[' <> toEncoding x <> commas xs <> B.char7 ']'
420+ where commas = P.foldr (\v vs -> B.char7 ',' <> toEncoding v <> vs) mempty
421+
344422instance (FromJSON a) => FromJSON [a] where
345423 parseJSON = withArray "[a]" $ mapM parseJSON . V.toList
346424 {-# INLINE parseJSON #-}
347425
348−instance (ToJSON a) => ToJSON (Seq.Seq a) where
426+instance (Foldable t, ToJSON a) => ToJSON (t a) where
349427 toJSON = toJSON . toList
350428 {-# INLINE toJSON #-}
351429
430+ toEncoding = E.foldable
431+
352432instance (FromJSON a) => FromJSON (Seq.Seq a) where
353433 parseJSON = withArray "Seq a" $ traverse parseJSON . Seq.fromList . V.toList
354434 {-# INLINE parseJSON #-}
@@ -357,6 +437,16 @@ instance (ToJSON a) => ToJSON (Vector a) where
357437 toJSON = Array . V.map toJSON
358438 {-# INLINE toJSON #-}
359439
440+ toEncoding = encodeVector
441+
442+encodeVector :: (ToJSON a, VG.Vector v a) => v a -> B.Builder
443+encodeVector xs
444+ | VG.null xs = E.emptyArray_
445+ | otherwise = B.char7 '[' <> toEncoding (VG.unsafeHead xs) <>
446+ VG.foldr go mempty (VG.unsafeTail xs) <>
447+ B.char7 ']'
448+ where go v b = B.char7 ',' <> toEncoding v <> b
449+
360450instance (FromJSON a) => FromJSON (Vector a) where
361451 parseJSON = withArray "Vector a" $ V.mapM parseJSON
362452 {-# INLINE parseJSON #-}
@@ -372,18 +462,24 @@ vectorParseJSON s = withArray s $ fmap V.convert . V.mapM parseJSON
372462instance (Storable a, ToJSON a) => ToJSON (VS.Vector a) where
373463 toJSON = vectorToJSON
374464
465+ toEncoding = encodeVector
466+
375467instance (Storable a, FromJSON a) => FromJSON (VS.Vector a) where
376468 parseJSON = vectorParseJSON "Data.Vector.Storable.Vector a"
377469
378470instance (VP.Prim a, ToJSON a) => ToJSON (VP.Vector a) where
379471 toJSON = vectorToJSON
380472
473+ toEncoding = encodeVector
474+
381475instance (VP.Prim a, FromJSON a) => FromJSON (VP.Vector a) where
382476 parseJSON = vectorParseJSON "Data.Vector.Primitive.Vector a"
383477
384478instance (VG.Vector VU.Vector a, ToJSON a) => ToJSON (VU.Vector a) where
385479 toJSON = vectorToJSON
386480
481+ toEncoding = encodeVector
482+
387483instance (VG.Vector VU.Vector a, FromJSON a) => FromJSON (VU.Vector a) where
388484 parseJSON = vectorParseJSON "Data.Vector.Unboxed.Vector a"
389485
@@ -391,6 +487,8 @@ instance (ToJSON a) => ToJSON (Set.Set a) where
391487 toJSON = toJSON . Set.toList
392488 {-# INLINE toJSON #-}
393489
490+ toEncoding = encodeSet Set.minView Set.foldr
491+
394492instance (Ord a, FromJSON a) => FromJSON (Set.Set a) where
395493 parseJSON = fmap Set.fromList . parseJSON
396494 {-# INLINE parseJSON #-}
@@ -399,6 +497,8 @@ instance (ToJSON a) => ToJSON (HashSet.HashSet a) where
399497 toJSON = toJSON . HashSet.toList
400498 {-# INLINE toJSON #-}
401499
500+ toEncoding = E.foldable
501+
402502instance (Eq a, Hashable a, FromJSON a) => FromJSON (HashSet.HashSet a) where
403503 parseJSON = fmap HashSet.fromList . parseJSON
404504 {-# INLINE parseJSON #-}
@@ -407,6 +507,20 @@ instance ToJSON IntSet.IntSet where
407507 toJSON = toJSON . IntSet.toList
408508 {-# INLINE toJSON #-}
409509
510+ toEncoding = encodeSet IntSet.minView IntSet.foldr
511+
512+encodeSet :: (ToJSON a) =>
513+ (s -> Maybe (a, s))
514+ -> ((a -> B.Builder -> B.Builder) -> B.Builder -> s -> B.Builder)
515+ -> s -> B.Builder
516+encodeSet minView foldr xs =
517+ case minView xs of
518+ Nothing -> E.emptyArray_
519+ Just (m,ys) -> B.char7 '[' <> toEncoding m <>
520+ foldr go mempty ys <>
521+ B.char7 ']'
522+ where go v b = B.char7 ',' <> toEncoding v <> b
523+
410524instance FromJSON IntSet.IntSet where
411525 parseJSON = fmap IntSet.fromList . parseJSON
412526 {-# INLINE parseJSON #-}
@@ -415,6 +529,8 @@ instance ToJSON a => ToJSON (IntMap.IntMap a) where
415529 toJSON = toJSON . IntMap.toList
416530 {-# INLINE toJSON #-}
417531
532+ toEncoding = toEncoding . IntMap.toList
533+
418534instance FromJSON a => FromJSON (IntMap.IntMap a) where
419535 parseJSON = fmap IntMap.fromList . parseJSON
420536 {-# INLINE parseJSON #-}
@@ -423,6 +539,28 @@ instance (ToJSON v) => ToJSON (M.Map Text v) where
423539 toJSON = Object . M.foldrWithKey (\k -> H.insert k . toJSON) H.empty
424540 {-# INLINE toJSON #-}
425541
542+ toEncoding = encodeMap M.minViewWithKey M.foldrWithKey
543+
544+encodeMap :: (ToJSON k, ToJSON v) =>
545+ (m -> Maybe ((k,v), m))
546+ -> ((k -> v -> B.Builder -> B.Builder) -> B.Builder -> m -> B.Builder)
547+ -> m -> B.Builder
548+encodeMap minViewWithKey foldrWithKey xs =
549+ case minViewWithKey xs of
550+ Nothing -> BP.primBounded (E.ascii2 ('{','}')) ()
551+ Just ((k,v),ys) -> B.char7 '{' <> encodePair k v <>
552+ foldrWithKey go mempty ys <> B.char7 '}'
553+ where go k v b = B.char7 ',' <> encodePair k v <> b
554+
555+encodeWithKey :: (ToJSON k, ToJSON v) =>
556+ ((k -> v -> Series -> Series) -> Series -> m -> Series)
557+ -> m -> B.Builder
558+encodeWithKey foldrWithKey xs = E.series '{' '}' . foldrWithKey go mempty $ xs
559+ where go k v c = Value (encodePair k v) <> c
560+
561+encodePair :: (ToJSON k, ToJSON v) => k -> v -> B.Builder
562+encodePair k v = toEncoding k <> B.char7 ':' <> toEncoding v
563+
426564instance (FromJSON v) => FromJSON (M.Map Text v) where
427565 parseJSON = withObject "Map Text a" $
428566 fmap (H.foldrWithKey M.insert M.empty) . traverse parseJSON
@@ -430,12 +568,16 @@ instance (FromJSON v) => FromJSON (M.Map Text v) where
430568instance (ToJSON v) => ToJSON (M.Map LT.Text v) where
431569 toJSON = Object . mapHashKeyVal LT.toStrict toJSON
432570
571+ toEncoding = encodeMap M.minViewWithKey M.foldrWithKey
572+
433573instance (FromJSON v) => FromJSON (M.Map LT.Text v) where
434574 parseJSON = fmap (hashMapKey LT.fromStrict) . parseJSON
435575
436576instance (ToJSON v) => ToJSON (M.Map String v) where
437577 toJSON = Object . mapHashKeyVal pack toJSON
438578
579+ toEncoding = encodeMap M.minViewWithKey M.foldrWithKey
580+
439581instance (FromJSON v) => FromJSON (M.Map String v) where
440582 parseJSON = fmap (hashMapKey unpack) . parseJSON
441583
@@ -443,24 +585,32 @@ instance (ToJSON v) => ToJSON (H.HashMap Text v) where
443585 toJSON = Object . H.map toJSON
444586 {-# INLINE toJSON #-}
445587
588+ toEncoding = encodeWithKey H.foldrWithKey
589+
446590instance (FromJSON v) => FromJSON (H.HashMap Text v) where
447591 parseJSON = withObject "HashMap Text a" $ traverse parseJSON
448592
449593instance (ToJSON v) => ToJSON (H.HashMap LT.Text v) where
450594 toJSON = Object . mapKeyVal LT.toStrict toJSON
451595
596+ toEncoding = encodeWithKey H.foldrWithKey
597+
452598instance (FromJSON v) => FromJSON (H.HashMap LT.Text v) where
453599 parseJSON = fmap (mapKey LT.fromStrict) . parseJSON
454600
455601instance (ToJSON v) => ToJSON (H.HashMap String v) where
456602 toJSON = Object . mapKeyVal pack toJSON
457603
604+ toEncoding = encodeWithKey H.foldrWithKey
605+
458606instance (FromJSON v) => FromJSON (H.HashMap String v) where
459607 parseJSON = fmap (mapKey unpack) . parseJSON
460608
461609instance (ToJSON v) => ToJSON (Tree.Tree v) where
462610 toJSON (Tree.Node root branches) = toJSON (root,branches)
463611
612+ toEncoding (Tree.Node root branches) = toEncoding (root,branches)
613+
464614instance (FromJSON v) => FromJSON (Tree.Tree v) where
465615 parseJSON j = uncurry Tree.Node <$> parseJSON j
466616
@@ -468,16 +618,22 @@ instance ToJSON Value where
468618 toJSON a = a
469619 {-# INLINE toJSON #-}
470620
621+ toEncoding = E.encodeToBuilder
622+
471623instance FromJSON Value where
472624 parseJSON a = pure a
473625 {-# INLINE parseJSON #-}
474626
475627instance ToJSON DotNetTime where
476− toJSON (DotNetTime t) =
477− String (pack (secs ++ formatMillis t ++ ")/"))
478− where secs = formatTime defaultTimeLocale "/Date(%s" t
628+ toJSON = toJSON . dotNetTime
479629 {-# INLINE toJSON #-}
480630
631+ toEncoding = toEncoding . dotNetTime
632+
633+dotNetTime :: DotNetTime -> String
634+dotNetTime (DotNetTime t) = secs ++ formatMillis t ++ ")/"
635+ where secs = formatTime defaultTimeLocale "/Date(%s" t
636+
481637instance FromJSON DotNetTime where
482638 parseJSON = withText "DotNetTime" $ \t ->
483639 let (s,m) = T.splitAt (T.length t - 5) t
@@ -488,12 +644,16 @@ instance FromJSON DotNetTime where
488644 {-# INLINE parseJSON #-}
489645
490646instance ToJSON ZonedTime where
491− toJSON t = String $ pack $ formatTime defaultTimeLocale format t
492− where
493− format = "%FT%T." ++ formatMillis t ++ tzFormat
494− tzFormat
495− | 0 == timeZoneMinutes (zonedTimeZone t) = "Z"
496− | otherwise = "%z"
647+ toJSON = toJSON . zonedTime
648+
649+ toEncoding = toEncoding . zonedTime
650+
651+zonedTime :: ZonedTime -> String
652+zonedTime t = formatTime defaultTimeLocale format t
653+ where
654+ format = "%FT%T." ++ formatMillis t ++ tzFormat
655+ tzFormat | timeZoneMinutes (zonedTimeZone t) == 0 = "Z"
656+ | otherwise = "%z"
497657
498658formatMillis :: (FormatTime t) => t -> String
499659formatMillis = take 3 . formatSubseconds
@@ -535,11 +695,15 @@ instance FromJSON ZonedTime where
535695 parseJSON v = typeMismatch "ZonedTime" v
536696
537697instance ToJSON UTCTime where
538− toJSON t = String $ pack $ formatTime defaultTimeLocale format t
539− where
540− format = "%FT%T." ++ formatSubseconds t ++ "Z"
698+ toJSON = toJSON . utcTime
541699 {-# INLINE toJSON #-}
542700
701+ toEncoding = toEncoding . utcTime
702+
703+utcTime :: UTCTime -> String
704+utcTime t = formatTime defaultTimeLocale format t
705+ where format = "%FT%T." ++ formatSubseconds t ++ "Z"
706+
543707instance FromJSON UTCTime where
544708 parseJSON = withText "UTCTime" $ \t ->
545709 case parseTime defaultTimeLocale "%FT%T%QZ" (unpack t) of
@@ -555,6 +719,10 @@ instance (ToJSON a, ToJSON b) => ToJSON (a,b) where
555719 return mv
556720 {-# INLINE toJSON #-}
557721
722+ toEncoding (a,b) = B.char7 '[' <>
723+ toEncoding a <> B.char7 ',' <>
724+ toEncoding b <> B.char7 ']'
725+
558726instance (FromJSON a, FromJSON b) => FromJSON (a,b) where
559727 parseJSON = withArray "(a,b)" $ \ab ->
560728 let n = V.length ab
@@ -574,6 +742,11 @@ instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (a,b,c) where
574742 return mv
575743 {-# INLINE toJSON #-}
576744
745+ toEncoding (a,b,c) = B.char7 '[' <>
746+ toEncoding a <> B.char7 ',' <>
747+ toEncoding b <> B.char7 ',' <>
748+ toEncoding c <> B.char7 ']'
749+
577750instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (a,b,c) where
578751 parseJSON = withArray "(a,b,c)" $ \abc ->
579752 let n = V.length abc
@@ -595,6 +768,12 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d) => ToJSON (a,b,c,d) where
595768 return mv
596769 {-# INLINE toJSON #-}
597770
771+ toEncoding (a,b,c,d) = B.char7 '[' <>
772+ toEncoding a <> B.char7 ',' <>
773+ toEncoding b <> B.char7 ',' <>
774+ toEncoding c <> B.char7 ',' <>
775+ toEncoding d <> B.char7 ']'
776+
598777instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d) =>
599778 FromJSON (a,b,c,d) where
600779 parseJSON = withArray "(a,b,c,d)" $ \abcd ->
@@ -620,6 +799,13 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e) =>
620799 return mv
621800 {-# INLINE toJSON #-}
622801
802+ toEncoding (a,b,c,d,e) = B.char7 '[' <>
803+ toEncoding a <> B.char7 ',' <>
804+ toEncoding b <> B.char7 ',' <>
805+ toEncoding c <> B.char7 ',' <>
806+ toEncoding d <> B.char7 ',' <>
807+ toEncoding e <> B.char7 ']'
808+
623809instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e) =>
624810 FromJSON (a,b,c,d,e) where
625811 parseJSON = withArray "(a,b,c,d,e)" $ \abcde ->
@@ -647,6 +833,14 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f) =>
647833 return mv
648834 {-# INLINE toJSON #-}
649835
836+ toEncoding (a,b,c,d,e,f) = B.char7 '[' <>
837+ toEncoding a <> B.char7 ',' <>
838+ toEncoding b <> B.char7 ',' <>
839+ toEncoding c <> B.char7 ',' <>
840+ toEncoding d <> B.char7 ',' <>
841+ toEncoding e <> B.char7 ',' <>
842+ toEncoding f <> B.char7 ']'
843+
650844instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
651845 FromJSON f) => FromJSON (a,b,c,d,e,f) where
652846 parseJSON = withArray "(a,b,c,d,e,f)" $ \abcdef ->
@@ -676,6 +870,15 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
676870 return mv
677871 {-# INLINE toJSON #-}
678872
873+ toEncoding (a,b,c,d,e,f,g) = B.char7 '[' <>
874+ toEncoding a <> B.char7 ',' <>
875+ toEncoding b <> B.char7 ',' <>
876+ toEncoding c <> B.char7 ',' <>
877+ toEncoding d <> B.char7 ',' <>
878+ toEncoding e <> B.char7 ',' <>
879+ toEncoding f <> B.char7 ',' <>
880+ toEncoding g <> B.char7 ']'
881+
679882instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
680883 FromJSON f, FromJSON g) => FromJSON (a,b,c,d,e,f,g) where
681884 parseJSON = withArray "(a,b,c,d,e,f,g)" $ \abcdefg ->
@@ -707,6 +910,16 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
707910 return mv
708911 {-# INLINE toJSON #-}
709912
913+ toEncoding (a,b,c,d,e,f,g,h) = B.char7 '[' <>
914+ toEncoding a <> B.char7 ',' <>
915+ toEncoding b <> B.char7 ',' <>
916+ toEncoding c <> B.char7 ',' <>
917+ toEncoding d <> B.char7 ',' <>
918+ toEncoding e <> B.char7 ',' <>
919+ toEncoding f <> B.char7 ',' <>
920+ toEncoding g <> B.char7 ',' <>
921+ toEncoding h <> B.char7 ']'
922+
710923instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
711924 FromJSON f, FromJSON g, FromJSON h) =>
712925 FromJSON (a,b,c,d,e,f,g,h) where
@@ -742,6 +955,17 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
742955 return mv
743956 {-# INLINE toJSON #-}
744957
958+ toEncoding (a,b,c,d,e,f,g,h,i) = B.char7 '[' <>
959+ toEncoding a <> B.char7 ',' <>
960+ toEncoding b <> B.char7 ',' <>
961+ toEncoding c <> B.char7 ',' <>
962+ toEncoding d <> B.char7 ',' <>
963+ toEncoding e <> B.char7 ',' <>
964+ toEncoding f <> B.char7 ',' <>
965+ toEncoding g <> B.char7 ',' <>
966+ toEncoding h <> B.char7 ',' <>
967+ toEncoding i <> B.char7 ']'
968+
745969instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
746970 FromJSON f, FromJSON g, FromJSON h, FromJSON i) =>
747971 FromJSON (a,b,c,d,e,f,g,h,i) where
@@ -780,6 +1004,18 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
7801004 return mv
7811005 {-# INLINE toJSON #-}
7821006
1007+ toEncoding (a,b,c,d,e,f,g,h,i,j) = B.char7 '[' <>
1008+ toEncoding a <> B.char7 ',' <>
1009+ toEncoding b <> B.char7 ',' <>
1010+ toEncoding c <> B.char7 ',' <>
1011+ toEncoding d <> B.char7 ',' <>
1012+ toEncoding e <> B.char7 ',' <>
1013+ toEncoding f <> B.char7 ',' <>
1014+ toEncoding g <> B.char7 ',' <>
1015+ toEncoding h <> B.char7 ',' <>
1016+ toEncoding i <> B.char7 ',' <>
1017+ toEncoding j <> B.char7 ']'
1018+
7831019instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
7841020 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j) =>
7851021 FromJSON (a,b,c,d,e,f,g,h,i,j) where
@@ -820,6 +1056,19 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
8201056 return mv
8211057 {-# INLINE toJSON #-}
8221058
1059+ toEncoding (a,b,c,d,e,f,g,h,i,j,k) = B.char7 '[' <>
1060+ toEncoding a <> B.char7 ',' <>
1061+ toEncoding b <> B.char7 ',' <>
1062+ toEncoding c <> B.char7 ',' <>
1063+ toEncoding d <> B.char7 ',' <>
1064+ toEncoding e <> B.char7 ',' <>
1065+ toEncoding f <> B.char7 ',' <>
1066+ toEncoding g <> B.char7 ',' <>
1067+ toEncoding h <> B.char7 ',' <>
1068+ toEncoding i <> B.char7 ',' <>
1069+ toEncoding j <> B.char7 ',' <>
1070+ toEncoding k <> B.char7 ']'
1071+
8231072instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
8241073 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j,
8251074 FromJSON k) =>
@@ -863,6 +1112,20 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
8631112 return mv
8641113 {-# INLINE toJSON #-}
8651114
1115+ toEncoding (a,b,c,d,e,f,g,h,i,j,k,l) = B.char7 '[' <>
1116+ toEncoding a <> B.char7 ',' <>
1117+ toEncoding b <> B.char7 ',' <>
1118+ toEncoding c <> B.char7 ',' <>
1119+ toEncoding d <> B.char7 ',' <>
1120+ toEncoding e <> B.char7 ',' <>
1121+ toEncoding f <> B.char7 ',' <>
1122+ toEncoding g <> B.char7 ',' <>
1123+ toEncoding h <> B.char7 ',' <>
1124+ toEncoding i <> B.char7 ',' <>
1125+ toEncoding j <> B.char7 ',' <>
1126+ toEncoding k <> B.char7 ',' <>
1127+ toEncoding l <> B.char7 ']'
1128+
8661129instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
8671130 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j,
8681131 FromJSON k, FromJSON l) =>
@@ -909,6 +1172,21 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
9091172 return mv
9101173 {-# INLINE toJSON #-}
9111174
1175+ toEncoding (a,b,c,d,e,f,g,h,i,j,k,l,m) = B.char7 '[' <>
1176+ toEncoding a <> B.char7 ',' <>
1177+ toEncoding b <> B.char7 ',' <>
1178+ toEncoding c <> B.char7 ',' <>
1179+ toEncoding d <> B.char7 ',' <>
1180+ toEncoding e <> B.char7 ',' <>
1181+ toEncoding f <> B.char7 ',' <>
1182+ toEncoding g <> B.char7 ',' <>
1183+ toEncoding h <> B.char7 ',' <>
1184+ toEncoding i <> B.char7 ',' <>
1185+ toEncoding j <> B.char7 ',' <>
1186+ toEncoding k <> B.char7 ',' <>
1187+ toEncoding l <> B.char7 ',' <>
1188+ toEncoding m <> B.char7 ']'
1189+
9121190instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
9131191 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j,
9141192 FromJSON k, FromJSON l, FromJSON m) =>
@@ -957,6 +1235,22 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
9571235 return mv
9581236 {-# INLINE toJSON #-}
9591237
1238+ toEncoding (a,b,c,d,e,f,g,h,i,j,k,l,m,n) = B.char7 '[' <>
1239+ toEncoding a <> B.char7 ',' <>
1240+ toEncoding b <> B.char7 ',' <>
1241+ toEncoding c <> B.char7 ',' <>
1242+ toEncoding d <> B.char7 ',' <>
1243+ toEncoding e <> B.char7 ',' <>
1244+ toEncoding f <> B.char7 ',' <>
1245+ toEncoding g <> B.char7 ',' <>
1246+ toEncoding h <> B.char7 ',' <>
1247+ toEncoding i <> B.char7 ',' <>
1248+ toEncoding j <> B.char7 ',' <>
1249+ toEncoding k <> B.char7 ',' <>
1250+ toEncoding l <> B.char7 ',' <>
1251+ toEncoding m <> B.char7 ',' <>
1252+ toEncoding n <> B.char7 ']'
1253+
9601254instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
9611255 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j,
9621256 FromJSON k, FromJSON l, FromJSON m, FromJSON n) =>
@@ -1007,6 +1301,23 @@ instance (ToJSON a, ToJSON b, ToJSON c, ToJSON d, ToJSON e, ToJSON f,
10071301 return mv
10081302 {-# INLINE toJSON #-}
10091303
1304+ toEncoding (a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) = B.char7 '[' <>
1305+ toEncoding a <> B.char7 ',' <>
1306+ toEncoding b <> B.char7 ',' <>
1307+ toEncoding c <> B.char7 ',' <>
1308+ toEncoding d <> B.char7 ',' <>
1309+ toEncoding e <> B.char7 ',' <>
1310+ toEncoding f <> B.char7 ',' <>
1311+ toEncoding g <> B.char7 ',' <>
1312+ toEncoding h <> B.char7 ',' <>
1313+ toEncoding i <> B.char7 ',' <>
1314+ toEncoding j <> B.char7 ',' <>
1315+ toEncoding k <> B.char7 ',' <>
1316+ toEncoding l <> B.char7 ',' <>
1317+ toEncoding m <> B.char7 ',' <>
1318+ toEncoding n <> B.char7 ',' <>
1319+ toEncoding o <> B.char7 ']'
1320+
10101321instance (FromJSON a, FromJSON b, FromJSON c, FromJSON d, FromJSON e,
10111322 FromJSON f, FromJSON g, FromJSON h, FromJSON i, FromJSON j,
10121323 FromJSON k, FromJSON l, FromJSON m, FromJSON n, FromJSON o) =>
@@ -1038,6 +1349,8 @@ instance ToJSON a => ToJSON (Dual a) where
10381349 toJSON = toJSON . getDual
10391350 {-# INLINE toJSON #-}
10401351
1352+ toEncoding = toEncoding . getDual
1353+
10411354instance FromJSON a => FromJSON (Dual a) where
10421355 parseJSON = fmap Dual . parseJSON
10431356 {-# INLINE parseJSON #-}
@@ -1046,6 +1359,8 @@ instance ToJSON a => ToJSON (First a) where
10461359 toJSON = toJSON . getFirst
10471360 {-# INLINE toJSON #-}
10481361
1362+ toEncoding = toEncoding . getFirst
1363+
10491364instance FromJSON a => FromJSON (First a) where
10501365 parseJSON = fmap First . parseJSON
10511366 {-# INLINE parseJSON #-}
@@ -1054,6 +1369,8 @@ instance ToJSON a => ToJSON (Last a) where
10541369 toJSON = toJSON . getLast
10551370 {-# INLINE toJSON #-}
10561371
1372+ toEncoding = toEncoding . getLast
1373+
10571374instance FromJSON a => FromJSON (Last a) where
10581375 parseJSON = fmap Last . parseJSON
10591376 {-# INLINE parseJSON #-}
@@ -1176,6 +1493,12 @@ realFloatToJSON d
11761493 | otherwise = Number $ Scientific.fromFloatDigits d
11771494{-# INLINE realFloatToJSON #-}
11781495
1496+realFloatToEncoding :: RealFloat a => a -> Encoding
1497+realFloatToEncoding d
1498+ | isNaN d || isInfinite d = E.null_
1499+ | otherwise = toEncoding (Scientific.fromFloatDigits d)
1500+{-# INLINE realFloatToEncoding #-}
1501+
11791502scientificToNumber :: Scientific -> Number
11801503scientificToNumber s
11811504 | e < 0 = D $ Scientific.toRealFloat s
Data/Aeson/Types/Internal.hs+20−2
@@ -15,6 +15,8 @@ module Data.Aeson.Types.Internal
1515 (
1616 -- * Core JSON types
1717 Value(..)
18+ , Encoding
19+ , Series(..)
1820 , Array
1921 , emptyArray, isEmptyArray
2022 , Pair
@@ -43,16 +45,16 @@ module Data.Aeson.Types.Internal
4345 , DotNetTime(..)
4446 ) where
4547
46−
4748import Control.Applicative
4849import Control.Monad
4950import Control.DeepSeq (NFData(..))
51+import Data.ByteString.Builder (Builder, char7)
5052import Data.Char (toLower, isUpper)
5153import Data.Scientific (Scientific)
5254import Data.Hashable (Hashable(..))
5355import Data.Data (Data)
5456import Data.HashMap.Strict (HashMap)
55−import Data.Monoid (Monoid(..))
57+import Data.Monoid (Monoid(..), (<>))
5658import Data.String (IsString(..))
5759import Data.Text (Text, pack)
5860import Data.Time (UTCTime)
@@ -182,6 +184,22 @@ data Value = Object !Object
182184 | Null
183185 deriving (Eq, Read, Show, Typeable, Data)
184186
187+-- | An encoding of a JSON value.
188+type Encoding = Builder
189+
190+data Series = Empty
191+ | Value Builder
192+ deriving (Typeable)
193+
194+instance Monoid Series where
195+ mempty = Empty
196+ mappend Empty a = a
197+ mappend (Value a) b =
198+ Value $
199+ a <> case b of
200+ Empty -> mempty
201+ Value c -> char7 ',' <> c
202+
185203-- | A newtype wrapper for 'UTCTime' that uses the same non-standard
186204-- serialization format as Microsoft .NET, whose @System.DateTime@
187205-- type is by default serialized to JSON as in the following example: