Convert interface{} to int
Asked Answered
B

12

151

I'm trying to get a value from a JSON and cast it to int but it doesn't work, and I don't know how to do it properly.

Here is the error message:

...cannot convert val (type interface {}) to type int: need type assertion

And the code:

    var f interface{}
    err = json.Unmarshal([]byte(jsonStr), &f)
    if err != nil {
        utility.CreateErrorResponse(w, "Error: failed to parse JSON data.")
        return
    }

    m := f.(map[string]interface{})

    val, ok := m["area_id"]
    if !ok {
        utility.CreateErrorResponse(w, "Error: Area ID is missing from submitted data.")
        return
    }

    fmt.Fprintf(w, "Type = %v", val)   // <--- Type = float64
    iAreaId := int(val)                // <--- Error on this line.
    testName := "Area_" + iAreaId      // not reaching here
Britanybritches answered 4/8, 2013 at 9:0 Comment(0)
B
285

Instead of

iAreaId := int(val)

you want a type assertion:

iAreaId := val.(int)
iAreaId, ok := val.(int) // Alt. non panicking version 

The reason why you cannot convert an interface typed value are these rules in the referenced specs parts:

Conversions are expressions of the form T(x) where T is a type and x is an expression that can be converted to type T.

...

A non-constant value x can be converted to type T in any of these cases:

  1. x is assignable to T.
  2. x's type and T have identical underlying types.
  3. x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
  4. x's type and T are both integer or floating point types.
  5. x's type and T are both complex types.
  6. x is an integer or a slice of bytes or runes and T is a string type.
  7. x is a string and T is a slice of bytes or runes.

But

iAreaId := int(val)

is not any of the cases 1.-7.

Buote answered 4/8, 2013 at 9:32 Comment(1)
Good answer! Language spec is always the best place to look for an answer!Euripides
T
61

I am assuming: If you sent the JSON value through browser then any number you sent that will be the type float64 so you cant get the value directly int in golang.

So do the conversion like:

//As that says: 
fmt.Fprintf(w, "Type = %v", val) // <--- Type = float64

var iAreaId int = int(val.(float64))

This way you can get exact value what you wanted.

Tertial answered 17/4, 2015 at 4:30 Comment(3)
You are 100% right @Mujibur, but any reason, as there is integer type in JSON specsBarong
@Barong that's because JSON, uses Javascript syntax and definitions. JavaScript only supports 64-bit floating point numbers. Ref# javascript.info/numberTertial
This may lose precisionIndictable
I
7

Adding another answer that uses switch... There are more comprehensive examples out there, but this will give you the idea.

In example, t becomes the specified data type within each case scope. Note, you have to provide a case for only one type at a type, otherwise t remains an interface.

package main

import "fmt"

func main() {
    var val interface{} // your starting value
    val = 4

    var i int // your final value

    switch t := val.(type) {
    case int:
        fmt.Printf("%d == %T\n", t, t)
        i = t
    case int8:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case int16:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case int32:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case int64:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case bool:
        fmt.Printf("%t == %T\n", t, t)
        // // not covertible unless...
        // if t {
        //  i = 1
        // } else {
        //  i = 0
        // }
    case float32:
        fmt.Printf("%g == %T\n", t, t)
        i = int(t) // standardizes across systems
    case float64:
        fmt.Printf("%f == %T\n", t, t)
        i = int(t) // standardizes across systems
    case uint8:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case uint16:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case uint32:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case uint64:
        fmt.Printf("%d == %T\n", t, t)
        i = int(t) // standardizes across systems
    case string:
        fmt.Printf("%s == %T\n", t, t)
        // gets a little messy...
    default:
        // what is it then?
        fmt.Printf("%v == %T\n", t, t)
    }

    fmt.Printf("i == %d\n", i)
}
Impoverished answered 16/10, 2018 at 0:59 Comment(1)
For case string, you can use strconv.ParseFloat(t, 32) and then cast the result to an intMady
I
5

I whole-heartedly agree with zzzz's type assertion answer and I strongly prefer that way over others. That said, here's what I've had to do when the preferred method has not worked... (long story related to cross-serialization of data). You can even chain this into a switch statement with case errInt == nil and similar expressions.

package main

import "fmt"
import "strconv"

