Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go: type assertion for maps

I'm reading data structures from JSON. There's a little bit of conversions going on and at the end I have a struct where one of the fields is of type interface{}. It's actually a map, so JSON puts it inside a map[string]inteface{}.

I actually know that the underlying structure is map[string]float64 and I would like to use it like that, so I try to do an assertion. The following code reproduces the behaviour:

type T interface{}

func jsonMap() T {
    result := map[string]interface{}{
        "test": 1.2,
    }
    return T(result)
}

func main() {
    res := jsonMap()

    myMap := res.(map[string]float64)

    fmt.Println(myMap)
}

I get the error:

panic: interface conversion: main.T is map[string]interface {}, not map[string]float64

I can do the following:

func main() {
    // A first assertion
    res := jsonMap().(map[string]interface{})

    myMap := map[string]float64{
        "test": res["test"].(float64), // A second assertion
    }

    fmt.Println(myMap)
}

This works fine, but I find it very ugly since I need to reconstruct the whole map and use two assertions. Is there a correct way to force the first assertion to drop the interface{} and use float64? In other words, what is the correct way to do the original assertion .(map[string]float64)?

Edit:

The actual data I'm parsing looks like this:

[
 {"Type":"pos",
 "Content":{"x":0.5 , y: 0.3}} ,

{"Type":"vel",
"Content":{"vx": 0.1, "vy": -0.2}}
]

In Go I use a struct and encoding/json in the following way.

type data struct {
    Type string
    Content interface{}
}

// I read the JSON from a WebSocket connection
_, event, _ := c.ws.ReadMessage()

j := make([]data,0)
json.Unmarshal(event, &j)
like image 222
rodrigolece Avatar asked Feb 29 '16 18:02

rodrigolece


People also ask

How do you type assertions in go?

Type assertion in Goi , which is the variable whose type we are asserting. This variable must be defined as an interface. type , which is the type we are asserting our variable is (such as string, int, float64, etc) t , which is a variable that stores the value of our variable i , if our type assertion is correct.

How do you type typescript assert?

However, there is another way to do type assertion, using the 'as' syntax. let code: any = 123; let employeeCode = code as number; Both the syntaxes are equivalent and we can use any of these type assertions syntaxes.

What is MAP string Golang?

Golang Maps is a collection of unordered pairs of key-value. It is widely used because it provides fast lookups and values that can retrieve, update or delete with the help of keys. It is a reference to a hash table.

What is assertion in Golang?

Golang provides a simple way of checking if an interface value is of a specific type. This is called type assertion, and it is generally used to make sure that an interface value satisfies another interface or to find the concrete type of the interface.


1 Answers

You cannot type assert map[string]interface{} to map[string]float64. You need to manually create new map.

package main

import (
    "encoding/json"
    "fmt"
)

var exampleResponseData = `{
        "Data":[
            {
                "Type":"pos",
                "Content":{
                    "x":0.5,
                    "y":0.3
                }
            },
            {
                "Type":"vel",
                "Content":{
                    "vx":0.1,
                    "vy":-0.2
                }
            }
        ]
    }`

type response struct {
    Data []struct {
        Type    string
        Content interface{}
    }
}

func main() {
    var response response
    err := json.Unmarshal([]byte(exampleResponseData), &response)
    if err != nil {
        fmt.Println("Cannot process not valid json")
    }

    for i := 0; i < len(response.Data); i++ {
        response.Data[i].Content = convertMap(response.Data[i].Content)
    }
}

func convertMap(originalMap interface{}) map[string]float64 {
    convertedMap := map[string]float64{}
    for key, value := range originalMap.(map[string]interface{}) {
        convertedMap[key] = value.(float64)
    }

    return convertedMap
}

Are you sure you cannot define Content as map[string]float64? See example below. If not, how can you know that you can cast it in the first place?

type response struct {
    Data []struct {
        Type    string
        Content map[string]float64
    }
}

var response response
err := json.Unmarshal([]byte(exampleResponseData), &response)
like image 123
s7anley Avatar answered Oct 03 '22 08:10

s7anley