tsv2json.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tsv2json.c (3359B)
       ---
            1 /* Converts TSV output from json2tsv back to JSON.
            2  * Can be useful to filter/replace JSON and reassemble it again.
            3  * json2tsv: https://git.codemadness.org/json2tsv/
            4  * example: json2tsv -r < file.json | \
            5  *          awk 'BEGIN { OFS = FS = "\t" } $1 == ".key" { $3 = ""; } { print $0 }' | \
            6  *          tsv2json
            7  */
            8 #include <sys/types.h>
            9 
           10 #include <ctype.h>
           11 #include <stdio.h>
           12 #include <stdlib.h>
           13 #include <string.h>
           14 
           15 enum { FieldSelector, FieldType, FieldValue, FieldLast };
           16 
           17 enum JSONType {
           18         JSON_TYPE_ARRAY  = 'a',
           19         JSON_TYPE_OBJECT = 'o',
           20         JSON_TYPE_STRING = 's',
           21         JSON_TYPE_BOOL   = 'b',
           22         JSON_TYPE_NULL   = '?',
           23         JSON_TYPE_NUMBER = 'n'
           24 };
           25 
           26 #define JSON_MAX_NODE_DEPTH 64
           27 
           28 static struct json_node {
           29         enum JSONType type;
           30         size_t index; /* count/index for array or object type */
           31 } nodes[JSON_MAX_NODE_DEPTH];
           32 
           33 size_t
           34 countdepth(const char *s)
           35 {
           36         size_t d;
           37 
           38         for (d = 0; *s; s++)
           39                 if (*s == '.' || *s == '[')
           40                         d++;
           41 
           42         return d;
           43 }
           44 
           45 size_t
           46 parseline(char *line, char *fields[FieldLast])
           47 {
           48         char *prev, *s;
           49         size_t i;
           50 
           51         for (prev = line, i = 0;
           52             (s = strchr(prev, '\t')) && i < FieldLast - 1;
           53             i++) {
           54                 *s = '\0';
           55                 fields[i] = prev;
           56                 prev = s + 1;
           57         }
           58         fields[i++] = prev;
           59         /* make non-parsed fields empty. */
           60         for (; i < FieldLast; i++)
           61                 fields[i] = "";
           62 
           63         return i;
           64 }
           65 
           66 void
           67 jsonstring(const char *s, size_t len)
           68 {
           69         size_t i;
           70 
           71         for (i = 0; i < len; i++) {
           72                 if (s[i] != '\\') {
           73                         if (s[i] == '"')
           74                                 putchar('\\');
           75                         putchar(s[i]);
           76                         continue;
           77                 }
           78                 switch (s[i]) {
           79                 case '\0': /* ignore */
           80                         break;
           81                 case 'n':
           82                 case '\\':
           83                 case 't':
           84                         putchar(s[i]);
           85                         break;
           86                 default:
           87                         if (iscntrl((unsigned char)s[i]))
           88                                 printf("\\\\u00%02x", (unsigned char)s[i]);
           89                 }
           90         }
           91 }
           92 
           93 void
           94 endnodes(size_t prevdepth, size_t depth)
           95 {
           96         size_t i;
           97 
           98         for (i = prevdepth; i > depth; i--) {
           99                 switch (nodes[i - 1].type) {
          100                 case JSON_TYPE_ARRAY:
          101                 case JSON_TYPE_OBJECT:
          102                         putchar(nodes[i - 1].type == JSON_TYPE_ARRAY ? ']' : '}');
          103                 default:
          104                         break;
          105                 }
          106         }
          107 }
          108 
          109 int
          110 main(void)
          111 {
          112         char *fields[FieldLast], *line = NULL, *p;
          113         size_t depth = 0, linesiz = 0, prevdepth = 0;
          114         ssize_t n;
          115 
          116         while ((n = getline(&line, &linesiz, stdin)) > 0) {
          117                 if (line[n - 1] == '\n')
          118                         line[--n] = '\0';
          119                 parseline(line, fields);
          120 
          121                 depth = countdepth(fields[FieldSelector]);
          122                 if (depth >= sizeof(nodes) / sizeof(nodes[0])) {
          123                         fputs("invalid input\n", stderr);
          124                         exit(1);
          125                 }
          126                 endnodes(prevdepth + (prevdepth == depth), depth);
          127 
          128                 nodes[depth].type = fields[FieldType][0];
          129 
          130                 if (depth) {
          131                         if (nodes[depth - 1].index)
          132                                 fputs(",", stdout);
          133                         nodes[depth - 1].index++;
          134 
          135                         if (nodes[depth - 1].type == JSON_TYPE_OBJECT) {
          136                                 fputs("\"", stdout);
          137                                 if ((p = strrchr(fields[FieldSelector], '.')))
          138                                         p++;
          139                                 else
          140                                         p = fields[FieldSelector];
          141                                 jsonstring(p, strcspn(p, "["));
          142                                 fputs("\":", stdout);
          143                         }
          144                 }
          145 
          146                 switch (fields[FieldType][0]) {
          147                 case JSON_TYPE_ARRAY:
          148                 case JSON_TYPE_OBJECT:
          149                         nodes[depth].index = 0;
          150                         putchar(fields[FieldType][0] == JSON_TYPE_ARRAY ? '[' : '{');
          151                         break;
          152                 case JSON_TYPE_STRING:
          153                         putchar('"');
          154                         jsonstring(fields[FieldValue], strlen(fields[FieldValue]));
          155                         putchar('"');
          156                         break;
          157                 case JSON_TYPE_NULL:
          158                         fputs("null", stdout);
          159                         break;
          160                 case JSON_TYPE_BOOL:
          161                 case JSON_TYPE_NUMBER:
          162                         fputs(fields[FieldValue], stdout);
          163                         break;
          164                 }
          165 
          166                 prevdepth = depth;
          167         }
          168 
          169         endnodes(prevdepth + 1, 0);
          170 
          171         return 0;
          172 }