go-playground / validator

:100:Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving
MIT License
16.06k stars 1.29k forks source link

Can't validate both `keys` and the struct value of maps of structs #1260

Open bfabio opened 1 month ago

bfabio commented 1 month ago

Package version eg. v9, v10:

v10

Issue, Question or Enhancement:

When you have a map with struct values, it looks there is no way to validate both the keys and the struct, you can only validate the keys OR the struct.

Code sample, to showcase or reproduce:

This works as expected, returning a validation error because Name is required in Author.

package main

import (
    "fmt"

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

type Book struct {
    RelatedAuthors map[string]Author `validate:"dive"`
}

type Author struct {
    Name string `validate:"required"`
}

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

    book := Book{
        RelatedAuthors: map[string]Author{
            "12345": {},
        },
    }

    err := validate.Struct(book)
    if err != nil {
        fmt.Println("Validation error:", err)
    } else {
        fmt.Println("Validation successful")
    }
}
Validation error: Key: 'Book.RelatedAuthors[12345].Name' Error:Field validation for 'Name' failed on the 'required' tag

This validates successfully, even if Name is required

package main

import (
    "fmt"

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

type Book struct {
    RelatedAuthors map[string]Author `validate:"dive,keys,len=5,endkeys"`
}

type Author struct {
    Name string `validate:"required"`
}

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

    book := Book{
        RelatedAuthors: map[string]Author{
            "12345": {},
        },
    }

    err := validate.Struct(book)
    if err != nil {
        fmt.Println("Validation error:", err)
    } else {
        fmt.Println("Validation successful")
    }
}
Validation successful

The only difference between the two testcases is:

        RelatedAuthors map[string]Author `validate:"dive"`
        // vs
    RelatedAuthors map[string]Author `validate:"dive,keys,len=5,endkeys"`