File: jsonpp.ml

package info (click to toggle)
easy-format 1.3.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 200 kB
  • sloc: ml: 1,223; makefile: 15
file content (162 lines) | stat: -rwxr-xr-x 4,849 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#! /usr/bin/env ocamlscript
Ocaml.packs := ["json-wheel"; "easy-format"]
--
open Json_type
open Easy_format

(* JSON does not allow rendering floats with a trailing dot: that is,
   1234. is not allowed, but 1234.0 is ok.  here, we add a '0' if
   string_of_int result in a trailing dot *)
let jstring_of_float f =
  let s = string_of_float f in
  let s_len = String.length s in
  if s.[ s_len - 1 ] = '.' then
    s ^ "0"
  else
    s

let escape_json_string buf s =
  for i = 0 to String.length s - 1 do
    let c = String.unsafe_get s i in
    match c with
      | '"'    -> Buffer.add_string buf "\\\""
      | '\t'   -> Buffer.add_string buf "\\t"
      | '\r'   -> Buffer.add_string buf "\\r"
      | '\b'   -> Buffer.add_string buf "\\b"
      | '\n'   -> Buffer.add_string buf "\\n"
      | '\012' -> Buffer.add_string buf "\\f"
      | '\\'   -> Buffer.add_string buf "\\\\"
   (* | '/'    -> "\\/" *) (* Forward slash can be escaped
                              but doesn't have to *)
      | '\x00'..'\x1F' (* Control characters that must be escaped *)
      | '\x7F' (* DEL *) ->
          Printf.bprintf buf "\\u%04X" (int_of_char c)
      | _      ->
          (* Don't bother detecting or escaping multibyte chars *)
          Buffer.add_char buf c
  done

let jstring_of_string s =
  let buf = Buffer.create (String.length s) in
  Buffer.add_char buf '"';
  escape_json_string buf s;
  Buffer.add_char buf '"';
  Buffer.contents buf



let null = { atom_style = Some "null" }
let bool = { atom_style = Some "bool" }
let int = { atom_style = Some "int" }
let float = { atom_style = Some "float" }
let string = { atom_style = Some "string" }
let label_string = { atom_style = Some "label" }
let colon = { atom_style = Some "punct" }

let array =
  { list with
      opening_style = Some "punct";
      separator_style = Some "punct";
      closing_style = Some "punct" }

let label_with_colon =
  { list with
      space_after_opening = false;
      space_before_closing = false;
      space_after_separator = false;
      wrap_body = `No_breaks }

let rec format = function
    Null -> Atom ("null", null)
  | Bool b -> Atom (string_of_bool b, bool)
  | Int i -> Atom (string_of_int i, int)
  | Float f -> Atom (jstring_of_float f, float)
  | String s -> Atom (jstring_of_string s, string)
  | Array l -> List (("[", ",", "]", array), List.map format l)
  | Object l -> List (("{", ",", "}", array), List.map format_field l)

and format_field (s, x) =
  let lab =
    List (("", "", "", label_with_colon),
	  [ Atom (jstring_of_string s, label_string);
	    Atom (":", colon) ])
  in
  Label ((lab, label), format x)



let html_escape_string s =
  let buf = Buffer.create (2 * String.length s) in
  for i = 0 to String.length s - 1 do
    match s.[i] with
	'&' -> Buffer.add_string buf "&"
      | '<' -> Buffer.add_string buf "&lt;"
      | '>' -> Buffer.add_string buf "&gt;"
      | c -> Buffer.add_char buf c
  done;
  Buffer.contents buf

let html_escape = `Escape_string html_escape_string
let html_style = [
  "null", { tag_open = "<span class=\"json-null\">";
	    tag_close = "</span>" };
  "bool", { tag_open = "<span class=\"json-bool\">";
	    tag_close = "</span>" };
  "int", { tag_open = "<span class=\"json-int\">";
	   tag_close = "</span>" };
  "float", { tag_open = "<span class=\"json-float\">";
	     tag_close = "</span>" };
  "string", { tag_open = "<span class=\"json-string\">";
	      tag_close = "</span>" };
  "label", { tag_open = "<span class=\"json-label\">";
	     tag_close = "</span>" };
  "punct", { tag_open = "<span class=\"json-punct\">";
	     tag_close = "</span>" };
]



let print_html json =
  print_string "\
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"
        \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">
<head>
<title>JSON</title>
<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />
<meta name=\"generator\" content=\"Easy-format\" />
<style type=\"text/css\">
body,code,pre { color:black;background-color:white }
.json-null { color: #808080; }
.json-bool { color: black; }
.json-int { color: black; }
.json-float { color: black; }
.json-string { color: black; }
.json-label { color: #0033cc; }
.json-punct { color: black; }
</style>
</head>
<body>
<pre>
";
  Pretty.to_stdout ~escape: html_escape ~styles: html_style (format json);
  print_string "\
</pre>
</body>
</html>
"

let () =
  let options = [] in
  let files = ref [] in
  let anon_fun s = files := s :: !files in
  let usage_msg = Printf.sprintf "Usage: %s <file>" Sys.argv.(0) in
  Arg.parse options anon_fun usage_msg;
  let file =
    match !files with
	[s] -> s
      | _ -> Arg.usage options usage_msg; exit 1
  in
  let json = Json_io.load_json ~allow_comments:true file in
  print_html json