pull/32520/merge
Sueun Cho 23 hours ago committed by GitHub
commit 7fd6249974
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -294,7 +294,7 @@ func TestMergeValuesCLI(t *testing.T) {
},
expected: map[string]any{
"foo": map[string]any{
"bar": []any{1.0, 2.0, 3.0},
"bar": []any{int64(1), int64(2), int64(3)},
},
},
},

@ -224,10 +224,11 @@ func (t *parser) key(data map[string]any, nestedNameLevel int) (reterr error) {
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
var jsonval any
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
dec.UseNumber()
if err := dec.Decode(&jsonval); err != nil {
return err
}
set(data, string(k), jsonval)
set(data, string(k), convertJSONNumbers(jsonval))
if _, err = io.CopyN(io.Discard, t.sc, dec.InputOffset()); err != nil {
return err
}
@ -356,10 +357,11 @@ func (t *parser) listItem(list []any, i, nestedNameLevel int) ([]any, error) {
// discard in t.sc the chars of the decoded json value (the number of those characters is returned by InputOffset).
var jsonval any
dec := json.NewDecoder(strings.NewReader(t.sc.String()))
dec.UseNumber()
if err := dec.Decode(&jsonval); err != nil {
return list, err
}
if list, err = setIndex(list, i, jsonval); err != nil {
if list, err = setIndex(list, i, convertJSONNumbers(jsonval)); err != nil {
return list, err
}
if _, err = io.CopyN(io.Discard, t.sc, dec.InputOffset()); err != nil {
@ -555,3 +557,33 @@ func typedVal(v []rune, st bool) any {
return val
}
// convertJSONNumbers walks a value decoded by encoding/json with UseNumber
// enabled and turns every json.Number into an int64 when it is integral, or a
// float64 otherwise. Decoding into an any without UseNumber makes every JSON
// number a float64, which silently loses precision for integers beyond 2^53
// (for example 9007199254740993 becomes 9007199254740992). This keeps the
// --set-json path consistent with the plain --set path, whose typedVal parses
// integers as int64.
func convertJSONNumbers(v any) any {
switch val := v.(type) {
case json.Number:
if iv, err := val.Int64(); err == nil {
return iv
}
fv, _ := val.Float64()
return fv
case map[string]any:
for k, e := range val {
val[k] = convertJSONNumbers(e)
}
return val
case []any:
for i, e := range val {
val[i] = convertJSONNumbers(e)
}
return val
default:
return v
}
}

@ -642,6 +642,51 @@ func TestParseJSON(t *testing.T) {
}
}
func TestParseJSONNumbers(t *testing.T) {
// A JSON integer must round-trip exactly, including values above 2^53
// where a float64 would lose precision. This mirrors the plain --set
// path, which parses integers as int64.
tests := []struct {
name string
input string
expect map[string]any
}{
{
name: "integer beyond 2^53 keeps its exact value",
input: "id=9007199254740993",
expect: map[string]any{"id": int64(9007199254740993)},
},
{
name: "small integer decodes as int64",
input: "n=3",
expect: map[string]any{"n": int64(3)},
},
{
name: "negative large integer keeps its exact value",
input: "n=-9223372036854775807",
expect: map[string]any{"n": int64(-9223372036854775807)},
},
{
name: "fractional value stays a float",
input: "ratio=1.5",
expect: map[string]any{"ratio": float64(1.5)},
},
{
name: "integers nested in objects and arrays keep their exact value",
input: `obj={"big":9007199254740993,"list":[9007199254740995]}`,
expect: map[string]any{"obj": map[string]any{"big": int64(9007199254740993), "list": []any{int64(9007199254740995)}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := map[string]any{}
err := ParseJSON(tt.input, got)
require.NoError(t, err)
assert.Equal(t, tt.expect, got)
})
}
}
func TestParseFile(t *testing.T) {
input := "name1=path1"
expect := map[string]any{

Loading…
Cancel
Save