json_writer.c 6.6 KB

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