govalidationgo-playground

Golang - go-playground/validator - How to include single inverted comma (') inside oneof rule


I am trying to validate a struct field in Golang using the go-playground/validator/v10 package. Specifically, I want to use the oneof tag to validate that the field value matches one of the predefined values, which includes strings with single quotes ('). Here's the code I am using:

package main

import (
    "fmt"

    "github.com/go-playground/validator/v10"
)

// Struct with validation tag
type Award struct {
    Title string `validate:"oneof=palm'dor level'dor 'state award'"`
}

func main() {
    validate := validator.New()

    // Test cases
    testCases := []Award{
        {"palm'dor"},    // Expected: Valid
        {"level'dor"},   // Expected: Valid
        {"state award"}, // Expected: Valid
        {"other"},       // Expected: Invalid
    }

    for _, testCase := range testCases {
        err := validate.Struct(testCase)
        if err != nil {
            fmt.Printf("Input: %q - Invalid (%v)\n", testCase.Title, err)
        } else {
            fmt.Printf("Input: %q - Valid\n", testCase.Title)
        }
    }
}

Expected Behavior: The program should validate "palm'dor", "level'dor", and "state award" as valid inputs. Any other value should be marked as invalid.

Problem: When I run the program, I got the following output:

Input: "palm'dor" - Invalid (Key: 'Award.Title' Error:Field validation for 'Title' failed on the 'oneof' tag)
Input: "level'dor" - Invalid (Key: 'Award.Title' Error:Field validation for 'Title' failed on the 'oneof' tag)
Input: "state award" - Valid
Input: "other" - Invalid (Key: 'Award.Title' Error:Field validation for 'Title' failed on the 'oneof' tag)

Solution

  • Seems there is a known issue reported in go-playground/validator, Issue #1350, that explains the existing tokenizer regex doesn't work well with multi-word strings that contain a single quote character.

    Until the point, the package maintainer comes up with an effective solution, I suggest creating a custom validator, that does a direct string comparison. It is particularly effective when there are special characters involved and also be easily extended to include additional validation rules.

    package main
    
    import (
        "fmt"
        "slices"
    
        "github.com/go-playground/validator/v10"
    )
    
    type Award struct {
        Title string `validate:"validAward"`
    }
    
    func main() {
        validate := validator.New()
    
        // Register custom validator
        validate.RegisterValidation("validAward", validateAward)
    
        testCases := []Award{
            {"palm'dor"},
            {"level'dor"},
            {"state award"},
            {"other"},
        }
    
        for _, testCase := range testCases {
            err := validate.Struct(testCase)
            if err != nil {
                fmt.Printf("Input: %q - Invalid (%v)\n", testCase.Title, err)
            } else {
                fmt.Printf("Input: %q - Valid\n", testCase.Title)
            }
        }
    }
    
    func validateAward(fl validator.FieldLevel) bool {
        value := fl.Field().String()
        validAwards := []string{"palm'dor", "level'dor", "state award"}
    
        return slices.Contains(validAwards, value)
    }
    

    which returns as expected.

    Input: "palm'dor" - Valid
    Input: "level'dor" - Valid
    Input: "state award" - Valid
    Input: "other" - Invalid (Key: 'Award.Title' Error:Field validation for 'Title' failed on the 'validAward' tag)