2011-09-01 19:16:31 +00:00
|
|
|
{- Streaming JSON output.
|
|
|
|
-
|
2016-07-26 17:30:07 +00:00
|
|
|
- Copyright 2011, 2016 Joey Hess <id@joeyh.name>
|
2011-09-01 19:16:31 +00:00
|
|
|
-
|
2014-05-10 14:01:27 +00:00
|
|
|
- License: BSD-2-clause
|
2011-09-01 19:16:31 +00:00
|
|
|
-}
|
|
|
|
|
2016-07-26 17:30:07 +00:00
|
|
|
{-# LANGUAGE GADTs #-}
|
|
|
|
|
2011-09-01 19:16:31 +00:00
|
|
|
module Utility.JSONStream (
|
2016-07-26 17:30:07 +00:00
|
|
|
JSONChunk(..),
|
2011-09-01 19:16:31 +00:00
|
|
|
start,
|
|
|
|
add,
|
|
|
|
end
|
|
|
|
) where
|
|
|
|
|
2016-07-26 17:30:07 +00:00
|
|
|
import qualified Text.JSON as JSON
|
|
|
|
import qualified Data.Aeson as Aeson
|
|
|
|
import qualified Data.ByteString.Lazy.UTF8 as B
|
2011-09-01 19:16:31 +00:00
|
|
|
|
2016-07-26 17:30:07 +00:00
|
|
|
{- Only JSON objects can be used as chunks in the stream, not
|
|
|
|
- other values.
|
|
|
|
-
|
|
|
|
- Both Aeson and Text.Json objects are supported. -}
|
|
|
|
data JSONChunk a where
|
|
|
|
JSONObject :: JSON.JSON a => [(String, a)] -> JSONChunk [(String, a)]
|
|
|
|
AesonObject :: Aeson.Object -> JSONChunk Aeson.Object
|
|
|
|
|
|
|
|
encodeJSONChunk :: JSONChunk a -> String
|
|
|
|
encodeJSONChunk (JSONObject l) = JSON.encodeStrict $ JSON.toJSObject l
|
|
|
|
encodeJSONChunk (AesonObject o) = B.toString (Aeson.encode o)
|
|
|
|
|
|
|
|
{- Text.JSON and Aeson do not support building up a larger JSON document
|
|
|
|
- piece by piece as a stream. To support streaming, a hack. The final "}"
|
|
|
|
- is left off the object, allowing it to be added to later. -}
|
|
|
|
start :: JSONChunk a -> String
|
|
|
|
start a
|
2011-09-21 03:24:48 +00:00
|
|
|
| last s == endchar = init s
|
2011-09-01 19:16:31 +00:00
|
|
|
| otherwise = bad s
|
2012-12-13 04:24:19 +00:00
|
|
|
where
|
2016-07-26 17:30:07 +00:00
|
|
|
s = encodeJSONChunk a
|
2011-09-01 19:16:31 +00:00
|
|
|
|
2016-07-26 17:30:07 +00:00
|
|
|
add :: JSONChunk a -> String
|
|
|
|
add a
|
2011-09-01 19:16:31 +00:00
|
|
|
| head s == startchar = ',' : drop 1 s
|
|
|
|
| otherwise = bad s
|
2012-12-13 04:24:19 +00:00
|
|
|
where
|
2016-07-26 17:30:07 +00:00
|
|
|
s = start a
|
2011-09-01 19:16:31 +00:00
|
|
|
|
|
|
|
end :: String
|
|
|
|
end = [endchar, '\n']
|
|
|
|
|
|
|
|
startchar :: Char
|
|
|
|
startchar = '{'
|
|
|
|
|
|
|
|
endchar :: Char
|
|
|
|
endchar = '}'
|
|
|
|
|
|
|
|
bad :: String -> a
|
2016-07-26 17:30:07 +00:00
|
|
|
bad s = error $ "JSON encoder generated unexpected value: " ++ s
|
|
|
|
|