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 }