diff --git a/json-to-go.js b/json-to-go.js index 870ce0b..4512a07 100644 --- a/json-to-go.js +++ b/json-to-go.js @@ -111,6 +111,11 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty continue } + // if variable was first detected as int (7) and a second time as float64 (3.14) + // then we want to select float64, not int. Similar for int64 and float64. + if(areSameType(currentValue, 1)) + allFields[keyname].value = findBestValueForNumberType(existingValue, currentValue); + if (areObjects(existingValue, currentValue)) { const comparisonResult = compareObjectKeys( Object.keys(currentValue), @@ -389,6 +394,50 @@ function jsonToGo(json, typename, flatten = true, example = false, allOmitempty } } + // change the value to expand ints and floats to their larger equivalent + function findBestValueForNumberType(existingValue, newValue) { + if (!areSameType(newValue, 1)) { + console.error(`Error: currentValue ${newValue} is not a number`) + return null // falls back to goType "any" + } + + const newGoType = goType(newValue) + const existingGoType = goType(existingValue) + + if (newGoType === existingGoType) + return existingValue + + // always upgrade float64 + if (newGoType === "float64") + return newValue + if (existingGoType === "float64") + return existingValue + + // it's too complex to distinguish int types and float32, so we force-upgrade to float64 + // if anyone has a better suggestion, PRs are welcome! + if (newGoType.includes("float") && existingGoType.includes("int")) + return Number.MAX_VALUE + if (newGoType.includes("int") && existingGoType.includes("float")) + return Number.MAX_VALUE + + if (newGoType.includes("int") && existingGoType.includes("int")) { + const existingValueAbs = Math.abs(existingValue); + const newValueAbs = Math.abs(newValue); + + // if the sum is overflowing, it's safe to assume numbers are very large. So we force int64. + if (!isFinite(existingValueAbs + newValueAbs)) + return Number.MAX_SAFE_INTEGER + + // it's too complex to distinguish int8, int16, int32 and int64, so we just use the sum as best-guess + return existingValueAbs + newValueAbs; + } + + // There should be other cases + console.error(`Error: something went wrong with findBestValueForNumberType() using the values: '${newValue}' and '${existingValue}'`) + console.error(" Please report the problem to https://github.com/mholt/json-to-go/issues") + return null // falls back to goType "any" + } + // Given two types, returns the more specific of the two function mostSpecificPossibleGoType(typ1, typ2) { diff --git a/json-to-go.test.js b/json-to-go.test.js index c2a6e3f..0b5cd9f 100644 --- a/json-to-go.test.js +++ b/json-to-go.test.js @@ -162,6 +162,7 @@ function testFiles() { const testCases = [ "duplicate-top-level-structs", "double-nested-objects", + "array-with-mixed-float-int", "array-with-nonmatching-types", ]; diff --git a/tests/array-with-mixed-float-int.go b/tests/array-with-mixed-float-int.go new file mode 100644 index 0000000..54cd08f --- /dev/null +++ b/tests/array-with-mixed-float-int.go @@ -0,0 +1,13 @@ +type AutoGenerated struct { + AgeOfTheUniverse []AgeOfTheUniverse `json:"age of the universe"` + AgeOfTheEarth []AgeOfTheEarth `json:"age of the earth"` + Date string `json:"date"` +} +type AgeOfTheUniverse struct { + Name string `json:"name"` + Value float64 `json:"value"` +} +type AgeOfTheEarth struct { + Name string `json:"name"` + Value int64 `json:"value"` +} diff --git a/tests/array-with-mixed-float-int.json b/tests/array-with-mixed-float-int.json new file mode 100644 index 0000000..4f72a86 --- /dev/null +++ b/tests/array-with-mixed-float-int.json @@ -0,0 +1,31 @@ +{ + "age of the universe": [ + { + "name": "in years", + "value": 1378700000 + }, + { + "name": "in seconds", + "value": 4.35075327952992e+17 + }, + { + "name": "in kapla", + "value": 0.31914351851 + } + ], + "age of the earth": [ + { + "name": "in eons (roughly)", + "value": 4 + }, + { + "name": "in years", + "value": 4.543e9 + }, + { + "name": "in seconds", + "value": 1.6592953e+12 + } + ], + "date": "2024-07-25" +}