Skip to content

Commit d0d0c35

Browse files
authored
Feat: support validate struct without struct tag (#934)
1 parent d3e4be3 commit d0d0c35

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/go-playground/validator/v10"
6+
)
7+
8+
type Data struct {
9+
Name string
10+
Email string
11+
Details *Details
12+
}
13+
14+
type Details struct {
15+
FamilyMembers *FamilyMembers
16+
Salary string
17+
}
18+
19+
type FamilyMembers struct {
20+
FatherName string
21+
MotherName string
22+
}
23+
24+
type Data2 struct {
25+
Name string
26+
Age uint32
27+
}
28+
29+
var validate = validator.New()
30+
31+
func main() {
32+
validateStruct()
33+
// output
34+
// Key: 'Data2.Name' Error:Field validation for 'Name' failed on the 'min' tag
35+
// Key: 'Data2.Age' Error:Field validation for 'Age' failed on the 'max' tag
36+
37+
validateStructNested()
38+
// output
39+
// Key: 'Data.Name' Error:Field validation for 'Name' failed on the 'max' tag
40+
// Key: 'Data.Details.FamilyMembers' Error:Field validation for 'FamilyMembers' failed on the 'required' tag
41+
}
42+
43+
func validateStruct() {
44+
data := Data2{
45+
Name: "leo",
46+
Age: 1000,
47+
}
48+
49+
rules := map[string]string{
50+
"Name": "min=4,max=6",
51+
"Age": "min=4,max=6",
52+
}
53+
54+
validate.RegisterStructValidationMapRules(rules, Data2{})
55+
56+
err := validate.Struct(data)
57+
fmt.Println(err)
58+
fmt.Println()
59+
}
60+
61+
func validateStructNested() {
62+
data := Data{
63+
Name: "11sdfddd111",
64+
65+
Details: &Details{
66+
Salary: "1000",
67+
},
68+
}
69+
70+
rules1 := map[string]string{
71+
"Name": "min=4,max=6",
72+
"Email": "required,email",
73+
"Details": "required",
74+
}
75+
76+
rules2 := map[string]string{
77+
"Salary": "number",
78+
"FamilyMembers": "required",
79+
}
80+
81+
rules3 := map[string]string{
82+
"FatherName": "required,min=4,max=32",
83+
"MotherName": "required,min=4,max=32",
84+
}
85+
86+
validate.RegisterStructValidationMapRules(rules1, Data{})
87+
validate.RegisterStructValidationMapRules(rules2, Details{})
88+
validate.RegisterStructValidationMapRules(rules3, FamilyMembers{})
89+
err := validate.Struct(data)
90+
91+
fmt.Println(err)
92+
}

cache.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,13 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
114114
cs = &cStruct{name: sName, fields: make([]*cField, 0), fn: v.structLevelFuncs[typ]}
115115

116116
numFields := current.NumField()
117+
rules := v.rules[typ]
117118

118119
var ctag *cTag
119120
var fld reflect.StructField
120121
var tag string
121122
var customName string
122-
123+
123124
for i := 0; i < numFields; i++ {
124125

125126
fld = typ.Field(i)
@@ -128,7 +129,11 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
128129
continue
129130
}
130131

131-
tag = fld.Tag.Get(v.tagName)
132+
if rtag, ok := rules[fld.Name]; ok {
133+
tag = rtag
134+
} else {
135+
tag = fld.Tag.Get(v.tagName)
136+
}
132137

133138
if tag == skipValidationTag {
134139
continue

validator_instance.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type Validate struct {
8686
aliases map[string]string
8787
validations map[string]internalValidationFuncWrapper
8888
transTagFunc map[ut.Translator]map[string]TranslationFunc // map[<locale>]map[<tag>]TranslationFunc
89+
rules map[reflect.Type]map[string]string
8990
tagCache *tagCache
9091
structCache *structCache
9192
}
@@ -283,6 +284,33 @@ func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...i
283284
}
284285
}
285286

287+
// RegisterStructValidationMapRules registers validate map rules
288+
//
289+
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
290+
func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, types ...interface{}) {
291+
if v.rules == nil {
292+
v.rules = make(map[reflect.Type]map[string]string)
293+
}
294+
295+
deepCopyRules := make(map[string]string)
296+
for i, rule := range rules {
297+
deepCopyRules[i] = rule
298+
}
299+
300+
for _, t := range types {
301+
typ := reflect.TypeOf(t)
302+
303+
if typ.Kind() == reflect.Ptr {
304+
typ = typ.Elem()
305+
}
306+
307+
if typ.Kind() != reflect.Struct {
308+
continue
309+
}
310+
v.rules[typ] = deepCopyRules
311+
}
312+
}
313+
286314
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
287315
//
288316
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation

0 commit comments

Comments
 (0)