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