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 } 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_printf(json_writer_t *self, const char *fmt, ...) 160 { 161 va_list ap; 162 163 va_start(ap, fmt); 164 jsonw_eor(self); 165 vfprintf(self->out, fmt, ap); 166 va_end(ap); 167 } 168 169 /* Collections */ 170 void jsonw_start_object(json_writer_t *self) 171 { 172 jsonw_begin(self, '{'); 173 } 174 175 void jsonw_end_object(json_writer_t *self) 176 { 177 jsonw_end(self, '}'); 178 } 179 180 void jsonw_start_array(json_writer_t *self) 181 { 182 jsonw_begin(self, '['); 183 } 184 185 void jsonw_end_array(json_writer_t *self) 186 { 187 jsonw_end(self, ']'); 188 } 189 190 /* JSON value types */ 191 void jsonw_string(json_writer_t *self, const char *value) 192 { 193 jsonw_eor(self); 194 jsonw_puts(self, value); 195 } 196 197 void jsonw_bool(json_writer_t *self, bool val) 198 { 199 jsonw_printf(self, "%s", val ? "true" : "false"); 200 } 201 202 void jsonw_null(json_writer_t *self) 203 { 204 jsonw_printf(self, "null"); 205 } 206 207 void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num) 208 { 209 jsonw_printf(self, fmt, num); 210 } 211 212 #ifdef notused 213 void jsonw_float(json_writer_t *self, double num) 214 { 215 jsonw_printf(self, "%g", num); 216 } 217 #endif 218 219 void jsonw_hu(json_writer_t *self, unsigned short num) 220 { 221 jsonw_printf(self, "%hu", num); 222 } 223 224 void jsonw_uint(json_writer_t *self, uint64_t num) 225 { 226 jsonw_printf(self, "%"PRIu64, num); 227 } 228 229 void jsonw_lluint(json_writer_t *self, unsigned long long int num) 230 { 231 jsonw_printf(self, "%llu", num); 232 } 233 234 void jsonw_int(json_writer_t *self, int64_t num) 235 { 236 jsonw_printf(self, "%"PRId64, num); 237 } 238 239 /* Basic name/value objects */ 240 void jsonw_string_field(json_writer_t *self, const char *prop, const char *val) 241 { 242 jsonw_name(self, prop); 243 jsonw_string(self, val); 244 } 245 246 void jsonw_bool_field(json_writer_t *self, const char *prop, bool val) 247 { 248 jsonw_name(self, prop); 249 jsonw_bool(self, val); 250 } 251 252 #ifdef notused 253 void jsonw_float_field(json_writer_t *self, const char *prop, double val) 254 { 255 jsonw_name(self, prop); 256 jsonw_float(self, val); 257 } 258 #endif 259 260 void jsonw_float_field_fmt(json_writer_t *self, 261 const char *prop, 262 const char *fmt, 263 double val) 264 { 265 jsonw_name(self, prop); 266 jsonw_float_fmt(self, fmt, val); 267 } 268 269 void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num) 270 { 271 jsonw_name(self, prop); 272 jsonw_uint(self, num); 273 } 274 275 void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num) 276 { 277 jsonw_name(self, prop); 278 jsonw_hu(self, num); 279 } 280 281 void jsonw_lluint_field(json_writer_t *self, 282 const char *prop, 283 unsigned long long int num) 284 { 285 jsonw_name(self, prop); 286 jsonw_lluint(self, num); 287 } 288 289 void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num) 290 { 291 jsonw_name(self, prop); 292 jsonw_int(self, num); 293 } 294 295 void jsonw_null_field(json_writer_t *self, const char *prop) 296 { 297 jsonw_name(self, prop); 298 jsonw_null(self); 299 } 300 301 #ifdef TEST 302 int main(int argc, char **argv) 303 { 304 json_writer_t *wr = jsonw_new(stdout); 305 306 jsonw_start_object(wr); 307 jsonw_pretty(wr, true); 308 jsonw_name(wr, "Vyatta"); 309 jsonw_start_object(wr); 310 jsonw_string_field(wr, "url", "http://vyatta.com"); 311 jsonw_uint_field(wr, "downloads", 2000000ul); 312 jsonw_float_field(wr, "stock", 8.16); 313 314 jsonw_name(wr, "ARGV"); 315 jsonw_start_array(wr); 316 while (--argc) 317 jsonw_string(wr, *++argv); 318 jsonw_end_array(wr); 319 320 jsonw_name(wr, "empty"); 321 jsonw_start_array(wr); 322 jsonw_end_array(wr); 323 324 jsonw_name(wr, "NIL"); 325 jsonw_start_object(wr); 326 jsonw_end_object(wr); 327 328 jsonw_null_field(wr, "my_null"); 329 330 jsonw_name(wr, "special chars"); 331 jsonw_start_array(wr); 332 jsonw_string_field(wr, "slash", "/"); 333 jsonw_string_field(wr, "newline", "\n"); 334 jsonw_string_field(wr, "tab", "\t"); 335 jsonw_string_field(wr, "ff", "\f"); 336 jsonw_string_field(wr, "quote", "\""); 337 jsonw_string_field(wr, "tick", "\'"); 338 jsonw_string_field(wr, "backslash", "\\"); 339 jsonw_end_array(wr); 340 341 jsonw_end_object(wr); 342 343 jsonw_end_object(wr); 344 jsonw_destroy(&wr); 345 return 0; 346 } 347 348 #endif 349