Golang接口到结构体的转换出现错误。

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

Golang Interface to struct conversion giving error

问题

我有一个以以下格式的字符串作为json:

{"add": [{"var": ["100"]}, "200"]}

这里的键'add'对于所有的json来说都不是一个固定的值。在某些情况下,它可以是'minus','multiply'等。
该键的值是一个数组。在这种情况下是[{"var": ["100"]}, "200"]。这意味着应该将100添加到现有值200上。

我正在尝试解析这个表达式。由于主键(在这种情况下是'add')不是一个常量,我无法将其转换为结构体。所以,我通过以下方式将其转换为json对象:

type mathExpVar struct {
    valueVar []string `json:"var"`
}
var mathExpJson map[string][]interface{}
var input = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
err := json.Unmarshal([]byte(input), &mathExpJson)
for operator, values := range mathExpJson{
    vals, ok := values[0].(mathExpVar) // 这里的values[0]将是{"var": ["100"]}
    if !ok{
        return nil
    }
}

这里'ok'总是返回false。我不确定为什么。没有额外的错误消息供我检查为什么失败。有人可以帮助我解决这个问题吗?

链接到相同的Go Playground:https://go.dev/play/p/POfQmEoPbjD

英文:

I have an json as a string of following format:

{"add": [{"var": ["100"]}, "200"]}

Here the key 'add' is not a constant value for all the jsons. In some cases, it can be 'minus', 'multiply' etc.
The value of that key is an array. In this case [{"var": ["100"]}, "200"]. This means that the 100 should be added to existing value 200.

I am trying to parse this expression. Since the main key(in this case 'add') is not a constant, I cannot convert into a struct. So, I converted it to a json object by following way:

type mathExpVar struct {
    valueVar []string `json:"var"`
}
var mathExpJson map[string][]interface{}
var input = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
err := json.Unmarshal([]byte(input), &mathExpJson)
for operator, values := range mathExpJson{
    vals, ok := values[0].(mathExpVar) // here values[0] will be {"var": ["100"]}
    if !ok{
        return nil
    }
}

Here 'ok' is always returning false. I am not sure why. There is no additional error message for me to check why this is failing. Could someone help me in resolving this?

Link to go playground for the same: https://go.dev/play/p/POfQmEoPbjD

答案1

得分: 1

一个工作示例:https://go.dev/play/p/02YzI5cv8vV

我认为Burak Serdar的原始回答无效的主要原因是没有考虑到你还需要处理其他参数。如果你仔细观察,你会发现这个表达式不是一个字符串数组,而是不同类型的。这个实现处理了自定义的解组,并将所有额外的参数存储在Extra字段中。

还有代码:

package main

import (
	"encoding/json"
	"log"
	"reflect"
)

const jsonPayload = `{"add": [{"var": ["100"]}, "200"]}`

func main() {

	data := MathExpressions{}
	err := json.Unmarshal([]byte(jsonPayload), &data)

	if err != nil {
		log.Println("Failed to unmarshal json, error:", err)
		return
	}

	log.Println(data)

	for operation, expression := range data {
		log.Print("Op:", operation, "Exp:", expression)
	}

	log.Println("Finished..")
}

/**
The sub definition for a specific expression in the object
*/
type ExpressionDefinition struct {
	Vars  []string `json:"var"`
	Extra []string
}

func (e *ExpressionDefinition) UnmarshalJSON(data []byte) error {
	tokens := make([]interface{}, 0)
	err := json.Unmarshal(data, &tokens)

	if err != nil {
		return err
	}

	for _, token := range tokens {
		log.Println("Processing token:", token, "type:", reflect.TypeOf(token))
		switch token.(type) {
		case map[string]interface{}:
			for _, v := range token.(map[string]interface{})["var"].([]interface{}) {
				e.Vars = append(e.Vars, v.(string))
			}
		case string:
			e.Extra = append(e.Extra, token.(string))
		}
	}

	log.Println(tokens)
	return nil
}

/**
The main expressions object which contains all the sub-expressions.
*/
type MathExpressions map[string]ExpressionDefinition

这段代码是一个工作示例,它演示了如何处理具有不同类型的参数的表达式。它使用自定义的解组方法来处理JSON数据,并将额外的参数存储在Extra字段中。

英文:

A Working example: https://go.dev/play/p/02YzI5cv8vV

The whole reason the original response from Burak Serdar was not valid in my opinion is that it does not take into account the fact that you would need to handle the rest of the params as well. If you look closely enough, then you see that the expression is not an array of strings, its of varying type. This implementation handles the custom Unmarshalling and stores all the extra parameters in the Extra field.

