json_writer.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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. #include <stdio.h>
  15. #include <stdbool.h>
  16. #include <stdarg.h>
  17. #include <assert.h>
  18. #include <malloc.h>
  19. #include <inttypes.h>
  20. #include <stdint.h>
  21. #include "json_writer.h"
  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. /* indentation for pretty print */
  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. /* end current line and indent if pretty printing */
  36. static void jsonw_eol(json_writer_t *self)
  37. {
  38. if (!self->pretty)
  39. return;
  40. putc('\n', self->out);
  41. jsonw_indent(self);
  42. }
  43. /* If current object is not empty print a comma */
  44. static void jsonw_eor(json_writer_t *self)
  45. {
  46. if (self->sep != '\0')
  47. putc(self->sep, self->out);
  48. self->sep = ',';
  49. }
  50. /* Output JSON encoded string */
  51. /* Handles C escapes, does not do Unicode */
  52. static void jsonw_puts(json_writer_t *self, const char *str)
  53. {
  54. putc('"', self->out);
  55. for (; *str; ++str)
  56. switch (*str) {
  57. case '\t':
  58. fputs("\\t", self->out);
  59. break;
  60. case '\n':
  61. fputs("\\n", self->out);
  62. break;
  63. case '\r':
  64. fputs("\\r", self->out);
  65. break;
  66. case '\f':
  67. fputs("\\f", self->out);
  68. break;
  69. case '\b':
  70. fputs("\\b", self->out);
  71. break;
  72. case '\\':
  73. fputs("\\n", self->out);
  74. break;
  75. case '"':
  76. fputs("\\\"", self->out);
  77. break;
  78. case '\'':
  79. fputs("\\\'", self->out);
  80. break;
  81. default:
  82. putc(*str, self->out);
  83. }
  84. putc('"', self->out);
  85. }
  86. /* Create a new JSON stream */
  87. json_writer_t *jsonw_new(FILE *f)
  88. {
  89. json_writer_t *self = malloc(sizeof(*self));
  90. if (self) {
  91. self->out = f;
  92. self->depth = 0;
  93. self->pretty = false;
  94. self->sep = '\0';
  95. }
  96. return self;
  97. }
  98. /* End output to JSON stream */
  99. void jsonw_destroy(json_writer_t **self_p)
  100. {
  101. json_writer_t *self = *self_p;
  102. assert(self->depth == 0);
  103. fputs("\n", self->out);
  104. fflush(self->out);
  105. free(self);
  106. *self_p = NULL;
  107. }
  108. void jsonw_pretty(json_writer_t *self, bool on)
  109. {
  110. self->pretty = on;
  111. }
  112. /* Basic blocks */
  113. static void jsonw_begin(json_writer_t *self, int c)
  114. {
  115. jsonw_eor(self);
  116. putc(c, self->out);
  117. ++self->depth;
  118. self->sep = '\0';
  119. }
  120. static void jsonw_end(json_writer_t *self, int c)
  121. {
  122. assert(self->depth > 0);
  123. --self->depth;
  124. if (self->sep != '\0')
  125. jsonw_eol(self);
  126. putc(c, self->out);
  127. self->sep = ',';
  128. }
  129. /* Add a JSON property name */
  130. void jsonw_name(json_writer_t *self, const char *name)
  131. {
  132. jsonw_eor(self);
  133. jsonw_eol(self);
  134. self->sep = '\0';
  135. jsonw_puts(self, name);
  136. putc(':', self->out);
  137. if (self->pretty)
  138. putc(' ', self->out);
  139. }
  140. void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
  141. {
  142. jsonw_eor(self);
  143. putc('"', self->out);
  144. vfprintf(self->out, fmt, ap);
  145. putc('"', self->out);
  146. }
  147. void jsonw_printf(json_writer_t *self, const char *fmt, ...)
  148. {
  149. va_list ap;
  150. va_start(ap, fmt);
  151. jsonw_eor(self);
  152. vfprintf(self->out, fmt, ap);
  153. va_end(ap);
  154. }
  155. /* Collections */
  156. void jsonw_start_object(json_writer_t *self)
  157. {
  158. jsonw_begin(self, '{');
  159. }
  160. void jsonw_end_object(json_writer_t *self)
  161. {
  162. jsonw_end(self, '}');
  163. }
  164. void jsonw_start_array(json_writer_t *self)
  165. {
  166. jsonw_begin(self, '[');
  167. }
  168. void jsonw_end_array(json_writer_t *self)
  169. {
  170. jsonw_end(self, ']');
  171. }
  172. /* JSON value types */
  173. void jsonw_string(json_writer_t *self, const char *value)
  174. {
  175. jsonw_eor(self);
  176. jsonw_puts(self, value);
  177. }
  178. void jsonw_bool(json_writer_t *self, bool val)
  179. {
  180. jsonw_printf(self, "%s", val ? "true" : "false");
  181. }
  182. void jsonw_null(json_writer_t *self)
  183. {
  184. jsonw_printf(self, "null");
  185. }
  186. void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
  187. {
  188. jsonw_printf(self, fmt, num);
  189. }
  190. #ifdef notused
  191. void jsonw_float(json_writer_t *self, double num)
  192. {
  193. jsonw_printf(self, "%g", num);
  194. }
  195. #endif
  196. void jsonw_hu(json_writer_t *self, unsigned short num)
  197. {
  198. jsonw_printf(self, "%hu", num);
  199. }
  200. void jsonw_uint(json_writer_t *self, uint64_t num)
  201. {
  202. jsonw_printf(self, "%"PRIu64, num);
  203. }
  204. void jsonw_lluint(json_writer_t *self, unsigned long long int num)
  205. {
  206. jsonw_printf(self, "%llu", num);
  207. }
  208. void jsonw_int(json_writer_t *self, int64_t num)
  209. {
  210. jsonw_printf(self, "%"PRId64, num);
  211. }
  212. /* Basic name/value objects */
  213. void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
  214. {
  215. jsonw_name(self, prop);
  216. jsonw_string(self, val);
  217. }
  218. void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
  219. {
  220. jsonw_name(self, prop);
  221. jsonw_bool(self, val);
  222. }
  223. #ifdef notused
  224. void jsonw_float_field(json_writer_t *self, const char *prop, double val)
  225. {
  226. jsonw_name(self, prop);
  227. jsonw_float(self, val);
  228. }
  229. #endif
  230. void jsonw_float_field_fmt(json_writer_t *self,
  231. const char *prop,
  232. const char *fmt,
  233. double val)
  234. {
  235. jsonw_name(self, prop);
  236. jsonw_float_fmt(self, fmt, val);
  237. }
  238. void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
  239. {
  240. jsonw_name(self, prop);
  241. jsonw_uint(self, num);
  242. }
  243. void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
  244. {
  245. jsonw_name(self, prop);
  246. jsonw_hu(self, num);
  247. }
  248. void jsonw_lluint_field(json_writer_t *self,
  249. const char *prop,
  250. unsigned long long int num)
  251. {
  252. jsonw_name(self, prop);
  253. jsonw_lluint(self, num);
  254. }
  255. void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
  256. {
  257. jsonw_name(self, prop);
  258. jsonw_int(self, num);
  259. }
  260. void jsonw_null_field(json_writer_t *self, const char *prop)
  261. {
  262. jsonw_name(self, prop);
  263. jsonw_null(self);
  264. }
  265. #ifdef TEST
  266. int main(int argc, char **argv)
  267. {
  268. json_writer_t *wr = jsonw_new(stdout);
  269. jsonw_start_object(wr);
  270. jsonw_pretty(wr, true);
  271. jsonw_name(wr, "Vyatta");
  272. jsonw_start_object(wr);
  273. jsonw_string_field(wr, "url", "http://vyatta.com");
  274. jsonw_uint_field(wr, "downloads", 2000000ul);
  275. jsonw_float_field(wr, "stock", 8.16);
  276. jsonw_name(wr, "ARGV");
  277. jsonw_start_array(wr);
  278. while (--argc)
  279. jsonw_string(wr, *++argv);
  280. jsonw_end_array(wr);
  281. jsonw_name(wr, "empty");
  282. jsonw_start_array(wr);
  283. jsonw_end_array(wr);
  284. jsonw_name(wr, "NIL");
  285. jsonw_start_object(wr);
  286. jsonw_end_object(wr);
  287. jsonw_null_field(wr, "my_null");
  288. jsonw_name(wr, "special chars");
  289. jsonw_start_array(wr);
  290. jsonw_string_field(wr, "slash", "/");
  291. jsonw_string_field(wr, "newline", "\n");
  292. jsonw_string_field(wr, "tab", "\t");
  293. jsonw_string_field(wr, "ff", "\f");
  294. jsonw_string_field(wr, "quote", "\"");
  295. jsonw_string_field(wr, "tick", "\'");
  296. jsonw_string_field(wr, "backslash", "\\");
  297. jsonw_end_array(wr);
  298. jsonw_end_object(wr);
  299. jsonw_end_object(wr);
  300. jsonw_destroy(&wr);
  301. return 0;
  302. }
  303. #endif