Skip to content

Commit 25237b6

Browse files
authored
Merge pull request #131 from grische/fix/duplicate-top-level-structs
fix duplicate top-level structs
2 parents e7be945 + 3c7724a commit 25237b6

File tree

6 files changed

+164
-9
lines changed

6 files changed

+164
-9
lines changed

json-to-go.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
1919
let accumulator = "";
2020
let innerTabs = 0;
2121
let parent = "";
22+
let globallySeenTypeNames = [];
23+
let previousParents = "";
2224

2325
try
2426
{
@@ -128,7 +130,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
128130
struct[keyname] = elem.value;
129131
omitempty[keyname] = elem.count != scopeLength;
130132
}
131-
parseStruct(depth + 1, innerTabs, struct, omitempty); // finally parse the struct !!
133+
parseStruct(depth + 1, innerTabs, struct, omitempty, previousParents); // finally parse the struct !!
132134
}
133135
else if (sliceType == "slice") {
134136
parseScope(scope[0], depth)
@@ -151,7 +153,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
151153
append(parent)
152154
}
153155
}
154-
parseStruct(depth + 1, innerTabs, scope);
156+
parseStruct(depth + 1, innerTabs, scope, false, previousParents);
155157
}
156158
}
157159
else {
@@ -164,7 +166,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
164166
}
165167
}
166168

