解组 Go 中的 json: 必填字段?

如果在使用 Go 解析 JSON 输入时没有找到字段,是否可能生成错误?

我在文件里找不到。

是否有任何标记指定所需的字段?

67299 次浏览

There is no tag in the encoding/json package that sets a field to "required". You will either have to write your own MarshalJSON() method, or do a post check for missing fields.

To check for missing fields, you will have to use pointers in order to distinguish between missing/null and zero values:

type JsonStruct struct {
String *string
Number *float64
}

Full working example:

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)
}

Playground

You can also override the unmarshalling for a specific type (so a required field buried in a few json layers) without having to make the field a pointer. UnmarshalJSON is defined by the Unmarshaler interface.

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
}

You can just implement the Unmarshaler interface to customize how your JSON gets unmarshalled.

Here is another way by checking your customized tag

you can create a tag for your struct like:

type Profile struct {
Name string `yourprojectname:"required"`
Age  int
}

Use reflect to check if the tag is assigned required value

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
}

And test cases are like:

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)


}

Result:

profile1 unmarshal: {foo 20}


2009/11/10 23:00:00 profile2 unmarshal error: required field is missing

You can go to Playground to have a look at the completed code

you can also make use of JSON schema validation.

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)
}
}


}

Outputs

{"$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


code example taken from Strict JSON parsing