Golang Cannot unmarshal string into Go value of type int(solved)

itachi sasuke
itachi sasuke
i.
Apr 24, 2021 3 min read

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