简体   繁体   中英

Golang: Validate Struct field of type string to be one of specific values

golang version: 1.18.3

validator: github.com/go-playground/validator/v10

I want to validate an incoming JSON payload after loaded into nested struct data structure. Here's my incoming JSON payload,

{
  "name": "Duro",
  "gender": "MALE",
  "tier": 3,
  "mobileNumber": "0356874586",
  "address": {
    "city": "Tokyo",
    "street": "Shibaura St"
  },
  "children":[
    {
      "title": "Mr",
      "lastName": "Takayashi"
    }
  ],
  "isEmployed": false,
  "requestedAt": "2022-01-10T03:30:12.639Z"
}

Here's my user.go file,

package main

type User struct {
        Name                   string                     `validate:"required"`
        Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
        Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
        MobileNumber           string                     `validate:"required"`
        Email                  string
        Address                *Address  `validate:"required"`
        Children               []Child   `validate:"required,dive"`
        IsEmployed             *bool     `validate:"required"`
        PreferredContactMethod string    `validate:"oneof=EMAIL PHONE POST SMS"`
        RequestedAt            time.Time `validate:"required"`
    }
    type Address struct {
        City   string `validate:"required"`
        Street string `validate:"required"`
    }
    
    type Child struct {
        Title     string `validate:"required"`
        FirstName string
        LastName  string `validate:"required"`
    }

Here's my test function

func TestUserPayload(t *testing.T) {
    validate := validator.New()
    var u User

    err := json.Unmarshal([]byte(jsonData), &u)
    if err != nil {
        panic(err)
    }

    err := validate.Struct(&u)
    if err != nil {
        t.Errorf("error %v", err)
    }

}

This test fails with error,

error Key: 'User.PreferredContactMethod' Error:Field validation for 'PreferredContactMethod' failed on the 'oneof' tag

This happens because Go assigns empty string to User.PreferredContactMethod struct field. Since PreferredContactMethod is NOT a required field, I DON'T want to see this validation error when json payload doesn't have it. How can I avoid this error message when the json payload doesn't have preferredContactMethod field?

If you have better alternatives to achieve this validation, happy to hear them as well.

Here's the code in Go Playground

Utilize omitempty along with oneof to make the validator library ignore empty or unset values.

type User struct {
        Name                   string                     `validate:"required"`
        Gender                 string                     `validate:"required,oneof=MALE FEMALE"`
        Tier                   *uint8                     `validate:"required,eq=0|eq=1|eq=2|eq=3"`
        MobileNumber           string                     `validate:"required"`
        Email                  string
        Address                *Address  `validate:"required"`
        Children               []Child   `validate:"required,dive"`
        IsEmployed             *bool     `validate:"required"`
        PreferredContactMethod string    `validate:"omitempty,oneof=EMAIL PHONE POST SMS"`
        RequestedAt            time.Time `validate:"required"`
    }

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"
    "time"

    "github.com/marrow16/valix"
)

type User struct {
    Name                   string    `json:"name" v8n:"required, notNull"`
    Gender                 string    `json:"gender" v8n:"required, notNull, &strtoken{['MALE','FEMALE']}"`
    Tier                   uint8     `json:"tier" v8n:"required, notNull, &mini{0}, &maxi{3}"`
    DateOfBirth            string    `json:"dateOfBirth" v8n:"notNull, &strisod"`
    MobileNumber           string    `json:"mobileNumber" v8n:"required, notNull, &strpatt{'^([0-9]{8,16})$'}"`
    Email                  string    `json:"email" v8n:"notNull"`
    Address                Address   `json:"address" v8n:"required, notNull"`
    Children               []Child   `json:"children" v8n:"required, notNull"`
    IsEmployed             bool      `json:"isEmployed" v8n:"required"`
    PreferredContactMethod string    `json:"preferredContactMethod" v8n:"notNull, &strtoken{['EMAIL','PHONE','POST','SMS']}"`
    RequestedAt            time.Time `json:"requestedAt" v8n:"required, notNull, &strisodt"`
}
type Address struct {
    City   string `json:"city" v8n:"required, notNull"`
    Street string `json:"street" v8n:"required, notNull"`
}

type Child struct {
    Title     string `json:"title" v8n:"required, notNull"`
    FirstName string `json:"firstName" v8n:"notNull"`
    LastName  string `json:"lastName" v8n:"required, notNull"`
}

const jsonData = `{
  "name": "Duro",
  "gender": "MALE",
  "tier": 3,
  "dateOfBirth": "2000-01-13",
  "mobileNumber": "0356874586",
  "address": {
    "city": "Tokyo",
    "street": "Shibaura St"
  },
  "children":[
    {
      "title": "Mr",
      "lastName": "Takayashi"
    }
  ],
  "isEmployed": false,
  "requestedAt": "2022-01-10T03:30:12.639Z"
}`

var UserRequestValidator = valix.MustCompileValidatorFor(User{}, nil)

func main() {
    user := User{}

    ok, violations, _ := UserRequestValidator.ValidateStringInto(jsonData, &user)

    if ok {
        fmt.Println("Validation successful")
    } else {
        fmt.Println(fmt.Sprintf("Validation failed! (violations: %d)", len(violations)))
        for _, v := range violations {
            fmt.Printf("%s (property: '%s', path: '%s')\n", v.Message, v.Property, v.Path)
        }
    }
}

Go playground here: https://go.dev/play/p/3zrkZx97m-e

Disclosure: I am the author of Valix

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM