When working with JSON in Go, it’s common to encounter numeric values represented both as numbers and as strings. This inconsistency can lead to issues during unmarshaling, as Go’s encoding/json package expects a consistent type. Fortunately, Go provides the json.Number type to handle such scenarios gracefully.

The Challenge

Consider the following JSON snippets:

{"value": 123}
{"value": "123"}

In the first example, value is a number, while in the second, it’s a string. If you define your Go struct as:

type Data struct {
    Value float64 `json:"value"`
}

Unmarshaling the first JSON will succeed, but the second will fail with an error:

json: cannot unmarshal string into Go struct field Data.value of type float64

Alternatively, using the string tag will allow the second JSON to unmarshal successfully, but the first will now fail

The solution: json.Number

Go’s encoding/json package provides the json.Number type, which is a string representation of a JSON number. It implements the UnmarshalJSON interface and can handle both quoted and unquoted numbers.

By defining your struct as:

type Data struct {
    Value json.Number `json:"value"`
}

you can unmarshal both JSON examples without errors. You can then convert json.Number to the desired numeric type using its methods

// If you want to convert to a float: 
num, err := data.Value.Float64()

// If you want to convert to an int64:
num, err := data.Value.Int64()

This approach provides flexibility in handling numeric values that may come as strings or numbers.

Handling inconsistent numeric representations in JSON can be challenging, but Go’s json.Number type offers a robust solution. By leveraging it or implementing custom unmarshaling, you can ensure your applications handle JSON data gracefully, regardless of how numbers are represented.

For more information, refer to the official Go documentation on encoding/json.