Converter interface {} em int

97

Estou tentando obter um valor de um JSON e convertê-lo em int, mas não funciona e não sei como fazer isso corretamente.

Aqui está a mensagem de erro:

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

E o código:

    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
usuario
fonte

Respostas:

190

Ao invés de

iAreaId := int(val)

você quer uma declaração de tipo :

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

O motivo pelo qual você não pode converter um valor digitado pela interface são estas regras nas partes de especificações referenciadas:

Conversões são expressões da forma em T(x)que Té um tipo e xé uma expressão que pode ser convertida para o tipo T.

...

Um valor não constante x pode ser convertido para o tipo T em qualquer um destes casos:

  1. x é atribuível a T.
  2. O tipo de x e T têm tipos subjacentes idênticos.
  3. O tipo de x e T são tipos de ponteiro sem nome e seus tipos de base de ponteiro têm tipos subjacentes idênticos.
  4. O tipo de x e T são tipos inteiros ou de ponto flutuante.
  5. O tipo de x e T são tipos complexos.
  6. x é um número inteiro ou uma fatia de bytes ou runas e T é um tipo de string.
  7. x é uma string e T é uma fatia de bytes ou runas.

Mas

iAreaId := int(val)

não é nenhum dos casos 1.-7.

zzzz
fonte
Boa resposta! As especificações de idioma são sempre o melhor lugar para procurar uma resposta!
Hot.PxL
Obrigado. é uma bela resposta.
Muktadir
29

Estou assumindo: se você enviou o valor JSON por meio do navegador, qualquer número enviado será do tipo float64, portanto, você não pode obter o valor diretamente em golang.

Então faça a conversão como:

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

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

Desta forma, você pode obter o valor exato que deseja.

Mujibur
fonte
Você está 100% certo @Mujibur, mas qualquer motivo, já que há tipo inteiro nas especificações JSON
kamal
@kamal é porque JSON usa sintaxe e definições Javascript. JavaScript suporta apenas números de ponto flutuante de 64 bits. Ref # javascript.info/number
Mujibur
4

Adicionando outra resposta que usa switch... Existem exemplos mais abrangentes por aí, mas isso lhe dará uma ideia.

Por exemplo, ttorna-se o tipo de dados especificado em cada caseescopo. Observe que você deve fornecer um casepara apenas um tipo em cada tipo, caso contrário, tpermanece um 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
fonte
Para case string, você pode usar strconv.ParseFloat(t, 32)e, em seguida, lançar o resultado para umint
JVE999
3

Eu concordo plenamente com a resposta de afirmação de tipo de zzzz e prefiro fortemente essa forma a outras. Dito isso, aqui está o que tive de fazer quando o método preferido não funcionou ... (longa história relacionada à serialização cruzada de dados). Você pode até mesmo encadear isso em uma switchdeclaração com case errInt == nilexpressões semelhantes.

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

Como eu disse acima, tente digitar a asserção antes de tentar dessa forma.

openwonk
fonte
Conforme apontado, ao analisar JSON, o valor será flutuante. Nesse caso, use em strconv.ParseFloat(v.(string), 64)vez disso. Você pode querer alterar os nomes das variáveis ​​também, por exemplo errFloat.
openwonk
0

Para entender melhor a conversão de tipo, observe o código abaixo:

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

Este código executa perfeitamente e converte o tipo de interface em tipo interno

Para uma expressão x do tipo de interface e um tipo T, a expressão primária x. (T) afirma que x não é nulo e que o valor armazenado em x é do tipo T. A notação x. (T) é chamada de asserção de tipo . Mais precisamente, se T não for um tipo de interface, x. (T) afirma que o tipo dinâmico de x é idêntico ao tipo T. Nesse caso, T deve implementar o tipo (interface) de x; caso contrário, a afirmação de tipo é inválida, pois não é possível para x armazenar um valor do tipo T. Se T for um tipo de interface, x. (T) afirma que o tipo dinâmico de x implementa a interface T.

Voltando ao seu código, este

iAreaId := val.(int)

deve funcionar bem. Se você quiser verificar o erro ocorrido durante a conversão, você também pode reescrever a linha acima como

iAreaId, ok := val.(int)

Bijendra Kumar
fonte
0

Eu escrevi uma biblioteca que pode ajudar com conversões de tipo 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
fonte
0

Maneira mais simples de fazer isso. Não é a melhor maneira, mas a maneira mais simples que conheço.

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
}
Mo-Gang
fonte
0

Você precisa fazer uma declaração de tipo para converter sua interface {} para o valor int.

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

Mais informações disponíveis .

Kabeer Shaikh
fonte
0

talvez você precise

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
fonte
-2

Melhor evitar a conversão declarando f como o tipo correto para corresponder ao JSON.

Amnon
fonte