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 25struct 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 */ 33static 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 */ 41static 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 */ 51static 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 */ 61static 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 */ 97json_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 */ 111void 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 123void jsonw_pretty(json_writer_t *self, bool on) 124{ 125 self->pretty = on; 126} 127 128/* Basic blocks */ 129static 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 137static 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 */ 150void 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 161static 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 */ 172void jsonw_start_object(json_writer_t *self) 173{ 174 jsonw_begin(self, '{'); 175} 176 177void jsonw_end_object(json_writer_t *self) 178{ 179 jsonw_end(self, '}'); 180} 181 182void jsonw_start_array(json_writer_t *self) 183{ 184 jsonw_begin(self, '['); 185} 186 187void jsonw_end_array(json_writer_t *self) 188{ 189 jsonw_end(self, ']'); 190} 191 192/* JSON value types */ 193void jsonw_string(json_writer_t *self, const char *value) 194{ 195 jsonw_eor(self); 196 jsonw_puts(self, value); 197} 198 199void jsonw_bool(json_writer_t *self, bool val) 200{ 201 jsonw_printf(self, "%s", val ? "true" : "false"); 202} 203 204#ifdef notused 205void jsonw_null(json_writer_t *self) 206{ 207 jsonw_printf(self, "null"); 208} 209 210void jsonw_float(json_writer_t *self, double num) 211{ 212 jsonw_printf(self, "%g", num); 213} 214#endif 215 216void jsonw_uint(json_writer_t *self, uint64_t num) 217{ 218 jsonw_printf(self, "%"PRIu64, num); 219} 220 221void jsonw_int(json_writer_t *self, int64_t num) 222{ 223 jsonw_printf(self, "%"PRId64, num); 224} 225 226/* Basic name/value objects */ 227void 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 233void 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 240void 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 247void 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 253void 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 260void 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 268int 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