Json.hs (1794B)
1 {-# OPTIONS_HADDOCK prune #-} 2 {-# LANGUAGE OverloadedStrings #-} 3 4 -- | 5 -- Module: Vestigia.Json 6 -- Copyright: (c) 2026 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- A JSON value and its encoding: enough to write a report, with 11 -- object keys in the order given. 12 13 module Vestigia.Json ( 14 Value(..) 15 , encode 16 ) where 17 18 import Data.Char (ord) 19 import qualified Data.Text as T 20 import Numeric (showHex) 21 22 -- | A JSON value. An object keeps its keys in the order given. 23 data Value = 24 Null 25 | Bool !Bool 26 | Number !Integer 27 | String !T.Text 28 | Array ![Value] 29 | Object ![(T.Text, Value)] 30 deriving (Eq, Show) 31 32 -- | The value's encoding, indented two spaces per level. 33 -- 34 -- >>> encode (Object [("a", Array [Number 1, Null])]) 35 -- "{\n \"a\": [\n 1,\n null\n ]\n}\n" 36 encode :: Value -> T.Text 37 encode v = go 0 v <> "\n" 38 where 39 go :: Int -> Value -> T.Text 40 go d x = case x of 41 Null -> "null" 42 Bool b -> if b then "true" else "false" 43 Number n -> T.pack (show n) 44 String s -> str s 45 Array [] -> "[]" 46 Array xs -> "[\n" <> T.intercalate ",\n" 47 [ pad (d + 1) <> go (d + 1) y | y <- xs ] 48 <> "\n" <> pad d <> "]" 49 Object [] -> "{}" 50 Object kvs -> "{\n" <> T.intercalate ",\n" 51 [ pad (d + 1) <> str k <> ": " <> go (d + 1) y 52 | (k, y) <- kvs ] 53 <> "\n" <> pad d <> "}" 54 55 pad d = T.replicate (2 * d) " " 56 57 str :: T.Text -> T.Text 58 str s = "\"" <> T.concatMap esc s <> "\"" 59 where 60 esc c = case c of 61 '"' -> "\\\"" 62 '\\' -> "\\\\" 63 '\n' -> "\\n" 64 '\r' -> "\\r" 65 '\t' -> "\\t" 66 _ | ord c < 0x20 -> 67 let h = showHex (ord c) "" 68 in T.pack ("\\u" <> replicate (4 - length h) '0' <> h) 69 | otherwise -> T.singleton c