Skip to content

Commit 22c38fe

Browse files
committed
Added module to parse and print (compact or pretty) JSON files
1 parent fd296c6 commit 22c38fe

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

JSON.hs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
module JSON (parseJSON
2+
,prettyJSON
3+
,JValue (..)
4+
) where
5+
import Text.Parsec
6+
import Data.List (intercalate)
7+
import System.Environment (getArgs)
8+
9+
data JValue = JObject [(String,JValue)]
10+
| JArray [JValue]
11+
| JNumber Float
12+
| JString String
13+
| JBool Bool
14+
| JNull
15+
deriving Eq
16+
17+
instance Show (JValue) where
18+
show (JObject xs) = surround "{" "}" $ intercalate "," (map showpair xs)
19+
where showpair (k,v) = show k ++ ":" ++ show v
20+
show (JArray ms) = surround "[" "]" $ intercalate "," (map show ms)
21+
show (JNumber n) = show n
22+
show (JString s) = show s
23+
show (JBool True) = "true"
24+
show (JBool False) = "false"
25+
show JNull = "null"
26+
27+
surround :: [a] -> [a] -> [a] -> [a]
28+
surround a b xs = a ++ xs ++ b
29+
30+
prettyJSON :: JValue -> String
31+
prettyJSON = unlines . pretty
32+
where
33+
pretty :: JValue -> [String] -- each string sits on its own line. Makes it easier to indent
34+
pretty (JObject ps) = prettyCollection "{" "}" prettyPair ps
35+
pretty (JArray ms) = prettyCollection "[" "]" pretty ms
36+
pretty x = [show x]
37+
prettyCollection a b p xs@(_ : _ : _) = -- more than one member: show on multiple lines, with all but the braces indented one level
38+
surround [a] [b] $ prettySequence p xs >>= pure . ('\t' : )
39+
prettyCollection a b p xs = -- one or fewer members: show on a single line
40+
prettySequence p xs >>= pure . surround a b
41+
42+
prettyPair :: (String, JValue) -> [String]
43+
prettyPair (name, json) = let (line : rest) = pretty json
44+
in (show name ++ " : " ++ line) : rest -- the first line of the value is on the same line as the name
45+
46+
prettySequence :: (a -> [String]) -> [a] -> [String] -- commas the prettified elements, bar the last
47+
prettySequence p (x : y : ys) = (commaLast $ p x) ++ (prettySequence p $ y : ys)
48+
prettySequence p [y] = p y
49+
prettySequence _ [] = []
50+
51+
commaLast [x] = [x ++ ","]
52+
commaLast (x : xs) = x : commaLast xs
53+
commaLast [] = []
54+
55+
parseJSON :: String -> String -> Either String JValue
56+
parseJSON fname input = case parse parseJValue fname input of
57+
Left err -> Left ("parse error: " ++ show err)
58+
Right json -> Right json
59+
60+
parseJValue :: Stream s m Char => ParsecT s u m JValue
61+
parseJValue = do
62+
spaces
63+
json <- (
64+
parseJObject
65+
<|> parseJArray
66+
<|> parseJNumber
67+
<|> parseJString
68+
<|> parseJBool
69+
<|> parseJNull)
70+
spaces
71+
return json
72+
parseJObject :: Stream s m Char => ParsecT s u m JValue
73+
parseJObject = do
74+
char '{'
75+
spaces
76+
ms <- members
77+
spaces
78+
char '}'
79+
return $ JObject ms
80+
where
81+
member = do
82+
name <- parseString
83+
spaces
84+
char ':'
85+
spaces
86+
value <- parseJValue
87+
return (name,value)
88+
members = do
89+
m <- member
90+
ms <- optionMaybe (do
91+
spaces
92+
char ','
93+
spaces
94+
ms <- members
95+
return ms)
96+
return $ case ms of
97+
Nothing -> [m]
98+
Just ms -> (m : ms)
99+
100+
parseJArray :: Stream s m Char => ParsecT s u m JValue
101+
parseJArray = do
102+
char '['
103+
spaces
104+
ms <- members
105+
spaces
106+
char ']'
107+
return $ JArray ms
108+
where
109+
member = do
110+
value <- parseJValue
111+
return value
112+
members = do
113+
m <- member
114+
ms <- optionMaybe (do
115+
spaces
116+
char ','
117+
spaces
118+
ms <- members
119+
return ms)
120+
return $ case ms of
121+
Nothing -> [m]
122+
Just ms -> (m : ms)
123+
124+
parseJNumber :: Stream s m Char => ParsecT s u m JValue
125+
parseJNumber = do
126+
val <- many1 digit
127+
dec <- option "0" (do
128+
char '.'
129+
many1 digit)
130+
return $ JNumber $ read (val ++ "." ++ dec)
131+
132+
parseString :: Stream s m Char => ParsecT s u m String
133+
parseString = do
134+
char '"'
135+
val <- many $ noneOf "\""
136+
char '"'
137+
return $ val
138+
139+
parseJString :: Stream s m Char => ParsecT s u m JValue
140+
parseJString = do
141+
val <- parseString
142+
return $ JString val
143+
144+
parseJBool :: Stream s m Char => ParsecT s u m JValue
145+
parseJBool = do
146+
bool <- string "true" <|> string "false"
147+
return $ case bool of
148+
"true" -> JBool True
149+
"false" -> JBool False
150+
151+
parseJNull :: Stream s m Char => ParsecT s u m JValue
152+
parseJNull = do
153+
string "null"
154+
return JNull
155+
156+
main :: IO ()
157+
main = do
158+
[ifile] <- getArgs
159+
text <- readFile ifile
160+
case parseJSON ifile text of
161+
Left err -> putStrLn $ err
162+
Right json -> putStr $ prettyJSON json

0 commit comments

Comments
 (0)