Codebase list golang-github-go-playground-validator-v10 / e8d470c
Merge pull request #229 from go-playground/v8-development Add FieldNamespace and NameNamespace + string comparison optimizations Dean Karn 8 years ago
5 changed file(s) with 298 addition(s) and 135 deletion(s). Raw diff Collapse all Expand all
307307
308308 Benchmarks
309309 ------
310 ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go 1.5.1
311 ```go
312 $ go test -cpu=4 -bench=. -benchmem=true
310 ###### Run on MacBook Pro (Retina, 15-inch, Late 2013) 2.6 GHz Intel Core i7 16 GB 1600 MHz DDR3 using Go version go1.5.2 darwin/amd64
311 ```go
312 go test -cpu=4 -bench=. -benchmem=true
313313 PASS
314 BenchmarkFieldSuccess-4 10000000 162 ns/op 0 B/op 0 allocs/op
315 BenchmarkFieldFailure-4 2000000 678 ns/op 400 B/op 4 allocs/op
316 BenchmarkFieldDiveSuccess-4 500000 3079 ns/op 480 B/op 27 allocs/op
317 BenchmarkFieldDiveFailure-4 300000 3584 ns/op 880 B/op 31 allocs/op
318 BenchmarkFieldCustomTypeSuccess-4 5000000 345 ns/op 32 B/op 2 allocs/op
319 BenchmarkFieldCustomTypeFailure-4 2000000 650 ns/op 400 B/op 4 allocs/op
320 BenchmarkFieldOrTagSuccess-4 1000000 1188 ns/op 16 B/op 1 allocs/op
321 BenchmarkFieldOrTagFailure-4 1000000 1088 ns/op 432 B/op 6 allocs/op
322 BenchmarkStructLevelValidationSuccess-4 2000000 689 ns/op 160 B/op 6 allocs/op
323 BenchmarkStructLevelValidationFailure-4 1000000 1290 ns/op 592 B/op 11 allocs/op
324 BenchmarkStructSimpleCustomTypeSuccess-4 2000000 911 ns/op 80 B/op 5 allocs/op
325 BenchmarkStructSimpleCustomTypeFailure-4 1000000 1446 ns/op 624 B/op 11 allocs/op
326 BenchmarkStructPartialSuccess-4 1000000 1221 ns/op 384 B/op 10 allocs/op
327 BenchmarkStructPartialFailure-4 1000000 1764 ns/op 800 B/op 15 allocs/op
328 BenchmarkStructExceptSuccess-4 2000000 941 ns/op 336 B/op 7 allocs/op
329 BenchmarkStructExceptFailure-4 1000000 1237 ns/op 384 B/op 10 allocs/op
330 BenchmarkStructSimpleCrossFieldSuccess-4 2000000 970 ns/op 128 B/op 6 allocs/op
331 BenchmarkStructSimpleCrossFieldFailure-4 1000000 1560 ns/op 560 B/op 11 allocs/op
332 BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1542 ns/op 176 B/op 9 allocs/op
333 BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2147 ns/op 608 B/op 14 allocs/op
334 BenchmarkStructSimpleSuccess-4 2000000 847 ns/op 48 B/op 3 allocs/op
335 BenchmarkStructSimpleFailure-4 1000000 1497 ns/op 624 B/op 11 allocs/op
336 BenchmarkStructSimpleSuccessParallel-4 5000000 257 ns/op 48 B/op 3 allocs/op
337 BenchmarkStructSimpleFailureParallel-4 2000000 586 ns/op 624 B/op 11 allocs/op
338 BenchmarkStructComplexSuccess-4 300000 5104 ns/op 496 B/op 29 allocs/op
339 BenchmarkStructComplexFailure-4 200000 9840 ns/op 3400 B/op 71 allocs/op
340 BenchmarkStructComplexSuccessParallel-4 1000000 1540 ns/op 496 B/op 29 allocs/op
341 BenchmarkStructComplexFailureParallel-4 500000 3478 ns/op 3400 B/op 71 allocs/op
314 BenchmarkFieldSuccess-4 10000000 176 ns/op 0 B/op 0 allocs/op
315 BenchmarkFieldFailure-4 2000000 727 ns/op 432 B/op 4 allocs/op
316 BenchmarkFieldDiveSuccess-4 500000 3220 ns/op 480 B/op 27 allocs/op
317 BenchmarkFieldDiveFailure-4 500000 3823 ns/op 912 B/op 31 allocs/op
318 BenchmarkFieldCustomTypeSuccess-4 5000000 368 ns/op 32 B/op 2 allocs/op
319 BenchmarkFieldCustomTypeFailure-4 2000000 699 ns/op 432 B/op 4 allocs/op
320 BenchmarkFieldOrTagSuccess-4 1000000 1265 ns/op 16 B/op 1 allocs/op
321 BenchmarkFieldOrTagFailure-4 1000000 1182 ns/op 464 B/op 6 allocs/op
322 BenchmarkStructLevelValidationSuccess-4 2000000 739 ns/op 176 B/op 6 allocs/op
323 BenchmarkStructLevelValidationFailure-4 1000000 1368 ns/op 640 B/op 11 allocs/op
324 BenchmarkStructSimpleCustomTypeSuccess-4 2000000 965 ns/op 80 B/op 5 allocs/op
325 BenchmarkStructSimpleCustomTypeFailure-4 1000000 1561 ns/op 688 B/op 11 allocs/op
326 BenchmarkStructPartialSuccess-4 1000000 1285 ns/op 384 B/op 10 allocs/op
327 BenchmarkStructPartialFailure-4 1000000 1879 ns/op 832 B/op 15 allocs/op
328 BenchmarkStructExceptSuccess-4 2000000 1038 ns/op 336 B/op 7 allocs/op
329 BenchmarkStructExceptFailure-4 1000000 1330 ns/op 384 B/op 10 allocs/op
330 BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1081 ns/op 128 B/op 6 allocs/op
331 BenchmarkStructSimpleCrossFieldFailure-4 1000000 1737 ns/op 592 B/op 11 allocs/op
332 BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1790 ns/op 192 B/op 10 allocs/op
333 BenchmarkStructSimpleCrossStructCrossFieldFailure-4 500000 2431 ns/op 656 B/op 15 allocs/op
334 BenchmarkStructSimpleSuccess-4 2000000 950 ns/op 48 B/op 3 allocs/op
335 BenchmarkStructSimpleFailure-4 1000000 1672 ns/op 688 B/op 11 allocs/op
336 BenchmarkStructSimpleSuccessParallel-4 5000000 271 ns/op 48 B/op 3 allocs/op
337 BenchmarkStructSimpleFailureParallel-4 2000000 670 ns/op 688 B/op 11 allocs/op
338 BenchmarkStructComplexSuccess-4 300000 5828 ns/op 544 B/op 32 allocs/op
339 BenchmarkStructComplexFailure-4 200000 11382 ns/op 3912 B/op 77 allocs/op
340 BenchmarkStructComplexSuccessParallel-4 1000000 1739 ns/op 544 B/op 32 allocs/op
341 BenchmarkStructComplexFailureParallel-4 300000 4682 ns/op 3912 B/op 77 allocs/op
342342 ```
343343
344344 How to Contribute
755755 return false
756756 }
757757
758 if len(url.Scheme) == 0 {
758 if url.Scheme == blank {
759759 return false
760760 }
761761
77 )
88
99 const (
10 dash = "-"
1011 blank = ""
1112 namespaceSeparator = "."
1213 leftBracket = "["
8586 return current, kind, false
8687 }
8788
88 if len(namespace) == 0 {
89 if namespace == blank {
8990 return current, kind, true
9091 }
9192
261262
262263 fld = typ.Field(i)
263264
264 if len(fld.PkgPath) != 0 {
265 if fld.PkgPath != blank {
265266 continue
266267 }
267268
272273 }
273274
274275 customName = fld.Name
275 if len(v.fieldNameTag) != 0 {
276 if v.fieldNameTag != blank {
276277
277278 name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
278279
308309
309310 func (v *Validate) parseTagsRecursive(cTag *cachedTag, tag, fieldName, alias string, isAlias bool) bool {
310311
311 if len(tag) == 0 {
312 if tag == blank {
312313 return true
313314 }
314315
364365 tagVal.tag = alias
365366 }
366367
367 if len(key) == 0 {
368 if key == blank {
368369 panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, fieldName)))
369370 }
370371
5151 TopStruct reflect.Value
5252 CurrentStruct reflect.Value
5353 errPrefix string
54 nsPrefix string
5455 errs ValidationErrors
5556 v *Validate
5657 }
5960 // Example: had a triple nested struct User, ContactInfo, Country and ran errs := validate.Struct(country)
6061 // from within a User struct level validation would call this method like so:
6162 // ReportValidationErrors("ContactInfo.", errs)
63 // NOTE: relativeKey can contain both the Field Relative and Custom name relative paths
64 // i.e. ReportValidationErrors("ContactInfo.|cInfo", errs) where cInfo represents say the JSON name of
65 // the relative path; this will be split into 2 variables in the next valiator version.
6266 func (sl *StructLevel) ReportValidationErrors(relativeKey string, errs ValidationErrors) {
6367 for _, e := range errs {
64 sl.errs[sl.errPrefix+relativeKey+e.Field] = e
68
69 idx := strings.Index(relativeKey, "|")
70 var rel string
71 var cRel string
72
73 if idx != -1 {
74 rel = relativeKey[:idx]
75 cRel = relativeKey[idx+1:]
76 } else {
77 rel = relativeKey
78 }
79
80 key := sl.errPrefix + rel + e.Field
81
82 e.FieldNamespace = key
83 e.NameNamespace = sl.nsPrefix + cRel + e.Name
84
85 sl.errs[key] = e
6586 }
6687 }
6788
7293
7394 field, kind := sl.v.ExtractType(field)
7495
75 if len(fieldName) == 0 {
96 if fieldName == blank {
7697 panic(fieldNameRequired)
7798 }
7899
79 if len(customName) == 0 {
100 if customName == blank {
80101 customName = fieldName
81102 }
82103
83 if len(tag) == 0 {
104 if tag == blank {
84105 panic(tagRequired)
85106 }
107
108 ns := sl.errPrefix + fieldName
86109
87110 switch kind {
88111 case reflect.Invalid:
89 sl.errs[sl.errPrefix+fieldName] = &FieldError{
90 Name: customName,
91 Field: fieldName,
92 Tag: tag,
93 ActualTag: tag,
94 Param: blank,
95 Kind: kind,
112 sl.errs[ns] = &FieldError{
113 FieldNamespace: ns,
114 NameNamespace: sl.nsPrefix + customName,
115 Name: customName,
116 Field: fieldName,
117 Tag: tag,
118 ActualTag: tag,
119 Param: blank,
120 Kind: kind,
96121 }
97122 default:
98 sl.errs[sl.errPrefix+fieldName] = &FieldError{
99 Name: customName,
100 Field: fieldName,
101 Tag: tag,
102 ActualTag: tag,
103 Param: blank,
104 Value: field.Interface(),
105 Kind: kind,
106 Type: field.Type(),
123 sl.errs[ns] = &FieldError{
124 FieldNamespace: ns,
125 NameNamespace: sl.nsPrefix + customName,
126 Name: customName,
127 Field: fieldName,
128 Tag: tag,
129 ActualTag: tag,
130 Param: blank,
131 Value: field.Interface(),
132 Kind: kind,
133 Type: field.Type(),
107134 }
108135 }
109136 }
177204 // FieldError contains a single field's validation error along
178205 // with other properties that may be needed for error message creation
179206 type FieldError struct {
180 Field string
181 Name string
182 Tag string
183 ActualTag string
184 Kind reflect.Kind
185 Type reflect.Type
186 Param string
187 Value interface{}
207 FieldNamespace string
208 NameNamespace string
209 Field string
210 Name string
211 Tag string
212 ActualTag string
213 Kind reflect.Kind
214 Type reflect.Type
215 Param string
216 Value interface{}
188217 }
189218
190219 // New creates a new Validate instance for use.
240269 func (v *Validate) RegisterValidation(key string, fn Func) error {
241270 v.initCheck()
242271
243 if len(key) == 0 {
272 if key == blank {
244273 return errors.New("Function Key cannot be empty")
245274 }
246275
305334 errs := v.errsPool.Get().(ValidationErrors)
306335 fieldVal := reflect.ValueOf(field)
307336
308 v.traverseField(fieldVal, fieldVal, fieldVal, blank, errs, false, tag, blank, blank, false, false, nil, nil)
337 v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, tag, blank, blank, false, false, nil, nil)
309338
310339 if len(errs) == 0 {
311340 v.errsPool.Put(errs)
325354 errs := v.errsPool.Get().(ValidationErrors)
326355 topVal := reflect.ValueOf(val)
327356
328 v.traverseField(topVal, topVal, reflect.ValueOf(field), blank, errs, false, tag, blank, blank, false, false, nil, nil)
357 v.traverseField(topVal, topVal, reflect.ValueOf(field), blank, blank, errs, false, tag, blank, blank, false, false, nil, nil)
329358
330359 if len(errs) == 0 {
331360 v.errsPool.Put(errs)
383412
384413 errs := v.errsPool.Get().(ValidationErrors)
385414
386 v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, false, m, false)
415 v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, false, m, false)
387416
388417 if len(errs) == 0 {
389418 v.errsPool.Put(errs)
405434 m := map[string]*struct{}{}
406435
407436 for _, key := range fields {
408 m[name+"."+key] = emptyStructPtr
437 m[name+namespaceSeparator+key] = emptyStructPtr
409438 }
410439
411440 errs := v.errsPool.Get().(ValidationErrors)
412441
413 v.tranverseStruct(sv, sv, sv, blank, errs, true, len(m) != 0, true, m, false)
442 v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, true, m, false)
414443
415444 if len(errs) == 0 {
416445 v.errsPool.Put(errs)
429458 errs := v.errsPool.Get().(ValidationErrors)
430459 sv := reflect.ValueOf(current)
431460
432 v.tranverseStruct(sv, sv, sv, blank, errs, true, false, false, nil, false)
461 v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, false, false, nil, false)
433462
434463 if len(errs) == 0 {
435464 v.errsPool.Put(errs)
440469 }
441470
442471 // tranverseStruct traverses a structs fields and then passes them to be validated by traverseField
443 func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, isStructOnly bool) {
472 func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, isStructOnly bool) {
444473
445474 if current.Kind() == reflect.Ptr && !current.IsNil() {
446475 current = current.Elem()
456485 sName := typ.Name()
457486
458487 if useStructName {
459 errPrefix += sName + "."
488 errPrefix += sName + namespaceSeparator
489
490 if v.fieldNameTag != blank {
491 nsPrefix += sName + namespaceSeparator
492 }
460493 }
461494
462495 // structonly tag present don't tranverseFields
468501
469502 // is anonymous struct, cannot parse or cache as
470503 // it has no name to index by
471 if len(sName) == 0 {
504 if sName == blank {
472505
473506 var customName string
474507 var ok bool
478511
479512 fld = typ.Field(i)
480513
481 if len(fld.PkgPath) != 0 {
514 if fld.PkgPath != blank {
482515 continue
483516 }
484517
492525 }
493526
494527 customName = fld.Name
495 if v.fieldNameTag != "" {
528
529 if v.fieldNameTag != blank {
496530
497531 name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
498532
499533 // dash check is for json "-" means don't output in json
500 if name != "" && name != "-" {
534 if name != blank && name != dash {
501535 customName = name
502536 }
503537 }
504538
505 v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude, nil)
539 v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, nsPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude, nil)
506540 }
507541 } else {
508542 s, ok := v.structCache.Get(typ)
522556 }
523557 fld = typ.Field(i)
524558
525 v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, f.CachedTag.tag, fld.Name, f.AltName, partial, exclude, includeExclude, f.CachedTag)
559 v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, nsPrefix, errs, true, f.CachedTag.tag, fld.Name, f.AltName, partial, exclude, includeExclude, f.CachedTag)
526560 }
527561 }
528562 }
530564 // check if any struct level validations, after all field validations already checked.
531565 if v.hasStructLevelFuncs {
532566 if fn, ok := v.structLevelFuncs[current.Type()]; ok {
533 fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, errs: errs})
567 fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs})
534568 }
535569 }
536570 }
537571
538572 // traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
539 func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
573 func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, isStructField bool, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
540574
541575 if tag == skipValidationTag {
542576 return
560594 return
561595 }
562596
563 if len(tag) > 0 {
597 if tag != blank {
598
599 ns := errPrefix + name
564600
565601 if kind == reflect.Invalid {
566 errs[errPrefix+name] = &FieldError{
567 Name: customName,
568 Field: name,
569 Tag: cTag.tags[0].tag,
570 ActualTag: cTag.tags[0].tagVals[0][0],
571 Param: cTag.tags[0].tagVals[0][1],
572 Kind: kind,
602 errs[ns] = &FieldError{
603 FieldNamespace: ns,
604 NameNamespace: nsPrefix + customName,
605 Name: customName,
606 Field: name,
607 Tag: cTag.tags[0].tag,
608 ActualTag: cTag.tags[0].tagVals[0][0],
609 Param: cTag.tags[0].tagVals[0][1],
610 Kind: kind,
573611 }
574612 return
575613 }
576614
577 errs[errPrefix+name] = &FieldError{
578 Name: customName,
579 Field: name,
580 Tag: cTag.tags[0].tag,
581 ActualTag: cTag.tags[0].tagVals[0][0],
582 Param: cTag.tags[0].tagVals[0][1],
583 Value: current.Interface(),
584 Kind: kind,
585 Type: current.Type(),
615 errs[ns] = &FieldError{
616 FieldNamespace: ns,
617 NameNamespace: nsPrefix + customName,
618 Name: customName,
619 Field: name,
620 Tag: cTag.tags[0].tag,
621 ActualTag: cTag.tags[0].tagVals[0][0],
622 Param: cTag.tags[0].tagVals[0][1],
623 Value: current.Interface(),
624 Kind: kind,
625 Type: current.Type(),
586626 }
587627
588628 return
602642 return
603643 }
604644
605 v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false, partial, exclude, includeExclude, cTag.isStructOnly)
645 v.tranverseStruct(topStruct, current, current, errPrefix+name+namespaceSeparator, nsPrefix+customName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cTag.isStructOnly)
606646 return
607647 }
608648 }
609649
610 if len(tag) == 0 {
650 if tag == blank {
611651 return
612652 }
613653
636676 continue
637677 }
638678
639 if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, valTag, name, customName) {
679 if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, nsPrefix, errs, valTag, name, customName) {
640680 return
641681 }
642682 }
646686 // or panic ;)
647687 switch kind {
648688 case reflect.Slice, reflect.Array:
649 v.traverseSlice(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
689 v.traverseSlice(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
650690 case reflect.Map:
651 v.traverseMap(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
691 v.traverseMap(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
652692 default:
653693 // throw error, if not a slice or map then should not have gotten here
654694 // bad dive tag
658698 }
659699
660700 // traverseSlice traverses a Slice or Array's elements and passes them to traverseField for validation
661 func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
701 func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
662702
663703 for i := 0; i < current.Len(); i++ {
664 v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i), fmt.Sprintf(arrayIndexFieldName, customName, i), partial, exclude, includeExclude, cTag)
704 v.traverseField(topStruct, currentStruct, current.Index(i), errPrefix, nsPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i), fmt.Sprintf(arrayIndexFieldName, customName, i), partial, exclude, includeExclude, cTag)
665705 }
666706 }
667707
668708 // traverseMap traverses a map's elements and passes them to traverseField for validation
669 func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
709 func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
670710
671711 for _, key := range current.MapKeys() {
672 v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()), fmt.Sprintf(mapIndexFieldName, customName, key.Interface()), partial, exclude, includeExclude, cTag)
712 v.traverseField(topStruct, currentStruct, current.MapIndex(key), errPrefix, nsPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()), fmt.Sprintf(mapIndexFieldName, customName, key.Interface()), partial, exclude, includeExclude, cTag)
673713 }
674714 }
675715
676716 // validateField validates a field based on the provided tag's key and param values and returns true if there is an error or false if all ok
677 func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, valTag *tagVals, name, customName string) bool {
717 func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, nsPrefix string, errs ValidationErrors, valTag *tagVals, name, customName string) bool {
678718
679719 var valFunc Func
680720 var ok bool
697737 errTag += orSeparator + val[0]
698738 }
699739
740 ns := errPrefix + name
741
700742 if valTag.isAlias {
701 errs[errPrefix+name] = &FieldError{
702 Name: customName,
703 Field: name,
704 Tag: valTag.tag,
705 ActualTag: errTag[1:],
706 Value: current.Interface(),
707 Type: currentType,
708 Kind: currentKind,
743 errs[ns] = &FieldError{
744 FieldNamespace: ns,
745 NameNamespace: nsPrefix + customName,
746 Name: customName,
747 Field: name,
748 Tag: valTag.tag,
749 ActualTag: errTag[1:],
750 Value: current.Interface(),
751 Type: currentType,
752 Kind: currentKind,
709753 }
710754 } else {
711755 errs[errPrefix+name] = &FieldError{
712 Name: customName,
713 Field: name,
714 Tag: errTag[1:],
715 ActualTag: errTag[1:],
716 Value: current.Interface(),
717 Type: currentType,
718 Kind: currentKind,
756 FieldNamespace: ns,
757 NameNamespace: nsPrefix + customName,
758 Name: customName,
759 Field: name,
760 Tag: errTag[1:],
761 ActualTag: errTag[1:],
762 Value: current.Interface(),
763 Type: currentType,
764 Kind: currentKind,
719765 }
720766 }
721767
731777 return false
732778 }
733779
734 errs[errPrefix+name] = &FieldError{
735 Name: customName,
736 Field: name,
737 Tag: valTag.tag,
738 ActualTag: valTag.tagVals[0][0],
739 Value: current.Interface(),
740 Param: valTag.tagVals[0][1],
741 Type: currentType,
742 Kind: currentKind,
780 ns := errPrefix + name
781
782 errs[ns] = &FieldError{
783 FieldNamespace: ns,
784 NameNamespace: nsPrefix + customName,
785 Name: customName,
786 Field: name,
787 Tag: valTag.tag,
788 ActualTag: valTag.tagVals[0][0],
789 Value: current.Interface(),
790 Param: valTag.tagVals[0][1],
791 Type: currentType,
792 Kind: currentKind,
743793 }
744794
745795 return true
283283 structLevel.ReportValidationErrors("Inner1.", errs.(ValidationErrors))
284284 }
285285
286 func StructValidationTestStructReturnValidationErrors2(v *Validate, structLevel *StructLevel) {
287
288 s := structLevel.CurrentStruct.Interface().(TestStructReturnValidationErrors)
289
290 errs := v.Struct(s.Inner1.Inner2)
291 if errs == nil {
292 return
293 }
294
295 structLevel.ReportValidationErrors("Inner1.|Inner1JSON.", errs.(ValidationErrors))
296 }
297
286298 type TestStructReturnValidationErrorsInner2 struct {
287 String string `validate:"required"`
299 String string `validate:"required" json:"JSONString"`
288300 }
289301
290302 type TestStructReturnValidationErrorsInner1 struct {
292304 }
293305
294306 type TestStructReturnValidationErrors struct {
295 Inner1 *TestStructReturnValidationErrorsInner1
307 Inner1 *TestStructReturnValidationErrorsInner1 `json:"Inner1JSON"`
308 }
309
310 type Inner2Namespace struct {
311 String []string `validate:"dive,required" json:"JSONString"`
312 }
313
314 type Inner1Namespace struct {
315 Inner2 *Inner2Namespace `json:"Inner2JSON"`
316 }
317
318 type Namespace struct {
319 Inner1 *Inner1Namespace `json:"Inner1JSON"`
320 }
321
322 func TestNameNamespace(t *testing.T) {
323
324 config := &Config{
325 TagName: "validate",
326 FieldNameTag: "json",
327 }
328
329 v1 := New(config)
330 i2 := &Inner2Namespace{String: []string{"ok", "ok", "ok"}}
331 i1 := &Inner1Namespace{Inner2: i2}
332 ns := &Namespace{Inner1: i1}
333
334 errs := v1.Struct(ns)
335 Equal(t, errs, nil)
336
337 i2.String[1] = ""
338
339 errs = v1.Struct(ns)
340 NotEqual(t, errs, nil)
341
342 ve := errs.(ValidationErrors)
343 Equal(t, len(ve), 1)
344 AssertError(t, errs, "Namespace.Inner1.Inner2.String[1]", "String[1]", "required")
345
346 fe, ok := ve["Namespace.Inner1.Inner2.String[1]"]
347 Equal(t, ok, true)
348
349 Equal(t, fe.Field, "String[1]")
350 Equal(t, fe.FieldNamespace, "Namespace.Inner1.Inner2.String[1]")
351 Equal(t, fe.Name, "JSONString[1]")
352 Equal(t, fe.NameNamespace, "Namespace.Inner1JSON.Inner2JSON.JSONString[1]")
296353 }
297354
298355 func TestAnonymous(t *testing.T) {
376433
377434 errs = v1.Struct(val)
378435 NotEqual(t, errs, nil)
436 Equal(t, len(errs.(ValidationErrors)), 2)
379437 AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.Inner2.String", "String", "required")
438 // this is an extra error reported from struct validation
439 AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.String", "String", "required")
440 }
441
442 func TestStructLevelReturnValidationErrorsWithJSON(t *testing.T) {
443 config := &Config{
444 TagName: "validate",
445 FieldNameTag: "json",
446 }
447
448 v1 := New(config)
449 v1.RegisterStructValidation(StructValidationTestStructReturnValidationErrors2, TestStructReturnValidationErrors{})
450
451 inner2 := &TestStructReturnValidationErrorsInner2{
452 String: "I'm HERE",
453 }
454
455 inner1 := &TestStructReturnValidationErrorsInner1{
456 Inner2: inner2,
457 }
458
459 val := &TestStructReturnValidationErrors{
460 Inner1: inner1,
461 }
462
463 errs := v1.Struct(val)
464 Equal(t, errs, nil)
465
466 inner2.String = ""
467
468 errs = v1.Struct(val)
469 NotEqual(t, errs, nil)
470 Equal(t, len(errs.(ValidationErrors)), 2)
471 AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.Inner2.String", "String", "required")
472 // this is an extra error reported from struct validation, it's a badly formatted one, but on purpose
473 AssertError(t, errs, "TestStructReturnValidationErrors.Inner1.String", "String", "required")
474
475 fe, ok := errs.(ValidationErrors)["TestStructReturnValidationErrors.Inner1.Inner2.String"]
476 Equal(t, ok, true)
477
478 // check for proper JSON namespace
479 Equal(t, fe.Field, "String")
480 Equal(t, fe.Name, "JSONString")
481 Equal(t, fe.FieldNamespace, "TestStructReturnValidationErrors.Inner1.Inner2.String")
482 Equal(t, fe.NameNamespace, "TestStructReturnValidationErrors.Inner1JSON.Inner2.JSONString")
483
484 fe, ok = errs.(ValidationErrors)["TestStructReturnValidationErrors.Inner1.String"]
485 Equal(t, ok, true)
486
487 // check for proper JSON namespace
488 Equal(t, fe.Field, "String")
489 Equal(t, fe.Name, "JSONString")
490 Equal(t, fe.FieldNamespace, "TestStructReturnValidationErrors.Inner1.String")
491 Equal(t, fe.NameNamespace, "TestStructReturnValidationErrors.Inner1JSON.JSONString")
380492 }
381493
382494 func TestStructLevelValidations(t *testing.T) {