validator icon indicating copy to clipboard operation
validator copied to clipboard

[Question/Bug]: Validation not working for map fields with struct values containing validation tags

Open hi-rai opened this issue 7 months ago • 4 comments

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

  1. I have a struct Outer with a field Inner that is a map of type map[string]Inner
  2. Outer.Inner is of type Inner and has a single field Value
type Inner struct {
	Value string `validate:"max=5"`
}

type Outer struct {
	Inner map[string]Inner `validate:"dive,keys,max=5,endkeys"`
}

Issue

  1. Current Behavior: Validation does not seem to apply to the map values (Inner structs) as expected.
  2. Workarounds a. Adding ,required after endkeys seems 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 only dive also triggers validation on values (but key validations are missed)
  3. 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 avatar May 13 '25 06:05 hi-rai

@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 avatar May 20 '25 03:05 nodivbyzero

@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

  1. Outer1 only validates the map keys
  2. Outer2 only validates the map values
  3. Outer3 works as a workaround for me but it doesn't allow zero values inside the map

hi-rai avatar May 20 '25 06:05 hi-rai

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!

nodivbyzero avatar May 24 '25 01:05 nodivbyzero

Hi I would like to work on this issue. Can someone kindly assign this to me?

JunaidIslam2105 avatar Jun 01 '25 04:06 JunaidIslam2105