validation - Golang - go-playgroundvalidator - How to include single inverted comma (') inside oneof rule - Stack Overfl

admin2025-05-02  1

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

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)
Share Improve this question edited Jan 2 at 6:25 Aslam-Ep asked Jan 2 at 6:06 Aslam-EpAslam-Ep 1411 silver badge7 bronze badges 0
Add a comment  | 

2 Answers 2

Reset to default 3

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)

Based on the answer by @Inian and the official GitHub repo(go-playground/validator) issue. We could create our own custom validation function like oneOfCustom which will receive the allowed parameter list where single quotes(') can be escaped with one more single quote('').

// Parse the allowed values with the given regex
func parseAllowedValues(input string) ([]string, error) {
    re, err := regexp.Compile(`'([^']|'')*'|\S+`)
    if err != nil {
        return nil, fmt.Errorf("failed to compile regex: %w", err)
    }

    matches := re.FindAllString(input, -1)
    if matches == nil {
        return nil, nil
    }

    for i, match := range matches {
        if len(match) > 1 {
            if (match[0] == '\'' && match[len(match)-1] == '\'') {
                // Remove outer single quotes
                match = match[1 : len(match)-1]
            }
            
            // Replace doubled single quotes with single quote
            match = regexp.MustCompile("''").ReplaceAllString(match, "'")
        }
        matches[i] = match
    }
    return matches, nil
}

// Custom validation function
func oneofCustom(fl validator.FieldLevel) bool {
    // Get the full tag value
    tag := fl.Param()

    // Parse the allowed values
    allowedValues, err := parseAllowedValues(tag)
    if err != nil {
        fmt.Printf("Error parsing allowed values: %v\n", err)
        return false
    }

    // Get the field value to validate
    fieldValue := fl.Field().String()

    // Check if the field value is in the allowed list
    for _, v := range allowedValues {
        if fieldValue == v {
            return true
        }
    }
    return false
}

Try it on the Go Playground.

转载请注明原文地址:http://www.anycun.com/QandA/1746131956a92015.html