Skip to content

Conversation

@phughes-scwx
Copy link

@phughes-scwx phughes-scwx commented Aug 1, 2025

What

Adds a case to the dataconv.Marshal type switch that retains values passed as json.Number as their underlying string value.

Why

In instances where a user is consuming raw JSON without a predetermined struct for unmarshaling, they often unmarshal into a map[string]interface{}. When doing this all numbers are converted to floats, even if they have no fractional part. Eg:

var v map[string]any
json.Unmarshal([]byte(`{"value":6}`), &v)
fmt.Printf("%T", v["value"])
// float64

Therefore a user, in a situation where they are unmarshaling deeply-nested JSON into a map[string]interface{} and then marshaling using dataconv.Marshal for use in Starlark, can expect integers to be misidentified as floats. Checks inside the script will fail to distinguish between integers and floats.

To simplify this situation, we ask the Go JSON decoder to use json.Number values for all numerics.

var v map[string]any
dec := json.NewDecoder(bytes.NewReader([]byte(`{"value":6}`)))
dec.UseNumber()
dec.Decode(&v)
fmt.Printf("%T", v["value"])
// json.Number

If dataconv.Marshal converts the value to a string representation, then we can handle the values differently based on str.isdigit() and str.isdecimal() within the script.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant