Skip to content

Commit fb4ffe9

Browse files
mike-hosseinimholt
authored andcommitted
Add flatten option for non-anonymous structs (#47)
1 parent c0a991f commit fb4ffe9

File tree

1 file changed

+134
-55
lines changed

1 file changed

+134
-55
lines changed

json-to-go.js

Lines changed: 134 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,17 @@
77
A simple utility to translate JSON into a Go type definition.
88
*/
99

10-
function jsonToGo(json, typename)
10+
function jsonToGo(json, typename, flatten = true)
1111
{
12-
var data;
13-
var scope;
14-
var go = "";
15-
var tabs = 0;
12+
let data;
13+
let scope;
14+
let go = "";
15+
let tabs = 0;
16+
17+
let accumulator = "";
18+
let innerTabs = 0;
19+
let stack = [];
20+
let parent = "";
1621

1722
try
1823
{
@@ -28,25 +33,29 @@ function jsonToGo(json, typename)
2833
}
2934

3035
typename = format(typename || "AutoGenerated");
31-
append("type "+typename+" ");
36+
append(`type ${typename} `);
3237

3338
parseScope(scope);
34-
35-
return { go: go };
3639

40+
return {
41+
go: flatten
42+
? go += accumulator
43+
: go
44+
};
3745

3846

39-
function parseScope(scope)
47+
function parseScope(scope, depth = 0)
4048
{
4149
if (typeof scope === "object" && scope !== null)
4250
{
4351
if (Array.isArray(scope))
4452
{
45-
var sliceType, scopeLength = scope.length;
53+
let sliceType;
54+
const scopeLength = scope.length;
4655

47-
for (var i = 0; i < scopeLength; i++)
56+
for (let i = 0; i < scopeLength; i++)
4857
{
49-
var thisType = goType(scope[i]);
58+
const thisType = goType(scope[i]);
5059
if (!sliceType)
5160
sliceType = thisType;
5261
else if (sliceType != thisType)
@@ -57,17 +66,20 @@ function jsonToGo(json, typename)
5766
}
5867
}
5968

60-
append("[]");
69+
if (flatten && depth >= 2)
70+
appender("[]");
71+
else
72+
append("[]")
6173
if (sliceType == "struct") {
62-
var allFields = {};
74+
const allFields = {};
6375

6476
// for each field counts how many times appears
65-
for (var i = 0; i < scopeLength; i++)
77+
for (let i = 0; i < scopeLength; i++)
6678
{
67-
var keys = Object.keys(scope[i])
68-
for (var k in keys)
79+
const keys = Object.keys(scope[i])
80+
for (let k in keys)
6981
{
70-
var keyname = keys[k];
82+
const keyname = keys[k];
7183
if (!(keyname in allFields)) {
7284
allFields[keyname] = {
7385
value: scope[i][keyname],
@@ -78,61 +90,117 @@ function jsonToGo(json, typename)
7890
allFields[keyname].count++;
7991
}
8092
}
81-
93+
8294
// create a common struct with all fields found in the current array
8395
// omitempty dict indicates if a field is optional
84-
var keys = Object.keys(allFields), struct = {}, omitempty = {};
85-
for (var k in keys)
96+
const keys = Object.keys(allFields), struct = {}, omitempty = {};
97+
for (let k in keys)
8698
{
87-
var keyname = keys[k], elem = allFields[keyname];
99+
const keyname = keys[k], elem = allFields[keyname];
88100

89101
struct[keyname] = elem.value;
90102
omitempty[keyname] = elem.count != scopeLength;
91103
}
92-
93-
parseStruct(struct, omitempty); // finally parse the struct !!
104+
parseStruct(depth + 1, innerTabs, struct, omitempty); // finally parse the struct !!
94105
}
95106
else if (sliceType == "slice") {
96-
parseScope(scope[0])
107+
parseScope(scope[0], depth)
108+
}
109+
else {
110+
if (flatten && depth >= 2) {
111+
appender(sliceType || "interface{}");
112+
} else {
113+
append(sliceType || "interface{}");
114+
}
97115
}
98-
else
99-
append(sliceType || "interface{}");
100116
}
101117
else
102118
{
103-
parseStruct(scope);
119+
if (flatten) {
120+
if (depth >= 2){
121+
appender(parent)
122+
}
123+
else {
124+
append(parent)
125+
}
126+
}
127+
parseStruct(depth + 1, innerTabs, scope);
128+
}
129+
}
130+
else {
131+
if (depth >= 2){
132+
appender(goType(scope));
133+
}
134+
else {
135+
append(goType(scope));
104136
}
105137
}
106-
else
107-
append(goType(scope));
108138
}
109139

110-
function parseStruct(scope, omitempty)
140+
function parseStruct(depth, innerTabs, scope, omitempty)
111141
{
112-
append("struct {\n");
113-
++tabs;
114-
var keys = Object.keys(scope);
115-
for (var i in keys)
116-
{
117-
var keyname = keys[i];
118-
indent(tabs);
119-
append(format(keyname)+" ");
120-
parseScope(scope[keyname]);
142+
if (flatten) {
143+
stack.push(
144+
depth >= 2
145+
? "\n"
146+
: ""
147+
)
148+
}
121149

122-
append(' `json:"'+keyname);
123-
if (omitempty && omitempty[keyname] === true)
150+
if (flatten && depth >= 2)
151+
{
152+
appender(`type ${parent} ` + "struct {\n");
153+
++innerTabs;
154+
const keys = Object.keys(scope);
155+
for (let i in keys)
124156
{
125-
append(',omitempty');
157+
const keyname = keys[i];
158+
indenter(innerTabs)
159+
const typename = format(keyname)
160+
appender(typename+" ");
161+
parent = typename
162+
parseScope(scope[keyname], depth);
163+
appender(' `json:"'+keyname);
164+
if (omitempty && omitempty[keyname] === true)
165+
{
166+
appender(',omitempty');
167+
}
168+
appender('"`\n');
169+
}
170+
indenter(--innerTabs);
171+
appender("}");
172+
}
173+
else
174+
{
175+
append("struct {\n");
176+
++tabs;
177+
const keys = Object.keys(scope);
178+
for (let i in keys)
179+
{
180+
const keyname = keys[i];
181+
indent(tabs);
182+
const typename = format(keyname);
183+
append(typename+" ");
184+
parent = typename
185+
parseScope(scope[keyname], depth);
186+
187+
append(' `json:"'+keyname);
188+
if (omitempty && omitempty[keyname] === true)
189+
{
190+
append(',omitempty');
191+
}
192+
append('"`\n');
126193
}
127-
append('"`\n');
194+
indent(--tabs);
195+
append("}");
128196
}
129-
indent(--tabs);
130-
append("}");
197+
if (flatten)
198+
accumulator += stack.pop();
131199
}
132200

133201
function indent(tabs)
134202
{
135-
for (var i = 0; i < tabs; i++)
203+
for (let i = 0; i < tabs; i++)
136204
go += '\t';
137205
}
138206

@@ -141,6 +209,17 @@ function jsonToGo(json, typename)
141209
go += str;
142210
}
143211

212+
function indenter(tabs)
213+
{
214+
for (let i = 0; i < tabs; i++)
215+
stack[stack.length - 1] += '\t';
216+
}
217+
218+
function appender(str)
219+
{
220+
stack[stack.length - 1] += str;
221+
}
222+
144223
// Sanitizes and formats a string to make an appropriate identifier in Go
145224
function format(str)
146225
{
@@ -150,7 +229,7 @@ function jsonToGo(json, typename)
150229
str = "Num" + str;
151230
else if (str.charAt(0).match(/\d/))
152231
{
153-
var numbers = {'0': "Zero_", '1': "One_", '2': "Two_", '3': "Three_",
232+
const numbers = {'0': "Zero_", '1': "One_", '2': "Two_", '3': "Three_",
154233
'4': "Four_", '5': "Five_", '6': "Six_", '7': "Seven_",
155234
'8': "Eight_", '9': "Nine_"};
156235
str = numbers[str.charAt(0)] + str.substr(1);
@@ -163,7 +242,7 @@ function jsonToGo(json, typename)
163242
{
164243
if (val === null)
165244
return "interface{}";
166-
245+
167246
switch (typeof val)
168247
{
169248
case "string":
@@ -209,10 +288,10 @@ function jsonToGo(json, typename)
209288
function toProperCase(str)
210289
{
211290
// https://github.com/golang/lint/blob/39d15d55e9777df34cdffde4f406ab27fd2e60c0/lint.go#L695-L731
212-
var commonInitialisms = [
213-
"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP",
214-
"HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA",
215-
"SMTP", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI",
291+
const commonInitialisms = [
292+
"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP",
293+
"HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA",
294+
"SMTP", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "UID", "UUID", "URI",
216295
"URL", "UTF8", "VM", "XML", "XSRF", "XSS"
217296
];
218297

@@ -235,10 +314,10 @@ function jsonToGo(json, typename)
235314
if (typeof module != 'undefined') {
236315
if (!module.parent) {
237316
process.stdin.on('data', function(buf) {
238-
var json = buf.toString('utf8')
317+
const json = buf.toString('utf8')
239318
console.log(jsonToGo(json).go)
240319
})
241320
} else {
242321
module.exports = jsonToGo
243322
}
244-
}
323+
}

0 commit comments

Comments
 (0)