167-
function parseStruct(depth, innerTabs, scope, omitempty)
169+
function parseStruct(depth, innerTabs, scope, omitempty, oldParents)
168170
{
169171
if (flatten) {
170172
stack.push(
@@ -193,12 +195,20 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
193195
appender(`${parentType} struct {\n`);
194196
++innerTabs;
195197
const keys = Object.keys(scope);
198+
previousParents = parent
196199
for (let i in keys)
197200
{
198201
const keyname = getOriginalName(keys[i]);
199202
indenter(innerTabs)
200-
const typename = uniqueTypeName(format(keyname), seenTypeNames)
201-
seenTypeNames.push(typename)
203+
let typename
204+
// structs will be defined on the top level of the go file, so they need to be globally unique
205+
if (typeof scope[keys[i]] === "object" && scope[keys[i]] !== null) {
206+
typename = uniqueTypeName(format(keyname), globallySeenTypeNames, previousParents)
207+
globallySeenTypeNames.push(typename)
208+
} else {
209+
typename = uniqueTypeName(format(keyname), seenTypeNames)
210+
seenTypeNames.push(typename)
211+
}
202212

203213
appender(typename+" ");
204214
parent = typename
@@ -212,18 +222,28 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
212222
}
213223
indenter(--innerTabs);
214224
appender("}");
225+
previousParents = oldParents;
215226
}
216227
else
217228
{
218229
append("struct {\n");
219230
++tabs;
220231
const keys = Object.keys(scope);
232+
previousParents = parent
221233
for (let i in keys)
222234
{
223235
const keyname = getOriginalName(keys[i]);
224236
indent(tabs);
225-
const typename = uniqueTypeName(format(keyname), seenTypeNames)
226-
seenTypeNames.push(typename)
237+
let typename
238+
// structs will be defined on the top level of the go file, so they need to be globally unique
239+
if (typeof scope[keys[i]] === "object" && scope[keys[i]] !== null) {
240+
typename = uniqueTypeName(format(keyname), globallySeenTypeNames, previousParents)
241+
globallySeenTypeNames.push(typename)
242+
} else {
243+
typename = uniqueTypeName(format(keyname), seenTypeNames)
244+
seenTypeNames.push(typename)
245+
}
246+
227247
append(typename+" ");
228248
parent = typename
229249
parseScope(scope[keys[i]], depth);
@@ -240,6 +260,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
240260
}
241261
indent(--tabs);
242262
append("}");
263+
previousParents = oldParents;
243264
}
244265
if (flatten)
245266
accumulator += stack.pop();
@@ -269,11 +290,19 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
269290

270291
// Generate a unique name to avoid duplicate struct field names.
271292
// This function appends a number at the end of the field name.
272-
function uniqueTypeName(name, seen) {
293+
function uniqueTypeName(name, seen, prefix=null) {
273294
if (seen.indexOf(name) === -1) {
274295
return name;
275296
}
276297

298+
// check if we can get a unique name by prefixing it
299+
if(prefix) {
300+
name = prefix+name
301+
if (seen.indexOf(name) === -1) {
302+
return name;
303+
}
304+
}
305+
277306
let i = 0;
278307
while (true) {
279308
let newName = name + i.toString();

json-to-go.test.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,39 @@ function test(includeExampleData) {
149149
);
150150
}
151151
}
152+
console.log(includeExampleData ? "done testing samples with data" : "done testing samples without data")
153+
}
154+
155+
function testFiles() {
156+
const fs = require('fs');
157+
const path = require('path');
158+
159+
const testCases = [
160+
"duplicate-top-level-structs",
161+
"double-nested-objects",
162+
];
152163

153-
console.log("done")
164+
for (const testCase of testCases) {
165+
166+
try {
167+
const jsonData = fs.readFileSync(path.join('tests', testCase + '.json'), 'utf8');
168+
const expectedGoData = fs.readFileSync(path.join('tests', testCase + '.go'), 'utf8');
169+
const got = jsonToGo(jsonData);
170+
if (got.error) {
171+
console.assert(!got.error, `format('${jsonData}'): ${got.error}`);
172+
} else {
173+
console.assert(
174+
got.go === expectedGoData,
175+
`format('${jsonData}'): \n\tgot: ${quote(got.go)}\n\twant: ${quote(expectedGoData)}`
176+
);
177+
}
178+
} catch (err) {
179+
console.error(err);
180+
}
181+
}
182+
console.log("done testing files")
154183
}
155184

156185
test(false);
157186
test(true)
187+
testFiles()

tests/double-nested-objects.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
type AutoGenerated struct {
2+
First First `json:"first"`
3+
Second Second `json:"second"`
4+
}
5+
type Type struct {
6+
Short string `json:"short"`
7+
Long string `json:"long"`
8+
}
9+
type First struct {
10+
ID int `json:"id"`
11+
Type Type `json:"type"`
12+
}
13+
type SecondType struct {
14+
Long string `json:"long"`
15+
}
16+
type Second struct {
17+
ID int `json:"id"`
18+
SecondType SecondType `json:"type"`
19+
}

tests/double-nested-objects.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"first": {
3+
"id": 1,
4+
"type": {
5+
"short": "owa",
6+
"long": "objectwitharray"
7+
}
8+
},
9+
"second": {
10+
"id": 2,
11+
"type": {
12+
"long": "object"
13+
}
14+
}
15+
}

tests/duplicate-top-level-structs.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
type AutoGenerated struct {
2+
Region Region `json:"region"`
3+
Municipality Municipality `json:"municipality"`
4+
Building Building `json:"building"`
5+
}
6+
type Identifier struct {
7+
Type string `json:"type"`
8+
ID int `json:"id"`
9+
}
10+
type Region struct {
11+
Identifier Identifier `json:"identifier"`
12+
Autonomous bool `json:"autonomous"`
13+
}
14+
type MunicipalityIdentifier struct {
15+
Type string `json:"type"`
16+
Name string `json:"name"`
17+
}
18+
type Municipality struct {
19+
MunicipalityIdentifier MunicipalityIdentifier `json:"identifier"`
20+
}
21+
type Postal struct {
22+
Type string `json:"type"`
23+
ID int `json:"id"`
24+
}
25+
type Road struct {
26+
Name string `json:"name"`
27+
ID int `json:"id"`
28+
}
29+
type BuildingIdentifier struct {
30+
Postal Postal `json:"postal"`
31+
Road Road `json:"road"`
32+
}
33+
type Building struct {
34+
BuildingIdentifier BuildingIdentifier `json:"identifier"`
35+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"region": {
3+
"identifier": {
4+
"type": "ISO 3166-1",
5+
"id": 234
6+
},
7+
"autonomous": true
8+
},
9+
"municipality": {
10+
"identifier": {
11+
"type": "local",
12+
"name": "Tórshavn"
13+
}
14+
},
15+
"building": {
16+
"identifier": {
17+
"postal": {
18+
"type": "local",
19+
"id": 100
20+
},
21+
"road": {
22+
"name": "Gríms Kambansgøta",
23+
"id": 1
24+
}
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)