Go:用于映射的类型断言

huangapple go评论71阅读模式
英文:

Go: type assertion for maps

问题

我正在从JSON中读取数据结构。在这个过程中进行了一些转换,最后我得到了一个struct,其中一个字段的类型是interface{}。实际上,它是一个map,所以JSON将它放在了map[string]interface{}中。

我知道底层结构实际上是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() {
    // 第一次断言
    res := jsonMap().(map[string]interface{})

    myMap := map[string]float64{
        "test": res["test"].(float64), // 第二次断言
    }

    fmt.Println(myMap)
}

这样可以正常工作,但我觉得这样做很丑陋,因为我需要重建整个map并使用两次断言。有没有一种正确的方法来强制第一次断言丢弃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{}
}

// 从WebSocket连接中读取JSON
_, event, _ := c.ws.ReadMessage()

j := make([]data, 0)
json.Unmarshal(event, &j)
英文:

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)

答案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("无法处理无效的 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)
英文:

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

package main

import (
	&quot;encoding/json&quot;
	&quot;fmt&quot;
)

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

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

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

	for i := 0; i &lt; 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), &amp;response)

huangapple
  • 本文由 发表于 2016年3月1日 02:10:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/35706501.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定