Convierta la interfaz {} a int

97

Estoy tratando de obtener un valor de un JSON y convertirlo en int, pero no funciona y no sé cómo hacerlo correctamente.

Aquí está el mensaje de error:

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

Y el codigo:

    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
Mella
fuente

Respuestas:

193

En vez de

iAreaId := int(val)

quieres una afirmación de tipo :

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

La razón por la que no puede convertir un valor escrito en la interfaz son estas reglas en las partes de especificaciones a las que se hace referencia:

Las conversiones son expresiones de la forma T(x)where Tes un tipo y xes una expresión que se puede convertir al tipo T.

...

Un valor no constante x se puede convertir al tipo T en cualquiera de estos casos:

  1. x es asignable a T.
  2. El tipo de x y T tienen tipos subyacentes idénticos.
  3. El tipo de x y T son tipos de puntero sin nombre y sus tipos de base de puntero tienen tipos subyacentes idénticos.
  4. El tipo de x y T son tipos enteros o de coma flotante.
  5. El tipo de x y T son tipos complejos.
  6. x es un número entero o una porción de bytes o runas y T es un tipo de cadena.
  7. x es una cadena y T es una porción de bytes o runas.

Pero

iAreaId := int(val)

no es ninguno de los casos 1.-7.

zzzz
fuente
¡Buena respuesta! ¡La especificación de idioma es siempre el mejor lugar para buscar una respuesta!
Hot.PxL
Gracias. es una hermosa respuesta.
Muktadir
29

Supongo: si envió el valor JSON a través del navegador, cualquier número que envió será del tipo float64, por lo que no puede obtener el valor directamente int en golang.

Entonces haz la conversión como:

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

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

De esta forma puede obtener el valor exacto que deseaba.

Mujibur
fuente
Tiene 100% razón @Mujibur, pero por cualquier motivo, ya que hay un tipo de entero en las especificaciones JSON
Kamal
@kamal eso es porque JSON, usa sintaxis y definiciones de Javascript. JavaScript solo admite números de punto flotante de 64 bits. Ref # javascript.info/number
Mujibur
4

Agregando otra respuesta que usa switch... Hay ejemplos más completos, pero esto le dará la idea.

Por ejemplo, se tconvierte en el tipo de datos especificado dentro de cada caseámbito. Tenga en cuenta que debe proporcionar un casesolo para un tipo en un tipo; de lo contrario, tsigue siendo un 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
fuente
Para case string, puede usar strconv.ParseFloat(t, 32)y luego int
enviar
3

Estoy totalmente de acuerdo con el tipo de respuesta de aserción de zzzz y prefiero firmemente esa forma sobre otras. Dicho esto, esto es lo que tuve que hacer cuando el método preferido no funcionó ... (larga historia relacionada con la serialización cruzada de datos). Incluso puede encadenar esto en una declaración con expresiones similares.switchcase 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 */
    }
}

Como dije anteriormente, primero intente escribir aserción antes de intentarlo de esta manera.

openwonk
fuente
Como se señaló, si se analiza JSON, el valor será flotante. En ese caso, utilice strconv.ParseFloat(v.(string), 64)en su lugar. Es posible que también desee cambiar los nombres de las variables, por ejemplo errFloat.
openwonk
0

Para comprender mejor la conversión de tipos, consulte el siguiente código:

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 se ejecuta perfectamente y convierte el tipo de interfaz en tipo int

Para una expresión x de tipo interfaz y un tipo T, la expresión primaria x. (T) afirma que x no es nulo y que el valor almacenado en x es de tipo T. La notación x. (T) se llama aserción de tipo . Más precisamente, si T no es un tipo de interfaz, x. (T) afirma que el tipo dinámico de x es idéntico al tipo T. En este caso, T debe implementar el tipo (de interfaz) de x; de lo contrario, la aserción de tipo no es válida, ya que no es posible que x almacene un valor de tipo T. Si T es un tipo de interfaz, x. (T) afirma que el tipo dinámico de x implementa la interfaz T.

Volviendo a su código, este

iAreaId := val.(int)

debería funcionar bien. Si desea verificar que se produjo un error durante la conversión, también puede volver a escribir la línea anterior como

iAreaId, ok := val.(int)

Bijendra Kumar
fuente
0

Escribí una biblioteca que puede ayudar con las conversiones de tipos 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
fuente
0

La forma más sencilla en que hice esto. No es la mejor forma, pero la forma más sencilla que conozco.

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
fuente
0

Necesita hacer una aserción de tipo para convertir su interfaz {} a un valor int.

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

Hay más información disponible .

Kabeer Shaikh
fuente
0

tal vez necesites

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

Es mejor evitar la conversión declarando que f es el tipo correcto para corresponder al JSON.

Amnón
fuente