7

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

2 Answers 2

14

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"`
    }

Sign up to request clarification or add additional context in comments.

Comments

2

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.