【发布时间】:2023-03-14 01:30:01
【问题描述】:
如果在使用 Go 解析 JSON 输入时未找到字段,是否会产生错误?
我在文档中找不到它。
是否有标签根据需要指定字段?
【问题讨论】:
如果在使用 Go 解析 JSON 输入时未找到字段,是否会产生错误?
我在文档中找不到它。
是否有标签根据需要指定字段?
【问题讨论】:
encoding/json 包中没有将字段设置为“必需”的标记。您要么必须编写自己的 MarshalJSON() 方法,要么对缺失的字段进行后期检查。
要检查缺失的字段,您必须使用指针来区分缺失/空值和零值:
type JsonStruct struct {
String *string
Number *float64
}
完整的工作示例:
package main
import (
"fmt"
"encoding/json"
)
type JsonStruct struct {
String *string
Number *float64
}
var rawJson = []byte(`{
"string":"We do not provide a number"
}`)
func main() {
var s *JsonStruct
err := json.Unmarshal(rawJson, &s)
if err != nil {
panic(err)
}
if s.String == nil {
panic("String is missing or null!")
}
if s.Number == nil {
panic("Number is missing or null!")
}
fmt.Printf("String: %s Number: %f\n", *s.String, *s.Number)
}
【讨论】:
struct 怎么办?
Object *Foo,然后检查是否s.Object == nil。
您还可以覆盖特定类型的解组(因此,一个必填字段隐藏在几个 json 层中),而不必使该字段成为指针。 UnmarshalJSON 由Unmarshaler 接口定义。
type EnumItem struct {
Named
Value string
}
func (item *EnumItem) UnmarshalJSON(data []byte) (err error) {
required := struct {
Value *string `json:"value"`
}{}
all := struct {
Named
Value string `json:"value"`
}{}
err = json.Unmarshal(data, &required)
if err != nil {
return
} else if required.Value == nil {
err = fmt.Errorf("Required field for EnumItem missing")
} else {
err = json.Unmarshal(data, &all)
item.Named = all.Named
item.Value = all.Value
}
return
}
【讨论】:
这是另一种方法,检查您自定义的tag
您可以为您的结构创建一个标签,例如:
type Profile struct {
Name string `yourprojectname:"required"`
Age int
}
使用reflect检查标签是否分配了required值
func (p *Profile) Unmarshal(data []byte) error {
err := json.Unmarshal(data, p)
if err != nil {
return err
}
fields := reflect.ValueOf(p).Elem()
for i := 0; i < fields.NumField(); i++ {
yourpojectTags := fields.Type().Field(i).Tag.Get("yourprojectname")
if strings.Contains(yourpojectTags, "required") && fields.Field(i).IsZero() {
return errors.New("required field is missing")
}
}
return nil
}
而测试用例是这样的:
func main() {
profile1 := `{"Name":"foo", "Age":20}`
profile2 := `{"Name":"", "Age":21}`
var profile Profile
err := profile.Unmarshal([]byte(profile1))
if err != nil {
log.Printf("profile1 unmarshal error: %s\n", err.Error())
return
}
fmt.Printf("profile1 unmarshal: %v\n", profile)
err = profile.Unmarshal([]byte(profile2))
if err != nil {
log.Printf("profile2 unmarshal error: %s\n", err.Error())
return
}
fmt.Printf("profile2 unmarshal: %v\n", profile)
}
结果:
profile1 unmarshal: {foo 20}
2009/11/10 23:00:00 profile2 unmarshal error: required field is missing
你可以去Playground看看完成的代码
【讨论】:
{"Age":21}
yourprojectname:"required"。见第一个示例代码
profile2 中缺少“名称”字段的操场代码。除非有效载荷中存在“名称”,否则不会返回错误。
{"Age":21}。在我的示例中,它是 profile2 := {"Name":"", "Age":21}。然而,这不是根本原因。根本原因是主函数中只声明了一个Profile 变量。在第一个 profile.Unmarshal([]byte(profile1)) 之后,填写名称字段。这是一个示例代码,您可以清空配置文件变量或在第一次解组调用后声明另一个变量。 Example
您只需实现 Unmarshaler 接口即可自定义 JSON 的解组方式。
【讨论】:
您还可以使用 JSON 模式验证。
package main
import (
"encoding/json"
"fmt"
"github.com/alecthomas/jsonschema"
"github.com/xeipuuv/gojsonschema"
)
type Bird struct {
Species string `json:"birdType"`
Description string `json:"what it does" jsonschema:"required"`
}
func main() {
var bird Bird
sc := jsonschema.Reflect(&bird)
b, _ := json.Marshal(sc)
fmt.Println(string(b))
loader := gojsonschema.NewStringLoader(string(b))
documentLoader := gojsonschema.NewStringLoader(`{"birdType": "pigeon"}`)
schema, err := gojsonschema.NewSchema(loader)
if err != nil {
panic("nop")
}
result, err := schema.Validate(documentLoader)
if err != nil {
panic("nop")
}
if result.Valid() {
fmt.Printf("The document is valid\n")
} else {
fmt.Printf("The document is not valid. see errors :\n")
for _, err := range result.Errors() {
// Err implements the ResultError interface
fmt.Printf("- %s\n", err)
}
}
}
输出
{"$schema":"http://json-schema.org/draft-04/schema#","$ref":"#/definitions/Bird","definitions":{"Bird":{"required":["birdType","what it does"],"properties":{"birdType":{"type":"string"},"what it does":{"type":"string"}},"additionalProperties":false,"type":"object"}}}
The document is not valid. see errors :
- (root): what it does is required
取自Strict JSON parsing的代码示例
【讨论】: