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