1 /* 2 * Simple streaming JSON writer 3 * 4 * This takes care of the annoying bits of JSON syntax like the commas 5 * after elements 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 10 * 2 of the License, or (at your option) any later version. 11 * 12 * Authors: Stephen Hemminger <stephen (at) networkplumber.org> 13 */ 14 15 #include <stdio.h> 16 #include <stdbool.h> 17 #include <stdarg.h> 18 #include <assert.h> 19 #include <malloc.h> 20 #include <inttypes.h> 21 #include <stdint.h> 22 23 #include "json_writer.h" 24 25 struct json_writer { 26 FILE *out; /* output file */ 27 unsigned depth; /* nesting */ 28 bool pretty; /* optional whitepace */ 29 char sep; /* either nul or comma */ 30 }; 31 32 /* indentation for pretty print */ 33 static void jsonw_indent(json_writer_t *self) 34 { 35 unsigned i; 36 for (i = 0; i <= self->depth; ++i) 37 fputs(" ", self->out); 38 } 39 40 /* end current line and indent if pretty printing */ 41 static void jsonw_eol(json_writer_t *self) 42 { 43 if (!self->pretty) 44 return; 45 46 putc('\n', self->out); 47 jsonw_indent(self); 48 } 49 50 /* If current object is not empty print a comma */ 51 static void jsonw_eor(json_writer_t *self) 52 { 53 if (self->sep != '\0') 54 putc(self->sep, self->out); 55 self->sep = ','; 56 } 57 58 59 /* Output JSON encoded string */ 60 /* Handles C escapes, does not do Unicode */ 61 static void jsonw_puts(json_writer_t *self, const char *str) 62 { 63 putc('"', self->out); 64 for (; *str; ++str) 65 switch (*str) { 66 case '\t': 67 fputs("\\t", self->out); 68 break; 69 case '\n': 70 fputs("\\n", self->out); 71 break; 72 case '\r': 73 fputs("\\r", self->out); 74 break; 75 case '\f': 76 fputs("\\f", self->out); 77 break; 78 case '\b': 79 fputs("\\b", self->out); 80 break; 81 case '\\': 82 fputs("\\n", self->out); 83 break; 84 case '"': 85 fputs("\\\"", self->out); 86 break; 87 case '\'': 88 fputs("\\\'", self->out); 89 break; 90 default: 91 putc(*str, self->out); 92 } 93 putc('"', self->out); 94 } 95 96 /* Create a new JSON stream */ 97 json_writer_t *jsonw_new(FILE *f) 98 { 99 json_writer_t *self = malloc(sizeof(*self)); 100 if (self) { 101 self->out = f; 102 self->depth = 0; 103 self->pretty = false; 104 self->sep = '\0'; 105 putc('{', self->out); 106 } 107 return self; 108 } 109 110 /* End output to JSON stream */ 111 void jsonw_destroy(json_writer_t **self_p) 112 { 113 json_writer_t *self = *self_p; 114 115 assert(self->depth == 0); 116 jsonw_eol(self); 117 fputs("}\n", self->out); 118 fflush(self->out); 119 free(self); 120 *self_p = NULL; 121 } 122 123 void jsonw_pretty(json_writer_t *self, bool on) 124 { 125 self->pretty = on; 126 } 127 128 /* Basic blocks */ 129 static void jsonw_begin(json_writer_t *self, int c) 130 { 131 jsonw_eor(self); 132 putc(c, self->out); 133 ++self->depth; 134 self->sep = '\0'; 135 } 136 137 static void jsonw_end(json_writer_t *self, int c) 138 { 139 assert(self->depth > 0); 140 141 --self->depth; 142 if (self->sep != '\0') 143 jsonw_eol(self); 144 putc(c, self->out); 145 self->sep = ','; 146 } 147 148 149 /* Add a JSON property name */ 150 void jsonw_name(json_writer_t *self, const char *name) 151 { 152 jsonw_eor(self); 153 jsonw_eol(self); 154 self->sep = '\0'; 155 jsonw_puts(self, name); 156 putc(':', self->out); 157 if (self->pretty) 158 putc(' ', self->out); 159 } 160 161 static void jsonw_printf(json_writer_t *self, const char *fmt, ...) 162 { 163 va_list ap; 164 165 va_start(ap, fmt); 166 jsonw_eor(self); 167 vfprintf(self->out, fmt, ap); 168 va_end(ap); 169 } 170 171 /* Collections */ 172 void jsonw_start_object(json_writer_t *self) 173 { 174 jsonw_begin(self, '{'); 175 } 176 177 void jsonw_end_object(json_writer_t *self) 178 { 179 jsonw_end(self, '}'); 180 } 181 182 void jsonw_start_array(json_writer_t *self) 183 { 184 jsonw_begin(self, '['); 185 } 186 187 void jsonw_end_array(json_writer_t *self) 188 { 189 jsonw_end(self, ']'); 190 } 191 192 /* JSON value types */ 193 void jsonw_string(json_writer_t *self, const char *value) 194 { 195 jsonw_eor(self); 196 jsonw_puts(self, value); 197 } 198 199 void jsonw_bool(json_writer_t *self, bool val) 200 { 201 jsonw_printf(self, "%s", val ? "true" : "false"); 202 } 203 204 #ifdef notused 205 void jsonw_null(json_writer_t *self) 206 { 207 jsonw_printf(self, "null"); 208 } 209 210 void jsonw_float(json_writer_t *self, double num) 211 { 212 jsonw_printf(self, "%g", num); 213 } 214 #endif 215 216 void jsonw_uint(json_writer_t *self, uint64_t num) 217 { 218 jsonw_printf(self, "%"PRIu64, num); 219 } 220 221 void jsonw_int(json_writer_t *self, int64_t num) 222 { 223 jsonw_printf(self, "%"PRId64, num); 224 } 225 226 /* Basic name/value objects */ 227 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) 228 { 229 jsonw_name(self, prop); 230 jsonw_string(self, val); 231 } 232 233 void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) 234 { 235 jsonw_name(self, prop); 236 jsonw_bool(self, val); 237 } 238 239 #ifdef notused 240 void jsonw_float_field(json_writer_t *self, const char *prop, double val) 241 { 242 jsonw_name(self, prop); 243 jsonw_float(self, val); 244 } 245 #endif 246 247 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) 248 { 249 jsonw_name(self, prop); 250 jsonw_uint(self, num); 251 } 252 253 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) 254 { 255 jsonw_name(self, prop); 256 jsonw_int(self, num); 257 } 258 259 #ifdef notused 260 void jsonw_null_field(json_writer_t *self, const char *prop) 261 { 262 jsonw_name(self, prop); 263 jsonw_null(self); 264 } 265 #endif 266 267 #ifdef TEST 268 int main(int argc, char **argv) 269 { 270 json_writer_t *wr = jsonw_new(stdout); 271 272 jsonw_pretty(wr, true); 273 jsonw_name(wr, "Vyatta"); 274 jsonw_start_object(wr); 275 jsonw_string_field(wr, "url", "http://vyatta.com"); 276 jsonw_uint_field(wr, "downloads", 2000000ul); 277 jsonw_float_field(wr, "stock", 8.16); 278 279 jsonw_name(wr, "ARGV"); 280 jsonw_start_array(wr); 281 while (--argc) 282 jsonw_string(wr, *++argv); 283 jsonw_end_array(wr); 284 285 jsonw_name(wr, "empty"); 286 jsonw_start_array(wr); 287 jsonw_end_array(wr); 288 289 jsonw_name(wr, "NIL"); 290 jsonw_start_object(wr); 291 jsonw_end_object(wr); 292 293 jsonw_null_field(wr, "my_null"); 294 295 jsonw_name(wr, "special chars"); 296 jsonw_start_array(wr); 297 jsonw_string_field(wr, "slash", "/"); 298 jsonw_string_field(wr, "newline", "\n"); 299 jsonw_string_field(wr, "tab", "\t"); 300 jsonw_string_field(wr, "ff", "\f"); 301 jsonw_string_field(wr, "quote", "\""); 302 jsonw_string_field(wr, "tick", "\'"); 303 jsonw_string_field(wr, "backslash", "\\"); 304 jsonw_end_array(wr); 305 306 jsonw_end_object(wr); 307 308 jsonw_destroy(&wr); 309 return 0; 310 } 311 312 #endif 313