英文:
Golang Interface{} wont type assert to int
问题
我正在处理一个 REST API,但是由于某种原因无法将 interface{} 类型的值断言为其底层类型 - int。
我通过 POST 请求发送数据来创建广告。请求如下所示:
POST http://localhost:8080/api/ads
{
"Title": "示例标题",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "描述",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}
传递的值被解码为 map[string]interface{},如下所示:
var adToValidate map[string]interface{}
err = json.NewDecoder(r.Body).Decode(&adToValidate)
if err != nil {
api.errorLog.Printf("解码广告对象时出错:%v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
当我将 costperdayincent 和 user 接口值断言为 int 时,出现了问题。
localScopeAd.CostPerDayInCent, ok = adMapToValidate["CostPerDayInCent"].(int)
if !ok {
fieldErrors[structFieldName] = fmt.Sprintf("%v 的值不是 int 类型", structFieldName)
}
if 语句执行,表示无法进行类型断言。这是为什么?是因为传递的 JSON 将每个值都视为字符串吗?我该如何解决这个问题?
后续
我在 @DanielFarrell 的帮助下解决了这个问题。
由于无法在评论部分中提供完整的回复,以下是我将数据解码为 map 而不是结构体的原因:
我理解将数据解码为结构体会更有意义。
最初,我是将数据解码为结构体的。但是,当我尝试验证布尔值时遇到了一些问题。
例如:
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
如果用户发送一个创建广告的 POST 请求,省略了上述字段。当请求体被解码时,结构体中的上述字段将默认为 false(布尔值的零值)。
如果我要验证传入的值,我将无法知道用户是输入了 false 还是完全省略了一个键值对。
为了确保用户输入了这些值,我首先将数据解码为 map,以便检查布尔键值对的键是否存在。
然后,如果缺少必需的布尔数据,我可以发送相应的响应。
如果您知道更简单的方法,请告诉我。有一些第三方包提供了具有三个值的布尔类型,这可能有效,但我决定使用上述方法。
英文:
I’m working on a rest api but for some reason can't type assert an interface{} to its underlying type - int.
I send data via post request, to create an ad. It looks like so:
POST http://localhost:8080/api/ads
{
"Title": "Example Title",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "A description",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}
The passed values are decoded into a map[string]interface{} like so:
var adToValidate map[string]interface{}
err = json.NewDecoder(r.Body).Decode(&adToValidate)
if err != nil {
api.errorLog.Printf("Error decoding ad object: %v", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
My problem happens when I type assert the costperdayincent and user interface values to int.
localScopeAd.CostPerDayInCent, ok = adMapToValidate[“CostPerDayInCent”].(int)
if !ok {
fieldErrors[structFieldName] = fmt.Sprintf("%v value is not of int type", structFieldName)
}
The if statement executes, indicating it cant type assert.
Why is this? Is it because the passed json treats every value as a string?
How would I resolve this issue?
FOLLOW UP
I resolved this issue with the help of @DanielFarrell 's answer.
As I couldn't fit a response in the comment section, below is my reason for decoding into a map and not a struct:
I understand it would make a lot more sense to decode into a struct.
I had initially been decoding into a struct. However I had run into some issues when trying to validate the bool values.
i.e
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
If a user was to make a post request to create an ad leaving out the above values. When the request body was decoded the above fields would default to false in the struct (bools 0 value).
If i was then going to validate the passed in values I wouldn't know if the user had entered false or had left out a whole key value pair.
As I wanted to ensure the user had entered these values I first decoded into a map so that I could check if the key of the bool key value pair was present.
Then I could send the relevant response if there was some missing required bool data.
If you know of a simpler way of going about the above I'd be interested in hearing it. There's some 3rd party packages that have 3 value boolean types which may have worked but I decided to go with the above instead.
答案1
得分: 0
我更喜欢将其解码为结构体,让json/encoding
来处理正确的类型处理工作。
这种方法非常常见且有用,有几个原因。首先,你可以选择类型。其次,你正在定义一个类型,因此可以附加函数,我发现这非常方便,可以用作组织代码和避免将结构作为显式函数参数传递的方法。第三,你可以为结构体的字段附加注释,进一步调整解码过程。
最后,对我来说最重要的是,你正在按照 Go 处理数据的方式进行思考。在许多流行的现代语言(如 Python)中,代码和文档通常不会将类型附加到函数上,也不会尝试显式枚举对象的属性。而 Go 则不同。它删除了固有的语言反射,这是它如此高效的原因之一。这对程序员来说可以带来巨大的好处——当你知道所有的类型时,你就知道事物的行为方式,并且可以准确地确定必须传递给调用的函数的内容。我建议你接受显式类型,并尽可能避免将 JSON 解码为面向接口的类型。你将知道自己在处理什么,并且还可以明确地为你的消费者定义它。
链接 https://play.golang.org/p/egaequIT8ET 对比了你的方法,其中你不需要费心定义类型,但也没有机会选择它们。
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Input struct {
Title,
Selection string
CostPerDayInCent int64
Description string
User int64
AllowMobileContact,
AllowEmailContact,
IsActive bool
}
func main() {
var input = `{
"Title": "Example Title",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "A description",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}`
// map[string]interface
data := make(map[string]interface{})
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(&data); err != nil {
panic(err)
}
fmt.Printf("%f %T\n", data["CostPerDayInCent"], data["CostPerDayInCent"])
// structure
obj := new(Input)
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(obj); err != nil {
panic(err)
}
fmt.Printf("%d %T", obj.CostPerDayInCent, obj.CostPerDayInCent)
}
英文:
I would prefer to decode into a struct and let json/encoding
do the work of handling proper type.
type Input struct {
Title,
Selection string
CostPerDayInCent int64
Description string
User int64
AllowMobileContact,
AllowEmailContact,
IsActive bool
}
This approach is quite common and useful for several reasons. First, you get to choose the type. Second, you are defining a type, so you get to attach functions, which I find quite convenient as a method to organize my code and avoid having to pass structures as explicit function arguments. Third, you can attach annotations to the struct's fields, further adjusting unmarshalling.
Finally, and to me, most importantly, you're thinking about data the way Go handles it. In many of the popular modern languages such as Python, the code - and the documentation - doesn't usually attach types to functions, or attempt to explicitly enumerate attributes of objects. Go is different. Its removal of inherent language reflection is one of the reasons it is so performant. This can provide vast benefits to the programmer - when you know all the types, you know exactly how things will behave, and can precisely identify what you must pass the functions you call. I suggest you embrace the explicit typing, and avoid decoding json into interface-oriented types whenever possible. You'll know what you're handling, and you'll be able to define it explicitly for your consumers as well.
https://play.golang.org/p/egaequIT8ET contrasts your approach, in which you don't need to bother defining the types - but you also don't have the opportunity to choose them.
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type Input struct {
Title,
Selection string
CostPerDayInCent int64
Description string
User int64
AllowMobileContact,
AllowEmailContact,
IsActive bool
}
func main() {
var input = `{
"Title": "Example Title",
"Section": "machinery",
"CostPerDayInCent": 34500,
"Description": "A description",
"User": 4,
"AllowMobileContact": true,
"AllowEmailContact": true,
"IsActive": false
}`
// map[string]interface
data := make(map[string]interface{})
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(&data); err != nil {
panic(err)
}
fmt.Printf("%f %T\n", data["CostPerDayInCent"], data["CostPerDayInCent"])
// structure
obj := new(Input)
if err := json.NewDecoder(bytes.NewBufferString(input)).Decode(obj); err != nil {
panic(err)
}
fmt.Printf("%d %T", obj.CostPerDayInCent, obj.CostPerDayInCent)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论