@@ -0,0 +1,55 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Data.Aeson.Encode
4+ (
5+ build
6+ , encode
7+ ) where
8+
9+import Blaze.ByteString.Builder
10+import Blaze.ByteString.Builder.Char.Utf8
11+import Data.Aeson.Types (JSON(..), Value(..))
12+import Data.Monoid (mappend, mconcat)
13+import qualified Data.ByteString.Char8 as B
14+import qualified Data.ByteString.Lazy.Char8 as L
15+import qualified Data.Map as M
16+import qualified Data.Text as T
17+import qualified Data.Vector as V
18+
19+build :: Value -> Builder
20+build Null = fromByteString "null"
21+build (Bool b) = fromByteString $ if b then "true" else "false"
22+build (Number n) = fromByteString (B.pack (show n))
23+build (String s) = string s
24+build (Array v)
25+ | V.null v = fromByteString "[]"
26+ | otherwise = fromChar '[' `mappend`
27+ build (V.unsafeHead v) `mappend`
28+ V.foldr f (fromChar ']') (V.unsafeTail v)
29+ where f a z = fromChar ',' `mappend` build a `mappend` z
30+build (Object m) =
31+ case M.toList m of
32+ (x:xs) -> fromChar '{' `mappend`
33+ one x `mappend`
34+ foldr f (fromChar '}') xs
35+ _ -> fromByteString "{}"
36+ where f a z = fromChar ',' `mappend` one a `mappend` z
37+ one (k,v) = string k `mappend` fromChar ':' `mappend` build v
38+
39+string :: T.Text -> Builder
40+string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
41+ where
42+ quote s = case T.uncons t of
43+ Just (c,t') -> mconcat [fromText h, fromText (escape c),
44+ quote t']
45+ Nothing -> fromText h
46+ where (h,t) = T.break isEscape s
47+ isEscape c = c == '\"' || c == '\n' || c == '\r' || c == '\t'
48+ escape '\"' = "\\\""
49+ escape '\n' = "\\n"
50+ escape '\r' = "\\r"
51+ escape '\t' = "\\t"
52+
53+encode :: JSON a => a -> L.ByteString
54+encode = toLazyByteString . build . toJSON
55+{-# INLINE encode #-}