Преобразовать интерфейс {} в int

Я пытаюсь получить значение из JSON и привести его к int, но оно не работает, и я не знаю, как это сделать правильно.

Вот сообщение об ошибке:

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

И код:

    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

12 ответов

Решение

Вместо

iAreaId := int(val)

Вы хотите утверждение типа:

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

Причиной, по которой вы не можете преобразовать типизированное значение интерфейса, являются следующие правила в указанных частях спецификации:

Конверсии - это выражения вида T(x) где T это тип и x является выражением, которое можно преобразовать в тип T.

...

Непостоянное значение x может быть преобразовано в тип T в любом из следующих случаев:

  1. х присваивается Т.
  2. Тип X и T имеют идентичные базовые типы.
  3. Тип x и T являются безымянными типами указателей, и их базовые типы указателей имеют идентичные базовые типы.
  4. Тип x и T оба являются целочисленными или с плавающей точкой.
  5. Тип x и T оба являются сложными типами.
  6. x - целое число или фрагмент байтов или рун, а T - строковый тип.
  7. x - это строка, а T - фрагмент байтов или рун.

Но

iAreaId := int(val)

не является ни одним из случаев 1.-7.

Я предполагаю: если вы отправили значение JSON через браузер, то любое отправленное вами число будет иметь тип float64, поэтому вы не можете получить значение непосредственно через int в golang.

Так что преобразование, как:

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

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

Таким образом, вы можете получить точное значение, что вы хотели.

Добавление другого ответа, который использует switch... Есть более полные примеры, но это даст вам идею.

В примере t становится указанным типом данных в каждом case объем. Обратите внимание, вы должны предоставить case только для одного типа в типе, в противном случае t остается 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)
}

Я искренне согласен с zzzz ответом zzzz, и я настоятельно предпочитаю этот способ, чем другим. Тем не менее, вот что я должен был сделать, когда предпочтительный метод не работал... (длинная история, связанная с кросс-сериализацией данных). Вы даже можете связать это в switch заявление с case errInt == nil и подобные выражения.

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 */
    }
}

Как я уже говорил выше, попробуйте сначала ввести утверждение, прежде чем пытаться таким образом.

Вы можете использовать отражение, чтобы помочь вам определить тип, а затем преобразовать.

      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")
    }
}

Перейти на игровую площадку

Может тебе нужно

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
}

использоватьcast.ToInt(anyValue)

Мне это нужно, потому что неприятная конечная точка, которую мне нужно использовать, имеет ошибку и иногда возвращает целое число в виде строки, иногда как float64.

https://github.com/spf13/cast

Вам нужно сделать утверждение типа для преобразования вашего интерфейса {} в значение типа int.

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

Более подробная информация доступна.

Самый простой способ, которым я сделал это. Не самый лучший, но самый простой способ, которым я умею.

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
}

Чтобы лучше понять преобразование типов, посмотрите на код ниже:

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)
}

Этот код выполняется отлично и преобразует тип интерфейса в тип int

Для выражения x типа интерфейса и типа T первичное выражение x.(T) утверждает, что x не равно nil и что значение, хранящееся в x, имеет тип T. Обозначение x.(T) называется утверждением типа, Точнее, если T не является интерфейсным типом, x.(T) утверждает, что динамический тип x идентичен типу T. В этом случае T должен реализовать тип (interface) x; в противном случае утверждение типа является недопустимым, поскольку для x невозможно сохранить значение типа T. Если T является типом интерфейса, x.(T) утверждает, что динамический тип x реализует интерфейс T.

Возвращаясь к вашему коду, это

iAreaId := val.(int)

должно работать хорошо. Если вы хотите проверить, произошла ли ошибка во время преобразования, вы также можете переписать вышеуказанную строку как

iAreaId, ok := val.(int)

Я написал библиотеку, которая может помочь с преобразованием типов 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]

Лучше всего избегать приведения, объявляя f правильным f, чтобы соответствовать JSON.

Другие вопросы по тегам