func main() {
    var v interface{}
    v = "4"

    i, errInt := strconv.ParseInt(v.(string), 10, 64)

    if errInt == nil {
        fmt.Printf("%d is a int", i)
        /* do what you wish with "i" here */
    }
}

Like I said above, try type assertion first before trying this way.

Impoverished answered 4/7, 2015 at 8:40 Comment(1)
As pointed out, if parsing JSON, value will be float. In that case, use strconv.ParseFloat(v.(string), 64) instead. You may want to change variable names too, say errFloat.Impoverished
B
3

maybe you need

func TransToString(data interface{}) (res string) {
    switch v := data.(type) {
    case float64:
        res = strconv.FormatFloat(data.(float64), 'f', 6, 64)
    case float32:
        res = strconv.FormatFloat(float64(data.(float32)), 'f', 6, 32)
    case int:
        res = strconv.FormatInt(int64(data.(int)), 10)
    case int64:
        res = strconv.FormatInt(data.(int64), 10)
    case uint:
        res = strconv.FormatUint(uint64(data.(uint)), 10)
    case uint64:
        res = strconv.FormatUint(data.(uint64), 10)
    case uint32:
        res = strconv.FormatUint(uint64(data.(uint32)), 10)
    case json.Number:
        res = data.(json.Number).String()
    case string:
        res = data.(string)
    case []byte:
        res = string(v)
    default:
        res = ""
    }
    return
}
Blende answered 8/7, 2020 at 5:59 Comment(0)
W
3

You can use reflect to help you determine the type and then convert.

func i2num(a interface{}) (interface{}, error) { // interface to number
    aValue := reflect.ValueOf(a)
    switch aValue.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        return aValue.Int(), nil
    case reflect.Float32, reflect.Float64:
        return aValue.Float(), nil
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        return aValue.Uint(), nil
    case reflect.Bool:
        if a == true {
            return 1, nil
        }
        return 0, nil
    case reflect.String:
        return strconv.ParseFloat(aValue.String(), 64)
    default:
        return nil, errors.New("type error")
    }
}

Go Playground

Widget answered 4/7, 2021 at 18:5 Comment(0)
P
1

You need to do type assertion for converting your interface{} to int value.

iAreaId := val.(int)
iAreaId, ok := val.(int)

More information is available.

Pot answered 23/1, 2018 at 2:59 Comment(0)
L
1

use cast.ToInt(anyValue)

I needed that, because the nasty endpoint which I need to use has a bug and sometimes is returning an integer as a string, sometimes as float64

https://github.com/spf13/cast

Licking answered 13/1, 2023 at 9:12 Comment(0)
G
0

To better understand the type conversion, look at the code below:

package main
import "fmt"
func foo(a interface{}) {
    fmt.Println(a.(int))  // conversion of interface into int
}
func main() {
    var a int = 10
    foo(a)
}

This code executes perfectly and converts interface type to int type

For an expression x of interface type and a type T, the primary expression x.(T) asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion. More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.

Going back to your code, this

iAreaId := val.(int)

should work good. If you want to check error occured while conversion, you can also re-write above line as

iAreaId, ok := val.(int)

Ganoid answered 17/10, 2017 at 10:26 Comment(0)
B
0

I wrote a library that can help with type convertions https://github.com/KromDaniel/jonson

js := jonson.New([]interface{}{55.6, 70.8, 10.4, 1, "48", "-90"})

js.SliceMap(func(jsn *jonson.JSON, index int) *jonson.JSON {
    jsn.MutateToInt()
    return jsn
}).SliceMap(func(jsn *jonson.JSON, index int) *jonson.JSON {
    if jsn.GetUnsafeInt() > 50{
        jsn.MutateToString()
    }
    return jsn
}) // ["55","70",10,1,48,-90]
Bacchanalia answered 14/5, 2018 at 9:29 Comment(0)
P
0

Simplest way I did this. Not the best way but simplest way I know how.

import "fmt"

func main() {
    fmt.Print(addTwoNumbers(5, 6))
}

func addTwoNumbers(val1 interface{}, val2 interface{}) int {
    op1, _ := val1.(int)
    op2, _ := val2.(int)

    return op1 + op2
}
Pillbox answered 20/6, 2018 at 10:22 Comment(0)
N
-2

Best avoid casting by declaring f to be f the correct type to correspond to the JSON.

Naked answered 7/1, 2018 at 21:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.