为了账号安全,请及时绑定邮箱和手机立即绑定

如何告诉客户端他们需要从 Go 服务器发送一个整数而不是一个字符串?

如何告诉客户端他们需要从 Go 服务器发送一个整数而不是一个字符串?

Go
皈依舞 2021-12-07 19:40:06
假设我在服务器上有以下 Go 结构type account struct {    Name    string    Balance int}我想对传入的请求调用 json.Decode 以将其解析为帐户。    var ac account    err := json.NewDecoder(r.Body).Decode(&ac)如果客户端发送以下请求:{    "name": "test@example.com",     "balance": "3"}Decode() 将返回以下错误:json: cannot unmarshal string into Go value of type int现在可以将其解析为“您为 Balance 发送了一个字符串,但您确实应该发送一个整数”,但这很棘手,因为您不知道字段名称。如果请求中有很多字段,它也会变得更加棘手 - 您不知道哪个解析失败。对于请求中任意数量的整数字段,在 Go 中处理传入请求并返回错误消息“余额必须是字符串”的最佳方法是什么?
查看完整描述

3 回答

?
慕工程0101907

TA贡献1887条经验 获得超5个赞

您可以为“余额”字段使用带有自定义解组算法的自定义类型。


现在有两种可能:


处理两种类型:


package main


import (

    "encoding/json"

    "fmt"

    "strconv"

)


type Int int


type account struct {

    Name    string

    Balance Int

}


func (i *Int) UnmarshalJSON(b []byte) (err error) {

    var s string

    err = json.Unmarshal(b, &s)

    if err == nil {

        var n int

        n, err = strconv.Atoi(s)

        if err != nil {

            return

        }

        *i = Int(n)

        return

    }


    var n int

    err = json.Unmarshal(b, &n)

    if err == nil {

        *i = Int(n)

    }

    return

}


func main() {

    for _, in := range [...]string{

        `{"Name": "foo", "Balance": 42}`,

        `{"Name": "foo", "Balance": "111"}`,

    } {

        var a account

        err := json.Unmarshal([]byte(in), &a)

        if err != nil {

            fmt.Printf("Error decoding JSON: %v\n", err)

        } else {

            fmt.Printf("Decoded OK: %v\n", a)

        }

    }

}

游乐场链接。


只处理一个数字类型,并通过一个合理的错误使其他任何事情失败:


package main


import (

    "encoding/json"

    "fmt"

)


type Int int


type account struct {

    Name    string

    Balance Int

}


type FormatError struct {

    Want   string

    Got    string

    Offset int64

}


func (fe *FormatError) Error() string {

    return fmt.Sprintf("Invalid data format at %d: want: %s, got: %s",

        fe.Offset, fe.Want, fe.Got)

}


func (i *Int) UnmarshalJSON(b []byte) (err error) {

    var n int

    err = json.Unmarshal(b, &n)

    if err == nil {

        *i = Int(n)

        return

    }

    if ute, ok := err.(*json.UnmarshalTypeError); ok {

        err = &FormatError{

            Want:   "number",

            Got:    ute.Value,

            Offset: ute.Offset,

        }

    }

    return

}


func main() {

    for _, in := range [...]string{

        `{"Name": "foo", "Balance": 42}`,

        `{"Name": "foo", "Balance": "111"}`,

    } {

        var a account

        err := json.Unmarshal([]byte(in), &a)

        if err != nil {

            fmt.Printf("Error decoding JSON: %#v\n", err)

            fmt.Printf("Error decoding JSON: %v\n", err)

        } else {

            fmt.Printf("Decoded OK: %v\n", a)

        }

    }

}

游乐场链接。


还有第三种可能性:为整个account类型编写自定义解组器,但它需要更多涉及的代码,因为您需要使用encoding/json.Decoder类型的方法实际迭代输入 JSON 数据 。


看完你的


对于请求中任意数量的整数字段,在 Go 中处理传入请求并返回错误消息“余额必须是字符串”的最佳方法是什么?


更仔细,我承认有整个类型的定制解析器唯一明智的可能性,除非你是一个OK第三方软件包实现解析器通过JSON模式支持验证(我想我会看看这个第一次作为juju是一个相当已建立的产品)。


查看完整回答
反对 回复 2021-12-07
?
慕姐4208626

TA贡献1852条经验 获得超7个赞

对此的解决方案可能是使用类型断言,通过使用映射将 JSON 数据解组为:


type account struct {

    Name    string

    Balance int

}


var str = `{

    "name": "test@example.com", 

    "balance": "3"

}`


func main() {

    var testing = map[string]interface{}{}

    err := json.Unmarshal([]byte(str), &testing)

    if err != nil {

        fmt.Println(err)

    }


    val, ok := testing["balance"]

    if !ok {

        fmt.Println("missing field balance")

        return

    }


    nv, ok := val.(float64)

    if !ok {

        fmt.Println("balance should be a number")

        return

    }


    fmt.Printf("%+v\n", nv)

}

见http://play.golang.org/p/iV7Qa1RrQZ


使用这里的类型断言是float64因为它是 Go 的 JSON 解码器支持的默认数字类型。


应该注意的是,这种使用interface{}可能不值得麻烦。


该UnmarshalTypeError(https://golang.org/pkg/encoding/json/#UnmarshalFieldError)包含一个Offset领域,可能允许检索触发错误的JSON数据的内容。


例如,您可以返回以下类型的消息:


cannot unmarshal string into Go value of type int near `"balance": "3"`


查看完整回答
反对 回复 2021-12-07
?
眼眸繁星

TA贡献1873条经验 获得超9个赞

似乎这里提供了一个实现来解决这个问题,仅在 Go 中。


type account struct {

    Name    string

    Balance int `json:",string"`

}

在我看来,更正确和可持续的方法是让你用 JavaScript 之类的东西创建一个客户端库,然后将它发布到 NPM 注册表中供其他人使用(私有存储库的工作方式相同)。通过提供这个库,你可以以一种有意义的方式为消费者定制 API,并防止错误蔓延到你的主程序中。


查看完整回答
反对 回复 2021-12-07
  • 3 回答
  • 0 关注
  • 173 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信