Sometimes your client side application could send a valid integer as a string while your application expect an integer.
This leads you to getting the error json: cannot unmarshal string into Go struct field Item.item_id of type int
when you try to convert the json into a struct.
For example consider this snippet.
You can find the full code snippet on github
1
2
3
4
5
6
7
8
9
10
|
type Item struct {
Name string `json:"name"`
ItemId int `json:"item_id"`
}
jsonData:= []byte(`{"name":"item 1","item_id":"30"}`)
var item Item
err:=json.Unmarshal(jsonData,&item)
if err!=nil {
log.Fatal(err)
}
|
We would expect that our application will convert string “30” to integer 30 since it’s a valid integer.
However, we will get the following error instead:
json: cannot unmarshal string into Go struct field Item.item_id of type int
How to solve this Issue
The good news is golang allows us to write our own custom json marshaller and unmarshalers.
We can create our own type alias for int type and add a custom marshaller and unmarshaler for our json
this way we can check our data and convert our string to int before unmarshal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
// StringInt create a type alias for type int
type StringInt int
// UnmarshalJSON create a custom unmarshal for the StringInt
/// this helps us check the type of our value before unmarshalling it
func (st *StringInt) UnmarshalJSON(b []byte) error {
//convert the bytes into an interface
//this will help us check the type of our value
//if it is a string that can be converted into an int we convert it
///otherwise we return an error
var item interface{}
if err := json.Unmarshal(b, &item); err != nil {
return err
}
switch v := item.(type) {
case int:
*st = StringInt(v)
case float64:
*st = StringInt(int(v))
case string:
///here convert the string into
///an integer
i, err := strconv.Atoi(v)
if err != nil {
///the string might not be of integer type
///so return an error
return err
}
*st = StringInt(i)
}
return nil
}
func main() {
//We can now send our item as either an int or string without getting any error
type Item struct {
Name string `json:"name"`
ItemId StringInt `json:"item_id"`
}
jsonData := []byte(`{"name":"item 1","item_id":"30"}`)
var item Item
err := json.Unmarshal(jsonData, &item)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", item)
}
|
The output will now be:
{Name:item 1 ItemId:30}
We can now send ItemId json as a string without getting any error.
You can find the full code snippet on github