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