go-ozzo / ozzo-validation

An idiomatic Go (golang) validation package. Supports configurable and extensible validation rules (validators) using normal language constructs instead of error-prone struct tags.
MIT License
3.73k stars 224 forks source link

Nested struct validation based on field #136

Open RobinVdBroeck opened 3 years ago

RobinVdBroeck commented 3 years ago

I would love for it to be possible to validate a field that is deeply nested. You can find an example of what I mean at: https://play.golang.org/p/bVjDNqkVzQj

In my use case, adding a Validate() function to the nested struct is not possible. The validation of the nested struct is not always the same, and depends on some external factors.

ki4jnq commented 3 years ago

Hey, I found that I could address this with my own helper like this:

func Nested(target interface{}, fieldRules ...*validation.FieldRules) *validation.FieldRules {
    return validation.Field(target, validation.By(func(value interface{}) error {
        valueV := reflect.Indirect(reflect.ValueOf(value))
        if valueV.CanAddr() {
            addr := valueV.Addr().Interface()
            return validation.ValidateStruct(addr, fieldRules...)
        }
        return validation.ValidateStruct(target, fieldRules...)
    }))
}

To use it I can do this:

wrapper := Wrapper{
    Attr1: 6,
    FieldOne: Foo{
        FieldThree: "Test",
        FieldFour:  "",
    },
    FieldTwo: &Bar{
        FieldFive: 16,
    },
}
err := validation.ValidateStruct(&wrapper,
    validation.Field(&wrapper.Attr1, validation.Required),
    Nested(&wrapper.FieldOne,
        validation.Field(&wrapper.FieldOne.FieldThree, validation.Required),
        validation.Field(&wrapper.FieldOne.FieldFour, validation.Required),
    ),
    Nested(&wrapper.FieldTwo,
        validation.Field(&wrapper.FieldTwo.FieldFive, validation.Required, validation.Max(10)),
    ),
)

Here's a playground link based on your initial example: https://play.golang.org/p/oLUVw6cikws

Haven't used it much so can't guarantee it's bug free, but seems to work as intended.

guiguan commented 3 years ago

@ki4jnq to access FieldFive, you may get nil pointer panic

ki4jnq commented 3 years ago

@ki4jnq to access FieldFive, you may get nil pointer panic

You mean when doing this: &wrapper.FieldTwo.FieldFive? True, your code would have to check that manually if it's a concern.