我有一个结构
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-Schema 验证的用例吗?
是的,这可能是 JSON 模式验证的一个很好的用例。您可以直接从 Go 结构生成 JSON 架构,然后使用验证库来获取更多有用的错误。例如,使用 Huma's (免责声明:我是作者)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 结构体包含有关发生的每个错误的大量信息。
使用 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 的作者