Konwertuj interfejs {} na int

100

Próbuję uzyskać wartość z JSON i rzutować ją na int, ale to nie działa i nie wiem, jak to zrobić poprawnie.

Oto komunikat o błędzie:

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

A kod:

    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
Nacięcie
źródło

Odpowiedzi:

202

Zamiast

iAreaId := int(val)

chcesz asercji typu :

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

Powodem, dla którego nie można przekonwertować wartości wpisanej w interfejsie, są następujące reguły w przywoływanych częściach specyfikacji:

Konwersje to wyrażenia formularza, w T(x)którym Tjest typem i xjest wyrażeniem, które można przekonwertować na typ T.

...

Niestałą wartość x można przekonwertować na typ T w każdym z następujących przypadków:

  1. x można przypisać do T.
  2. x i T mają identyczne typy bazowe.
  3. x i T są nienazwanymi typami wskaźników, a ich podstawowe typy wskaźników mają identyczne typy podstawowe.
  4. x i T są typami całkowitymi lub zmiennoprzecinkowymi.
  5. Typ x i T są typami złożonymi.
  6. x jest liczbą całkowitą lub fragmentem bajtów lub run, a T jest typem ciągu.
  7. x to łańcuch, a T to kawałek bajtów lub run.

Ale

iAreaId := int(val)

nie jest żadnym z przypadków 1.-7.

zzzz
źródło
Dobra odpowiedź! Specyfikacja językowa jest zawsze najlepszym miejscem do szukania odpowiedzi!
Hot.PxL
Dzięki. to piękna odpowiedź.
Muktadir,
30

Zakładam: jeśli wysłałeś wartość JSON przez przeglądarkę, to każda wysłana liczba będzie typu float64, więc nie możesz uzyskać wartości bezpośrednio w golang.

Zrób więc konwersję jak:

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

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

W ten sposób możesz uzyskać dokładną wartość, jaką chciałeś.

Mujibur
źródło
Masz 100% racji @Mujibur, ale z dowolnego powodu, ponieważ w specyfikacjach JSON jest typ liczby całkowitej
kamal
@kamal to dlatego, że JSON używa składni i definicji Javascript. JavaScript obsługuje tylko 64-bitowe liczby zmiennoprzecinkowe. Ref # javascript.info/number
Mujibur
4

Dodanie kolejnej odpowiedzi, która używa switch... Istnieją bardziej wyczerpujące przykłady, ale to da ci pomysł.

Na przykład tstaje się określonym typem danych w każdym casezakresie. Uwaga, musisz podać casetylko jeden typ w typie, w przeciwnym razie tpozostaje 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)
}
openwonk
źródło
Dla case string, można użyć strconv.ParseFloat(t, 32), a następnie rzucić wynik doint
JVE999
3

I całym sercem zgadzam się z zzzz „s typu twierdzenie odpowiedź, a ja zdecydowanie wolę ten sposób nad innymi. To powiedziawszy, oto, co musiałem zrobić, gdy preferowana metoda nie zadziałała ... (długa historia związana z cross-serializacją danych). Możesz nawet połączyć to w switchinstrukcję z case errInt == nili podobnymi wyrażeniami.

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

Jak powiedziałem powyżej, spróbuj najpierw potwierdzić typ, zanim spróbujesz w ten sposób.

openwonk
źródło
Jak już wspomniano, jeśli analizujesz JSON, wartość będzie zmiennoprzecinkowa. W takim przypadku użyj strconv.ParseFloat(v.(string), 64)zamiast tego. Na przykład możesz chcieć zmienić nazwy zmiennych errFloat.
openwonk
0

Aby lepiej zrozumieć konwersję typów, spójrz na poniższy kod:

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

Ten kod działa doskonale i konwertuje typ interfejsu na typ int

Dla wyrażenia x typu interfejsu i typu T, pierwotne wyrażenie x. (T) stwierdza, że ​​x nie jest zerowe i że wartość przechowywana w x jest typu T. Notacja x. (T) nazywana jest asercją typu . Dokładniej, jeśli T nie jest typem interfejsu, x. (T) stwierdza, że ​​typ dynamiczny x jest identyczny z typem T. W tym przypadku T musi implementować typ (interfejs) x; w przeciwnym razie potwierdzenie typu jest niepoprawne, ponieważ x nie może przechowywać wartości typu T. Jeśli T jest typem interfejsu, x. (T) zapewnia, że ​​typ dynamiczny x implementuje interfejs T.

Wracając do twojego kodu, to

iAreaId := val.(int)

powinno działać dobrze. Jeśli chcesz sprawdzić, czy wystąpił błąd podczas konwersji, możesz również ponownie napisać powyższą linię jako

iAreaId, ok := val.(int)

Bijendra Kumar
źródło
0

Napisałem bibliotekę, która może pomóc w konwersji typów 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]
Daniel Krom
źródło
0

Najprościej to zrobiłem. Nie jest to najlepszy sposób, ale najprostszy sposób, jaki znam.

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
}
Godfrey
źródło
0

Musisz wykonać asercję typu, aby przekonwertować interfejs {} na wartość int.

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

Więcej informacji jest dostępnych .

Kabeer Shaikh
źródło
0

może potrzebujesz

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
}
aierui
źródło
-2

Najlepiej unikać rzutowania, deklarując f jako poprawny typ, odpowiadający JSON.

Amnon
źródło