Go语言:映射类型断言

7
我正在从JSON中读取数据结构。在此过程中进行了一些转换,最终我得到了一个struct,其中一个字段的类型是interface{}。实际上它是一个map,因此JSON将其放在了map[string]inteface{}中。
我知道底层结构实际上是map[string]float64,我想要像这样使用它,所以我尝试进行断言。以下代码重现了这种行为:
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)
}

我遇到了这个错误:

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

我可以做以下事情:
func main() {
    // A first assertion
    res := jsonMap().(map[string]interface{})

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

    fmt.Println(myMap)
}

这段代码可以正常运行,但是我觉得它很丑陋,因为我需要重构整个映射并使用两个断言。有没有一种正确的方法来强制第一个断言放弃 interface{} 并使用 float64?换句话说,如何以正确的方式执行原始断言 .(map[string]float64)编辑: 我要解析的实际数据看起来像这样:
[
 {"Type":"pos",
 "Content":{"x":0.5 , y: 0.3}} ,

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

在Go语言中,我使用structencoding/json实现以下功能。
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)

1
那么为什么您不使用map[string]float64作为字段类型,而是使用interface{}呢? - Danilo
因为我从JSON解析的数据不仅仅是一种类型,它也可以是一个“字符串”或一个“整数”。我应该重新表述,“有时它可能是一个映射,当它是一个映射时,JSON将其放在'map[string]interface{}'中。” - rodrigolece
在解析JSON之前,您知道它将是什么类型吗? - Danilo
你能提供一个JSON数据样本和你要解析的结构体吗?从你的例子中不太清楚你想实现什么。 - s7anley
不,我事先不知道。@s7anley,我编辑了我的评论以显示实际数据。 - rodrigolece
类型断言行不通,你必须实际循环遍历 map[string]interface{} 并创建一个带有浮点键的映射。这是因为 map[string]interface{}map[string]float64 在内存中实际上具有不同的表示方式;它不能将一个视为另一个。 (而 encoding/json 使映射 interface{} 键,因为 Go 在创建 map 时不知道键的类型将是什么。) - twotwotwo
1个回答

8

您无法将map[string]interface{}断言为map[string]float64。 您需要手动创建新的映射。

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
}

您确定不能将Content定义为map[string]float64吗?请看下面的示例。如果不行,那么您如何知道首先可以进行类型转换呢?

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

var response response
err := json.Unmarshal([]byte(exampleResponseData), &response)

是的,我认为我可以做一些操作来将“Content”定义为“map[string]float64”。这样做会节省新地图,并且非常值得。谢谢! - rodrigolece

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接