Skip to content

Commit 73be549

Browse files
committed
merge top-level objects inside arrays
1 parent c162664 commit 73be549

File tree

4 files changed

+103
-5
lines changed

4 files changed

+103
-5
lines changed

json-to-go.js

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
5252
};
5353

5454

55-
function parseScope(scope, depth = 0)
55+
function parseScope(scope, depth = 0, forceOmitEmptyNonArrays = false)
5656
{
5757
if (typeof scope === "object" && scope !== null)
5858
{
@@ -82,8 +82,11 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
8282
appender(slice);
8383
else
8484
append(slice)
85+
86+
// TODO: structs need a proper recursive solution to make merging generic
8587
if (sliceType == "struct") {
8688
const allFields = {};
89+
let insideOmitEmpty = false;
8790

8891
// for each field counts how many times appears
8992
for (let i = 0; i < scopeLength; i++)
@@ -112,6 +115,47 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
112115
}
113116

114117
if (areObjects(existingValue, currentValue)) {
118+
const currentKeys = Object.keys(currentValue)
119+
const existingKeys = Object.keys(existingValue)
120+
121+
// try to merge two object fields instead of creating separate ones
122+
// TODO: find a proper handling of omitempty for nested structs instead of
123+
// fake-forcing omitempty on all nested elements
124+
if (existingKeys.length > 0 && currentKeys.length > 0) {
125+
if (!allOmitempty) {
126+
// check if any of the existingKeys (which are assumed to be mandatory),
127+
// is not in the currentKeys
128+
for (const key of existingKeys) {
129+
if (!Object.keys(currentKeys).includes(key)) {
130+
insideOmitEmpty = true // TODO: only set this one key to omitempty
131+
break
132+
}
133+
}
134+
}
135+
136+
var mergedValues = existingValue
137+
for (const key of currentKeys) {
138+
// check if key has been found previously
139+
if (!Object.keys(mergedValues).includes(key)) {
140+
mergedValues[key] = currentValue[key]
141+
insideOmitEmpty = true // TODO: only set this one key to omitempty
142+
continue
143+
}
144+
145+
// check if types between previously found values and the current value
146+
if (!areSameType(mergedValues[key], currentValue[key])) {
147+
if (mergedValues[key] !== null) {
148+
mergedValues[key] = null // force type "any" if types are not identical
149+
console.warn(`Warning: nested key "${key}" uses multiple types. Defaulting to type "any".`)
150+
}
151+
}
152+
}
153+
154+
allFields[keyname].value = mergedValues;
155+
allFields[keyname].count++;
156+
continue;
157+
}
158+
115159
const comparisonResult = compareObjectKeys(
116160
Object.keys(currentValue),
117161
Object.keys(existingValue)
@@ -139,7 +183,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
139183
struct[keyname] = elem.value;
140184
omitempty[keyname] = elem.count != scopeLength;
141185
}
142-
parseStruct(depth + 1, innerTabs, struct, omitempty, previousParents); // finally parse the struct !!
186+
parseStruct(depth + 1, innerTabs, struct, omitempty, previousParents, insideOmitEmpty); // finally parse the struct !!
143187
}
144188
else if (sliceType == "slice") {
145189
parseScope(scope[0], depth)
@@ -162,7 +206,16 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
162206
append(parent)
163207
}
164208
}
165-
parseStruct(depth + 1, innerTabs, scope, false, previousParents);
209+
210+
const omitempty = {};
211+
if (forceOmitEmptyNonArrays) {
212+
const allKeys = Object.keys(scope)
213+
for (let k in allKeys) {
214+
const keyname = allKeys[k];
215+
omitempty[keyname] = true;
216+
}
217+
}
218+
parseStruct(depth + 1, innerTabs, scope, omitempty, previousParents);
166219
}
167220
}
168221
else {
@@ -175,7 +228,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
175228
}
176229
}
177230

178-
function parseStruct(depth, innerTabs, scope, omitempty, oldParents)
231+
function parseStruct(depth, innerTabs, scope, omitempty, oldParents, insideOmitEmpty)
179232
{
180233
if (flatten) {
181234
stack.push(
@@ -221,7 +274,7 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty
221274

222275
appender(typename+" ");
223276
parent = typename
224-
parseScope(scope[keys[i]], depth);
277+
parseScope(scope[keys[i]], depth, insideOmitEmpty);
225278
appender(' `json:"'+keyname);
226279
if (allOmitempty || (omitempty && omitempty[keys[i]] === true))
227280
{

json-to-go.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ function testFiles() {
163163
"duplicate-top-level-structs",
164164
"double-nested-objects",
165165
"array-with-nonmatching-types",
166+
"array-with-mergable-objects",
166167
];
167168

168169
for (const testCase of testCases) {

tests/array-with-mergable-objects.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
type AutoGenerated struct {
2+
Booleanfield bool `json:"booleanfield"`
3+
Inconsistentarray []Inconsistentarray `json:"inconsistentarray"`
4+
Date string `json:"date"`
5+
}
6+
type Features struct {
7+
Age int `json:"age,omitempty"`
8+
Height any `json:"height,omitempty"`
9+
Gender string `json:"gender,omitempty"`
10+
}
11+
type Inconsistentarray struct {
12+
ID int `json:"id"`
13+
Name string `json:"name"`
14+
Features Features `json:"features"`
15+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"booleanfield": true,
3+
"inconsistentarray": [
4+
{
5+
"id": 1,
6+
"name": "John Doe",
7+
"features": {
8+
"age": 49,
9+
"height": 175
10+
}
11+
},
12+
{
13+
"id": 2,
14+
"name": "Jane Doe",
15+
"features": {
16+
"height": 164
17+
}
18+
},
19+
{
20+
"id": 3,
21+
"name": "John Doe",
22+
"features": {
23+
"gender": "male",
24+
"height": "unknown"
25+
}
26+
}
27+
],
28+
"date": "2024-07-22"
29+
}

0 commit comments

Comments
 (0)