Golang 字段类型验证

发布于 2025-01-10 22:26:04 字数 572 浏览 0 评论 0原文

我有一个结构体

type InputData struct {
   SomeNumber  int     `json:"someNumber"`
   SomeText    string  `json:"someText"`
}

,我将 json.Unmarshal 我的 http 请求正文转换为 InputData 类型的结构体。

如果我传入 {"someNumber": "NaN", "someText": 42} 我会得到类似的内容

恐慌:json:无法将字符串解组到 Go 结构字段中 InputData.someNumber int 类型

有没有办法获得完整且更结构化的错误数据?

例如,所有不可解析字段的列表及其原因? (在我的示例中,我想知道 someNumber 无效,因为我传入了一个字符串,并且 someText 无效,因为我传入了一个数字)

我怀疑这是可能的,但我仍然想验证我的输入感觉。 这是 JSON 模式验证的用例吗?

I have a struct

type InputData struct {
   SomeNumber  int     `json:"someNumber"`
   SomeText    string  `json:"someText"`
}

I do json.Unmarshal my http request body into a struct of type InputData.

In case I pass in {"someNumber": "NaN", "someText": 42} I get something like

panic: json: cannot unmarshal string into Go struct field
InputData.someNumber of type int

Is there a way to get complete and more structured error data?

For instance, a list of all the non-parseable fields along w/ the reason for that?
(in my example, I'd like to know that someNumber is invalid because I passed in a string AND that someText is invalid because I passed in a number)

I doubt that's possible, but I'd still like to validate my input in that sense. Is it a use case for JSON-Schema validation?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浪漫人生路 2025-01-17 22:26:04

是的,这可能是 JSON 模式验证的一个很好的用例。您可以直接从 Go 结构生成 JSON 架构,然后使用验证库来获取更多有用的错误。例如,使用 Huma 的(免责声明:我是作者)JSON Schema 包以及优秀的 xeipuuv/gojsonschema

package main

import (
    "fmt"
    "reflect"

    "github.com/danielgtaylor/huma/schema"
    "github.com/xeipuuv/gojsonschema"
)

type InputData struct {
    SomeNumber int    `json:"someNumber"`
    SomeText   string `json:"someText"`
}

func main() {
    // Generate a JSON Schema from the struct type.
    s, err := schema.Generate(reflect.TypeOf(InputData{}))
    if err != nil {
        panic(err)
    }

    // Get the input from the user
    input := []byte(`{"someNumber": "NaN", "someText": 42}`)

    // Load the schema and validate the user's input as bytes which
    // means we don't have to handle invalid types by first unmarshaling.
    loader := gojsonschema.NewGoLoader(s)
    doc := gojsonschema.NewBytesLoader(input)
    validator, err := gojsonschema.NewSchema(loader)
    if err != nil {
        panic(err)
    }
    result, err := validator.Validate(doc)
    if err != nil {
        panic(err)
    }

    // Display the results!
    if !result.Valid() {
        for _, desc := range result.Errors() {
            fmt.Printf("%s (%s)\n", desc, desc.Value())
        }
        return
    }

    fmt.Println("Input was valid!")
}

完整示例:https://go.dev/play/p/XEnrQswp_7N。输出如下:

someNumber: Invalid type. Expected: integer, given: string (NaN)
someText: Invalid type. Expected: string, given: integer (42)

ResultError 结构有一个完整的有关发生的每个错误的一堆信息。

Yes, this is probably a good use-case for JSON Schema validation. You can generate a JSON Schema directly from your Go struct and then use a validation library to get more useful errors. For example, using Huma's (disclaimer: I am the author) JSON Schema package with the excellent xeipuuv/gojsonschema:

package main

import (
    "fmt"
    "reflect"

    "github.com/danielgtaylor/huma/schema"
    "github.com/xeipuuv/gojsonschema"
)

type InputData struct {
    SomeNumber int    `json:"someNumber"`
    SomeText   string `json:"someText"`
}

func main() {
    // Generate a JSON Schema from the struct type.
    s, err := schema.Generate(reflect.TypeOf(InputData{}))
    if err != nil {
        panic(err)
    }

    // Get the input from the user
    input := []byte(`{"someNumber": "NaN", "someText": 42}`)

    // Load the schema and validate the user's input as bytes which
    // means we don't have to handle invalid types by first unmarshaling.
    loader := gojsonschema.NewGoLoader(s)
    doc := gojsonschema.NewBytesLoader(input)
    validator, err := gojsonschema.NewSchema(loader)
    if err != nil {
        panic(err)
    }
    result, err := validator.Validate(doc)
    if err != nil {
        panic(err)
    }

    // Display the results!
    if !result.Valid() {
        for _, desc := range result.Errors() {
            fmt.Printf("%s (%s)\n", desc, desc.Value())
        }
        return
    }

    fmt.Println("Input was valid!")
}

Full example: https://go.dev/play/p/XEnrQswp_7N. Output looks like:

someNumber: Invalid type. Expected: integer, given: string (NaN)
someText: Invalid type. Expected: string, given: integer (42)

The ResultError struct has a whole bunch of information about each error that occurs.

葬花如无物 2025-01-17 22:26:04

使用 https://github.com/marrow16/valix (https://pkg.go.dev/github.com/marrow16/valix)可以通过以下方式实现:

package main

import (
    "fmt"
    "github.com/marrow16/valix"
    "testing"
)

type InputData struct {
    SomeNumber int    `json:"someNumber"`
    SomeText   string `json:"someText"`
}

var validator = valix.MustCompileValidatorFor(InputData{}, nil)

func TestInputDataValidation(t *testing.T) {
    myInputData := &InputData{}
    str := `{"someNumber": "NaN", "someText": 43}`
        
    ok, violations, _ := validator.ValidateStringInto(str, myInputData)
    println("Validated OK? ", ok)
    if !ok {
        for i, v := range violations {
            fmt.Printf("Violation #%d: Property='%s', Message='%s'\n", i+1, v.Property, v.Message)
        }
    }
}

会输出:

Validated OK? false
Violation #1: Property='someNumber', Message='Value expected to be of type number'
Violation #2: Property='someText', Message='Value expected to be of type string'

披露:我是 Valix 的作者

Using https://github.com/marrow16/valix (https://pkg.go.dev/github.com/marrow16/valix) it could be achieved with the following:

package main

import (
    "fmt"
    "github.com/marrow16/valix"
    "testing"
)

type InputData struct {
    SomeNumber int    `json:"someNumber"`
    SomeText   string `json:"someText"`
}

var validator = valix.MustCompileValidatorFor(InputData{}, nil)

func TestInputDataValidation(t *testing.T) {
    myInputData := &InputData{}
    str := `{"someNumber": "NaN", "someText": 43}`
        
    ok, violations, _ := validator.ValidateStringInto(str, myInputData)
    println("Validated OK? ", ok)
    if !ok {
        for i, v := range violations {
            fmt.Printf("Violation #%d: Property='%s', Message='%s'\n", i+1, v.Property, v.Message)
        }
    }
}

Would output:

Validated OK? false
Violation #1: Property='someNumber', Message='Value expected to be of type number'
Violation #2: Property='someText', Message='Value expected to be of type string'

Disclosure: I am the author of Valix

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文