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