Search code examples
gostructslicego-gormdelve

Required_If Combination Issue in github.com/go-playground/validator/v10 package


Package version eg. v9, v10:

Package Version: v10

Issue, Question or Enhancement: When I trying to run the below code. i get this error this is wired

Output

Validation error: Key: 'Application.Applicants[0].Entity.Name' Error:Field validation for 'Name' failed on the 'required' tag
Key: 'Application.Applicants[0].Entity.TaxID' Error:Field validation for 'TaxID' failed on the 'required' tag
Key: 'Application.Applicants[1].Person.Name' Error:Field validation for 'Name' failed on the 'required' tag
Key: 'Application.Applicants[1].Person.Age' Error:Field validation for 'Age' failed on the 'required' tag
Key: 'Application.Applicants[1].Person.Email' Error:Field validation for 'Email' failed on the 'required' tag

Code sample, to showcase or reproduce:

package main

import (
    "fmt"
    "github.com/go-playground/validator/v10"
)

type Application struct {
    Applicants []Applicant `validate:"dive"`
}

type Applicant struct {
    ApplicantCategory string `validate:"required,oneof=PERSON ENTITY"`
    Person            Person `validate:"required_if=ApplicantCategory PERSON"`
    Entity            Entity `validate:"required_if=ApplicantCategory ENTITY"`
}

type Person struct {
    Name  string `validate:"required"`
    Age   int    `validate:"required,gte=18"`
    Email string `validate:"required,email"`
}

type Entity struct {
    Name  string `validate:"required"`
    TaxID string `validate:"required"`
}

func main() {
    // Create a new validator instance
    v := validator.New()

    // Create an instance of Application to validate
    data := Application{
        Applicants: []Applicant{
            {
                ApplicantCategory: "PERSON",
                Person: Person{
                    Name:  "John Doe",
                    Age:   25,
                    Email: "[email protected]",
                },
            },
            {
                ApplicantCategory: "ENTITY",
                Entity: Entity{
                    Name:  "Example Corp",
                    TaxID: "123456789",
                },
            },
        },
    }

    // Use the validator to validate the Application struct and its Applicants
    if err := v.Struct(data); err != nil {
        fmt.Println("Validation error:", err)
    } else {
        fmt.Println("Validation passed")
    }
}

Could not find out what is the issue in the code or validator package. Any help would be appreciated...


Solution

  • Add omitempty e.g.:

    type Applicant struct {
        ApplicantCategory string `validate:"required,oneof=PERSON ENTITY"`
        Person            Person `validate:"required_if=ApplicantCategory PERSON,omitempty"`
        Entity            Entity `validate:"required_if=ApplicantCategory ENTITY,omitempty"`
    }
    

    Full example in playground (note that this dos not run reliably in the playground due to the size of imported packages).

    The issue is that the required_if causes the library to check if the Person/Entity is present, but the library will still validate the empty Person/Entity (and that fails!). Adding omitempty means that the library will ignore an empty struct; this provides the desired result because the required_if will ensure that any required struct is not empty (meaning it will be validated).

    An alternative option is to use pointers (playground):

    type Applicant struct {
        ApplicantCategory string `validate:"required,oneof=PERSON ENTITY"`
        Person            *Person `validate:"required_if=ApplicantCategory PERSON"`
        Entity            *Entity `validate:"required_if=ApplicantCategory ENTITY"`
    }
    

    The difference here is that when there is no Entity then the value will be nil (as opposed to a Entity with default values) meaning there is nothing for validator to validate.

    Nots: I'd suggest using v := validator.New(validator.WithRequiredStructEnabled()) (as per the docs).