@@ -9,7 +9,8 @@ module Data.Aeson.Encode
99import Blaze.ByteString.Builder
1010import Blaze.ByteString.Builder.Char.Utf8
1111import Data.Aeson.Types (ToJSON(..), Value(..))
12−import Data.Monoid (mappend, mconcat)
12+import Data.Monoid (mappend)
13+import Numeric (showHex)
1314import qualified Data.ByteString.Char8 as B
1415import qualified Data.ByteString.Lazy.Char8 as L
1516import qualified Data.Map as M
@@ -40,16 +41,19 @@ string :: T.Text -> Builder
4041string s = fromChar '"' `mappend` quote s `mappend` fromChar '"'
4142 where
4243 quote q = case T.uncons t of
43− Just (c,t') -> mconcat [fromText h, fromText (escape c),
44− quote t']
44+ Just (c,t') -> fromText h `mappend` escape c `mappend` quote t'
4545 Nothing -> fromText h
4646 where (h,t) = T.break isEscape q
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− escape _ = error "Data.Aeson.Encode.build: internal error"
47+ isEscape c = c == '\"' || c == '\\' || c < '\x20'
48+ escape '\"' = fromByteString "\\\""
49+ escape '\\' = fromByteString "\\\\"
50+ escape '\n' = fromByteString "\\n"
51+ escape '\r' = fromByteString "\\r"
52+ escape '\t' = fromByteString "\\t"
53+ escape c
54+ | c < '\x20' = fromString $ "\\u" ++ replicate (4 - length h) '0' ++ h
55+ | otherwise = fromChar c
56+ where h = showHex (fromEnum c) ""
5357
5458encode :: ToJSON a => a -> L.ByteString
5559encode = toLazyByteString . build . toJSON