Home | History | Annotate | Download | only in lib
      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