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@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 } 106 return self; 107 } 108 109 /* End output to JSON stream */ 110 void jsonw_destroy(json_writer_t **self_p) 111 { 112 json_writer_t *self = *self_p; 113 114 assert(self->depth == 0); 115 fputs("\n", self->out); 116 fflush(self->out); 117 free(self); 118 *self_p = NULL; 119 } 120 121 void jsonw_pretty(json_writer_t *self, bool on) 122 { 123 self->pretty = on; 124 } 125 126 /* Basic blocks */ 127 static void jsonw_begin(json_writer_t *self, int c) 128 { 129 jsonw_eor(self); 130 putc(c, self->out); 131 ++self->depth; 132 self->sep = '\0'; 133 } 134 135 static void jsonw_end(json_writer_t *self, int c) 136 { 137 assert(self->depth > 0); 138 139 --self->depth; 140 if (self->sep != '\0') 141 jsonw_eol(self); 142 putc(c, self->out); 143 self->sep = ','; 144 } 145 146 147 /* Add a JSON property name */ 148 void jsonw_name(json_writer_t *self, const char *name) 149 { 150 jsonw_eor(self); 151 jsonw_eol(self); 152 self->sep = '\0'; 153 jsonw_puts(self, name); 154 putc(':', self->out); 155 if (self->pretty) 156 putc(' ', self->out); 157 } 158 159 void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap) 160 { 161 jsonw_eor(self); 162 putc('"', self->out); 163 vfprintf(self->out, fmt, ap); 164 putc('"', self->out); 165 } 166 167 void jsonw_printf(json_writer_t *self, const char *fmt, ...) 168 { 169 va_list ap; 170 171 va_start(ap, fmt); 172 jsonw_eor(self); 173 vfprintf(self->out, fmt, ap); 174 va_end(ap); 175 } 176 177 /* Collections */ 178 void jsonw_start_object(json_writer_t *self) 179 { 180 jsonw_begin(self, '{'); 181 } 182 183 void jsonw_end_object(json_writer_t *self) 184 { 185 jsonw_end(self, '}'); 186 } 187 188 void jsonw_start_array(json_writer_t *self) 189 { 190 jsonw_begin(self, '['); 191 } 192 193 void jsonw_end_array(json_writer_t *self) 194 { 195 jsonw_end(self, ']'); 196 } 197 198 /* JSON value types */ 199 void jsonw_string(json_writer_t *self, const char *value) 200 { 201 jsonw_eor(self); 202 jsonw_puts(self, value); 203 } 204 205 void jsonw_bool(json_writer_t *self, bool val) 206 { 207 jsonw_printf(self, "%s", val ? "true" : "false"); 208 } 209 210 void jsonw_null(json_writer_t *self) 211 { 212 jsonw_printf(self, "null"); 213 } 214 215 void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num) 216 { 217 jsonw_printf(self, fmt, num); 218 } 219 220 #ifdef notused 221 void jsonw_float(json_writer_t *self, double num) 222 { 223 jsonw_printf(self, "%g", num); 224 } 225 #endif 226 227 void jsonw_hu(json_writer_t *self, unsigned short num) 228 { 229 jsonw_printf(self, "%hu", num); 230 } 231 232 void jsonw_uint(json_writer_t *self, uint64_t num) 233 { 234 jsonw_printf(self, "%"PRIu64, num); 235 } 236 237 void jsonw_lluint(json_writer_t *self, unsigned long long int num) 238 { 239 jsonw_printf(self, "%llu", num); 240 } 241 242 void jsonw_int(json_writer_t *self, int64_t num) 243 { 244 jsonw_printf(self, "%"PRId64, num); 245 } 246 247 /* Basic name/value objects */ 248 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) 249 { 250 jsonw_name(self, prop); 251 jsonw_string(self, val); 252 } 253 254 void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) 255 { 256 jsonw_name(self, prop); 257 jsonw_bool(self, val); 258 } 259 260 #ifdef notused 261 void jsonw_float_field(json_writer_t *self, const char *prop, double val) 262 { 263 jsonw_name(self, prop); 264 jsonw_float(self, val); 265 } 266 #endif 267 268 void jsonw_float_field_fmt(json_writer_t *self, 269 const char *prop, 270 const char *fmt, 271 double val) 272 { 273 jsonw_name(self, prop); 274 jsonw_float_fmt(self, fmt, val); 275 } 276 277 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) 278 { 279 jsonw_name(self, prop); 280 jsonw_uint(self, num); 281 } 282 283 void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) 284 { 285 jsonw_name(self, prop); 286 jsonw_hu(self, num); 287 } 288 289 void jsonw_lluint_field(json_writer_t *self, 290 const char *prop, 291 unsigned long long int num) 292 { 293 jsonw_name(self, prop); 294 jsonw_lluint(self, num); 295 } 296 297 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) 298 { 299 jsonw_name(self, prop); 300 jsonw_int(self, num); 301 } 302 303 void jsonw_null_field(json_writer_t *self, const char *prop) 304 { 305 jsonw_name(self, prop); 306 jsonw_null(self); 307 } 308 309 #ifdef TEST 310 int main(int argc, char **argv) 311 { 312 json_writer_t *wr = jsonw_new(stdout); 313 314 jsonw_start_object(wr); 315 jsonw_pretty(wr, true); 316 jsonw_name(wr, "Vyatta"); 317 jsonw_start_object(wr); 318 jsonw_string_field(wr, "url", "http://vyatta.com"); 319 jsonw_uint_field(wr, "downloads", 2000000ul); 320 jsonw_float_field(wr, "stock", 8.16); 321 322 jsonw_name(wr, "ARGV"); 323 jsonw_start_array(wr); 324 while (--argc) 325 jsonw_string(wr, *++argv); 326 jsonw_end_array(wr); 327 328 jsonw_name(wr, "empty"); 329 jsonw_start_array(wr); 330 jsonw_end_array(wr); 331 332 jsonw_name(wr, "NIL"); 333 jsonw_start_object(wr); 334 jsonw_end_object(wr); 335 336 jsonw_null_field(wr, "my_null"); 337 338 jsonw_name(wr, "special chars"); 339 jsonw_start_array(wr); 340 jsonw_string_field(wr, "slash", "/"); 341 jsonw_string_field(wr, "newline", "\n"); 342 jsonw_string_field(wr, "tab", "\t"); 343 jsonw_string_field(wr, "ff", "\f"); 344 jsonw_string_field(wr, "quote", "\""); 345 jsonw_string_field(wr, "tick", "\'"); 346 jsonw_string_field(wr, "backslash", "\\"); 347 jsonw_end_array(wr); 348 349 jsonw_end_object(wr); 350 351 jsonw_end_object(wr); 352 jsonw_destroy(&wr); 353 return 0; 354 } 355 356 #endif 357