'how to use the unique tag with a slice of struct in gin Go framework?

I'm trying to use gin framework to validate a slice of struct in order to ensure the slice is unique.

With The following code:

type CreateOrderParam struct {
    Items           []*CreateItemParam `form:"items" binding:"gt=0,required,unique,dive"`
}

type CreateItemParam struct {
    Dress *CreateDressParam `form:"dress" validate:"required"`
}

type CreateDressParam struct {
    Id int `form:"id" validate:"gt=0,required"`
}

I use the following JSON to test the code:

{
    "items": [
        {
            "dress": {
                "id": 1
            }
        },
        {
            "dress": {
                "id": 1
            }
        },
        {
            "dress": {
                "id": 3
            }
        }
    ]
}

However,the JSON pass validation.So **I doubt that the unique tag not being used correctly.**My question is:how to use the unique tag to validate the slice of struct?



Solution 1:[1]

In short, validation with unique on the slice elements amounts to comparing those elements with the == operator. Actually the implementation of gopkg.in/go-playground/validator.v10, which Gin depends on, constructs a map with reflection, but that's essentially the same thing.

So CreateItemParam — as all structs — compares with == by comparing the fields recursively, however the field Dress is a pointer type. Two pointer types are equal if they have the same memory address, irrespective of what the pointed values are.

If you change the struct definition to:

type CreateItemParam struct {
    Dress CreateDressParam `form:"dress" validate:"required"`
}

validation will then compare the actual CreateDressParam struct values instead of the pointers, and fail the unique check as you expect.

If you can't change the struct definition, you might have to register a custom validator into Gin's validator engine:

type CreateOrderParam struct {
    Items []*CreateItemParam `json:"items" binding:"gt=0,required,unique_struct_with_ptr,dive"`
}

func main() {
    r := gin.New()

    if ginvalidator, ok := binding.Validator.Engine().(*validator.Validate); ok {
        ginvalidator.RegisterValidation("unique_struct_with_ptr", func(fl validator.FieldLevel) bool {
            v := fl.Field().Interface().([]*CreateItemParam)
            // implement uniqueness for these structs
            // return true if validation passes, false otherwise
        })
    }
    // ... rest of router code
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 blackgreen