Also code:

package main
import (
"encoding/json"
"log"
"reflect"
)
const jsonPayload = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
func main() {
data := MathExpressions{}
err := json.Unmarshal([]byte(jsonPayload), &data)
if err != nil {
log.Println("Failed to unmarshal json, error:", err)
return
}
log.Println(data)
for operation, expression := range data {
log.Print("Op:", operation, "Exp:", expression)
}
log.Println("Finished..")
}
/**
The sub definition for a specific expression in the object
*/
type ExpressionDefinition struct {
Vars  []string `json:"var"`
Extra []string
}
func (e *ExpressionDefinition) UnmarshalJSON(data []byte) error {
tokens := make([]interface{}, 0)
err := json.Unmarshal(data, &tokens)
if err != nil {
return err
}
for _, token := range tokens {
log.Println("Processing token:", token, "type:", reflect.TypeOf(token))
switch token.(type) {
case map[string]interface{}:
for _, v := range token.(map[string]interface{})["var"].([]interface{}) {
e.Vars = append(e.Vars, v.(string))
}
case string:
e.Extra = append(e.Extra, token.(string))
}
}
log.Println(tokens)
return nil
}
/**
The main expressions object which contains all the sub-expressions.
*/
type MathExpressions map[string]ExpressionDefinition

答案2

得分: 0

在这行代码中:

vals, ok := values[0].(mathExpVar)

values[0] 的类型是 map[string]interface{},无法断言为 mathExpVar,因为它是一个结构体,完全不同的数据类型。

你需要先将其断言为 map[string]interface{},然后在每个嵌套级别上都这样做:

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	type mathExpVar struct {
		valueVar []string `json:"var"`
	}
	var mathExpJson map[string][]interface{}
	var input = `{"add": [{"var": ["100"]}, "200"]}`
	err := json.Unmarshal([]byte(input), &mathExpJson)
	if err != nil {
		fmt.Println("Error in unmarshalling")
	}
	for _, values := range mathExpJson {
		var vals mathExpVar
		valMap, ok := values[0].(map[string]interface{})
		if ok {
			varSlice, ok := valMap["var"].([]interface{})
			if ok {
				for _, v := range varSlice {
					nv, ok := v.(string)
					if ok {
						vals.valueVar = append(vals.valueVar, nv)
					} else {
						fmt.Printf("%T\n", v)
					}
				}
			} else {
				fmt.Printf("%T\n", valMap["var"])
			}
		} else {
			fmt.Printf("%T\n", values[0])
		}
		fmt.Printf("%+v\n", vals)
	}
}

参考链接:https://go.dev/play/p/Ot_9IZr4pwM

有关接口和Go反射的更多信息,请查看:https://go.dev/blog/laws-of-reflection

英文:

Here the entire structure of the parsed json value will be stored in nested map[string]interface{}(json object) or []interface{}(json array) types.

In the line:

vals, ok := values[0].(mathExpVar)

values[0] would be of type map[string]interface{}, which cannot be asserted to mathExpVar, which is a struct, an entirely different datatype.

You need to type assert to map[string]interface{} first, then do this in each nested level as you go forward:

package main
import (
"encoding/json"
"fmt"
)
func main() {
type mathExpVar struct {
valueVar []string `json:"var"`
}
var mathExpJson map[string][]interface{}
var input = "{\"add\": [{\"var\": [\"100\"]}, \"200\"]}"
err := json.Unmarshal([]byte(input), &mathExpJson)
if err != nil {
fmt.Println("Error in unmarshalling")
}
for _, values := range mathExpJson {
var vals mathExpVar
valMap, ok := values[0].(map[string]interface{})
if ok {
varSlice, ok := valMap["var"].([]interface{})
if ok {
for _, v := range varSlice {
nv, ok := v.(string)
if ok {
vals.valueVar = append(vals.valueVar, nv)
} else {
fmt.Printf("%T\n", v)
}
}
} else {
fmt.Printf("%T\n", valMap["var"])
}
} else {
fmt.Printf("%T\n", values[0])
}
fmt.Printf("%+v\n", vals)
}
}

See: https://go.dev/play/p/Ot_9IZr4pwM

For more on interfaces and go reflection, check out: https://go.dev/blog/laws-of-reflection

huangapple
  • 本文由 发表于 2022年1月21日 13:36:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/70796852.html
匿名

发表评论

匿名网友

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

确定