When trying to parse a json with a float value for distance to the following struct
type CreateBookingRequest struct {
Distance float64 `json:"distance,string"`
DistanceSource string `json:"distanceSource"`
}
I get the following error
json: invalid use of ,string struct tag, trying to unmarshal unquoted value into [34 100 105 115 116 97 110 99 101 34]%!(EXTRA *reflect.rtype=dto.CreateBookingRequest)
Is there a way for me to avoid the error/get a better error message?
Edit: I am actually expecting the users of the API to pass in a string value but if they for some reason pass in a non-string value, I would like to be able to tell them clearly, instead of this hard to read error message.
This error happens when the "distance" JSON value is encoded as a number instead of a string (per the "string" tag on the "Distance") field:
str := []byte(`{"distance":1.23,"distanceSource":"foo"}`)
// Note JSON number -------^
var cbr CreateBookingRequest
err := json.Unmarshal(str, &cbr)
// err => json: invalid use of ,string struct tag, trying to unmarshal unquoted value into [34 100 105 115 116 97 110 99 101 34]%!(EXTRA *reflect.rtype=main.CreateBookingRequest)
If you change the type of the distance value to a string (per the tag) then it works fine:
str := []byte(`{"distance":"1.23","distanceSource":"foo"}`)
// Note JSON string -------^
You could change the error message by identifying that specific error somehow and provide a different message. You might also consider changing the tag for the Distance type to simply accept a number instead of a string:
type CreateBookingRequest struct {
Distance float64 `json:"distance"`
...
}
...
str := []byte(`{"distance":1.23,"distanceSource":"foo"}`)
The error is simply saying you designated Distance
as a string with your json annotations but in the json string you're trying to deserialize the value is not quoted (therefor not a string).
The solution is simple, either change this json:"distance,string"
to json:"distance"
or get json that matches your definition (meaning it has distince in quotes like "Distance":"10.4"
)
Given, the error and the fact that your native Go type is a float64 I would advise getting rid of the string annotation.
func unmarshal state:
To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match.
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
So, unmarshal expecting Distance should be float64
by default. But as per tag, you are requesting unmarshal to except Distance as string
. Here is data type missing matches.
So you have two options, either you change distance tag with float64 or marshal distance as string.
I had to work with an API which sometimes quotes numbers and sometimes doesn't. The owners of the service weren't likely to fix it, so I came up with a simple workaround:
re := regexp.MustCompile(`(":\s*)([\d\.]+)(\s*[,}])`)
rawJsonByteArray = re.ReplaceAll(rawJsonByteArray, []byte(`$1"$2"$3`))
Regular expressions are somewhat inefficient, but I don't believe I'd be able to implement something substantially faster.