[Question/Bug]: Validation not working for map fields with struct values containing validation tags
What happened?
I'm facing an issue with validating a struct that contains a map field. The map's keys and values need to be validated, but the current setup isn't working as expected.
Struct Definitions
- I have a struct
Outerwith a fieldInnerthat is a map of typemap[string]Inner Outer.Inneris of typeInnerand has a single fieldValue
type Inner struct {
Value string `validate:"max=5"`
}
type Outer struct {
Inner map[string]Inner `validate:"dive,keys,max=5,endkeys"`
}
Issue
- Current Behavior: Validation does not seem to apply to the map values (
Innerstructs) as expected. - Workarounds
a. Adding
,requiredafterendkeysseems to trigger the validation, but it also prevents (with validator.WithRequiredStructEnabled()) empty struct values (Inner{}), which is not the desired behavior. b. Removing key validations and retaining onlydivealso triggers validation on values (but key validations are missed) - Documentation: I did not find any similar mention or examples in the documentation.
Is this a bug ? Or am I supposed to do something differently to achieve my desired behavior ?
Version
v10.26.0
Example Code
package main
import (
"fmt"
"github.com/go-playground/validator/v10"
)
type Inner struct {
Value string `validate:"max=5"`
}
type Outer1 struct {
Inner map[string]Inner `validate:"dive,keys,max=5,endkeys"`
}
type Outer2 struct {
Inner map[string]Inner `validate:"dive"`
}
type Outer3 struct {
Inner map[string]Inner `validate:"dive,keys,max=5,endkeys,required"`
}
func main() {
v := validator.New(validator.WithRequiredStructEnabled())
// Should fail, but passes
fmt.Println("Outer1:", v.Struct(Outer1{Inner: map[string]Inner{
"a": {Value: "1234567890"},
}}))
// Should fail, and fails (but there is no validation for keys)
fmt.Println("Outer2:", v.Struct(Outer2{Inner: map[string]Inner{
"a": {Value: "1234567890"},
}}))
// Should fail, and fails (but has extra required validation, which is not intended)
fmt.Println("Outer3:", v.Struct(Outer3{Inner: map[string]Inner{
"a": {Value: "1234567890"},
}}))
// Should fail (due to required), but I don't want it to fail
fmt.Println("Outer3 (a):", v.Struct(Outer3{Inner: map[string]Inner{
"a": {Value: ""},
}}))
}
// Actual output:
// Outer1: <nil>
// Outer2: Key: 'Outer2.Inner[a].Value' Error:Field validation for 'Value' failed on the 'max' tag
// Outer3: Key: 'Outer3.Inner[a].Value' Error:Field validation for 'Value' failed on the 'max' tag
// Outer3 (a): Key: 'Outer3.Inner[a]' Error:Field validation for 'Inner[a]' failed on the 'required' tag
@hi-rai Regarding
// Should fail, but passes
fmt.Println("Outer1:", v.Struct(Outer1{Inner: map[string]Inner{
"a": {Value: "1234567890"},
}}))
This validation passes because Outer1 applies validation rules to the keys of the map. The max=5 constraint refers to the length of the key, not the value. In your example, the key is "a", which has a length of 1 - which is within the limit of 5.
Does that make sense?
@nodivbyzero Thanks for looking into it. I do get your point that the max=5 validates the keys.
But my intention here is to validate the Inner values in the map as well as its keys (in Outer1.Inner), but I can't seem to figure out a way to do so. I already have validations specified for the Inner struct and am using dive
Outer1only validates the map keysOuter2only validates the map valuesOuter3works as a workaround for me but it doesn't allow zero values inside the map
I looked into this issue and can confirm that validation doesn't work for map fields with struct values, although it does work for map fields with simple value types.
If anyone is interested in adding support for struct values, contributions are welcome!
Hi I would like to work on this issue. Can someone kindly assign this to me?