Skip to content

Commit 61caf9d

Browse files
author
Dean Karn
authored
add map key validation support (#324)
1 parent 1304298 commit 61caf9d

File tree

9 files changed

+557
-66
lines changed

9 files changed

+557
-66
lines changed

README.md

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package validator
22
================
33
<img align="right" src="https://raw.githubusercontent.com/go-playground/validator/v9/logo.png">[![Join the chat at https://gitter.im/go-playground/validator](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-playground/validator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4-
![Project status](https://img.shields.io/badge/version-9.8.0-green.svg)
4+
![Project status](https://img.shields.io/badge/version-9.9.0-green.svg)
55
[![Build Status](https://semaphoreci.com/api/v1/joeybloggs/validator/branches/v9/badge.svg)](https://semaphoreci.com/joeybloggs/validator)
66
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=v9&service=github)](https://coveralls.io/github/go-playground/validator?branch=v9)
77
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@@ -13,7 +13,8 @@ Package validator implements value validations for structs and individual fields
1313
It has the following **unique** features:
1414

1515
- Cross Field and Cross Struct validations by using validation tags or custom validators.
16-
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
16+
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
17+
- Ability to dive into both map keys and values for validation
1718
- Handles type interface by determining it's underlying type prior to validation.
1819
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
1920
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
@@ -65,61 +66,69 @@ Please see http://godoc.org/gopkg.in/go-playground/validator.v9 for detailed usa
6566

6667
Benchmarks
6768
------
68-
###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.1 linux/amd64
69+
###### Run on MacBook Pro (15-inch, 2017) Go version go1.9.2 darwin/amd64
6970
```go
7071
go test -bench=. -benchmem=true
71-
BenchmarkFieldSuccess-8 20000000 87.2 ns/op 0 B/op 0 allocs/op
72-
BenchmarkFieldSuccessParallel-8 50000000 26.1 ns/op 0 B/op 0 allocs/op
73-
BenchmarkFieldFailure-8 5000000 299 ns/op 208 B/op 4 allocs/op
74-
BenchmarkFieldFailureParallel-8 20000000 100 ns/op 208 B/op 4 allocs/op
75-
BenchmarkFieldDiveSuccess-8 2000000 645 ns/op 201 B/op 11 allocs/op
76-
BenchmarkFieldDiveSuccessParallel-8 10000000 198 ns/op 201 B/op 11 allocs/op
77-
BenchmarkFieldDiveFailure-8 2000000 876 ns/op 412 B/op 16 allocs/op
78-
BenchmarkFieldDiveFailureParallel-8 5000000 268 ns/op 413 B/op 16 allocs/op
79-
BenchmarkFieldCustomTypeSuccess-8 10000000 228 ns/op 32 B/op 2 allocs/op
80-
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 70.0 ns/op 32 B/op 2 allocs/op
81-
BenchmarkFieldCustomTypeFailure-8 5000000 286 ns/op 208 B/op 4 allocs/op
82-
BenchmarkFieldCustomTypeFailureParallel-8 20000000 95.6 ns/op 208 B/op 4 allocs/op
83-
BenchmarkFieldOrTagSuccess-8 2000000 857 ns/op 16 B/op 1 allocs/op
84-
BenchmarkFieldOrTagSuccessParallel-8 3000000 397 ns/op 16 B/op 1 allocs/op
85-
BenchmarkFieldOrTagFailure-8 3000000 495 ns/op 224 B/op 5 allocs/op
86-
BenchmarkFieldOrTagFailureParallel-8 5000000 376 ns/op 224 B/op 5 allocs/op
87-
BenchmarkStructLevelValidationSuccess-8 10000000 226 ns/op 32 B/op 2 allocs/op
88-
BenchmarkStructLevelValidationSuccessParallel-8 20000000 68.4 ns/op 32 B/op 2 allocs/op
89-
BenchmarkStructLevelValidationFailure-8 3000000 497 ns/op 304 B/op 8 allocs/op
90-
BenchmarkStructLevelValidationFailureParallel-8 10000000 170 ns/op 304 B/op 8 allocs/op
91-
BenchmarkStructSimpleCustomTypeSuccess-8 3000000 420 ns/op 32 B/op 2 allocs/op
92-
BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 124 ns/op 32 B/op 2 allocs/op
93-
BenchmarkStructSimpleCustomTypeFailure-8 2000000 681 ns/op 424 B/op 9 allocs/op
94-
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 244 ns/op 440 B/op 10 allocs/op
95-
BenchmarkStructFilteredSuccess-8 2000000 659 ns/op 288 B/op 9 allocs/op
96-
BenchmarkStructFilteredSuccessParallel-8 10000000 211 ns/op 288 B/op 9 allocs/op
97-
BenchmarkStructFilteredFailure-8 3000000 482 ns/op 256 B/op 7 allocs/op
98-
BenchmarkStructFilteredFailureParallel-8 10000000 162 ns/op 256 B/op 7 allocs/op
99-
BenchmarkStructPartialSuccess-8 3000000 564 ns/op 256 B/op 6 allocs/op
100-
BenchmarkStructPartialSuccessParallel-8 10000000 180 ns/op 256 B/op 6 allocs/op
101-
BenchmarkStructPartialFailure-8 2000000 779 ns/op 480 B/op 11 allocs/op
102-
BenchmarkStructPartialFailureParallel-8 5000000 268 ns/op 480 B/op 11 allocs/op
103-
BenchmarkStructExceptSuccess-8 2000000 879 ns/op 496 B/op 12 allocs/op
72+
BenchmarkFieldSuccess-8 20000000 79.9 ns/op 0 B/op 0 allocs/op
73+
BenchmarkFieldSuccessParallel-8 50000000 25.0 ns/op 0 B/op 0 allocs/op
74+
BenchmarkFieldFailure-8 5000000 281 ns/op 208 B/op 4 allocs/op
75+
BenchmarkFieldFailureParallel-8 20000000 97.0 ns/op 208 B/op 4 allocs/op
76+
BenchmarkFieldArrayDiveSuccess-8 3000000 591 ns/op 201 B/op 11 allocs/op
77+
BenchmarkFieldArrayDiveSuccessParallel-8 10000000 195 ns/op 201 B/op 11 allocs/op
78+
BenchmarkFieldArrayDiveFailure-8 2000000 878 ns/op 412 B/op 16 allocs/op
79+
BenchmarkFieldArrayDiveFailureParallel-8 5000000 274 ns/op 413 B/op 16 allocs/op
80+
BenchmarkFieldMapDiveSuccess-8 1000000 1279 ns/op 432 B/op 18 allocs/op
81+
BenchmarkFieldMapDiveSuccessParallel-8 5000000 401 ns/op 432 B/op 18 allocs/op
82+
BenchmarkFieldMapDiveFailure-8 1000000 1060 ns/op 512 B/op 16 allocs/op
83+
BenchmarkFieldMapDiveFailureParallel-8 5000000 334 ns/op 512 B/op 16 allocs/op
84+
BenchmarkFieldMapDiveWithKeysSuccess-8 1000000 1462 ns/op 480 B/op 21 allocs/op
85+
BenchmarkFieldMapDiveWithKeysSuccessParallel-8 3000000 463 ns/op 480 B/op 21 allocs/op
86+
BenchmarkFieldMapDiveWithKeysFailure-8 1000000 1414 ns/op 721 B/op 21 allocs/op
87+
BenchmarkFieldMapDiveWithKeysFailureParallel-8 3000000 446 ns/op 721 B/op 21 allocs/op
88+
BenchmarkFieldCustomTypeSuccess-8 10000000 211 ns/op 32 B/op 2 allocs/op
89+
BenchmarkFieldCustomTypeSuccessParallel-8 20000000 65.9 ns/op 32 B/op 2 allocs/op
90+
BenchmarkFieldCustomTypeFailure-8 5000000 270 ns/op 208 B/op 4 allocs/op
91+
BenchmarkFieldCustomTypeFailureParallel-8 20000000 93.3 ns/op 208 B/op 4 allocs/op
92+
BenchmarkFieldOrTagSuccess-8 2000000 729 ns/op 16 B/op 1 allocs/op
93+
BenchmarkFieldOrTagSuccessParallel-8 5000000 367 ns/op 16 B/op 1 allocs/op
94+
BenchmarkFieldOrTagFailure-8 3000000 472 ns/op 224 B/op 5 allocs/op
95+
BenchmarkFieldOrTagFailureParallel-8 5000000 373 ns/op 224 B/op 5 allocs/op
96+
BenchmarkStructLevelValidationSuccess-8 10000000 201 ns/op 32 B/op 2 allocs/op
97+
BenchmarkStructLevelValidationSuccessParallel-8 20000000 66.3 ns/op 32 B/op 2 allocs/op
98+
BenchmarkStructLevelValidationFailure-8 3000000 468 ns/op 304 B/op 8 allocs/op
99+
BenchmarkStructLevelValidationFailureParallel-8 10000000 172 ns/op 304 B/op 8 allocs/op
100+
BenchmarkStructSimpleCustomTypeSuccess-8 5000000 376 ns/op 32 B/op 2 allocs/op
101+
BenchmarkStructSimpleCustomTypeSuccessParallel-8 20000000 126 ns/op 32 B/op 2 allocs/op
102+
BenchmarkStructSimpleCustomTypeFailure-8 2000000 646 ns/op 424 B/op 9 allocs/op
103+
BenchmarkStructSimpleCustomTypeFailureParallel-8 10000000 240 ns/op 440 B/op 10 allocs/op
104+
BenchmarkStructFilteredSuccess-8 3000000 582 ns/op 288 B/op 9 allocs/op
105+
BenchmarkStructFilteredSuccessParallel-8 10000000 198 ns/op 288 B/op 9 allocs/op
106+
BenchmarkStructFilteredFailure-8 3000000 447 ns/op 256 B/op 7 allocs/op
107+
BenchmarkStructFilteredFailureParallel-8 10000000 156 ns/op 256 B/op 7 allocs/op
108+
BenchmarkStructPartialSuccess-8 3000000 536 ns/op 256 B/op 6 allocs/op
109+
BenchmarkStructPartialSuccessParallel-8 10000000 175 ns/op 256 B/op 6 allocs/op
110+
BenchmarkStructPartialFailure-8 2000000 738 ns/op 480 B/op 11 allocs/op
111+
BenchmarkStructPartialFailureParallel-8 5000000 256 ns/op 480 B/op 11 allocs/op
112+
BenchmarkStructExceptSuccess-8 2000000 835 ns/op 496 B/op 12 allocs/op
104113
BenchmarkStructExceptSuccessParallel-8 10000000 163 ns/op 240 B/op 5 allocs/op
105-
BenchmarkStructExceptFailure-8 2000000 734 ns/op 464 B/op 10 allocs/op
106-
BenchmarkStructExceptFailureParallel-8 5000000 259 ns/op 464 B/op 10 allocs/op
107-
BenchmarkStructSimpleCrossFieldSuccess-8 3000000 432 ns/op 72 B/op 3 allocs/op
108-
BenchmarkStructSimpleCrossFieldSuccessParallel-8 10000000 129 ns/op 72 B/op 3 allocs/op
109-
BenchmarkStructSimpleCrossFieldFailure-8 2000000 671 ns/op 304 B/op 8 allocs/op
110-
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 229 ns/op 304 B/op 8 allocs/op
111-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 2000000 628 ns/op 80 B/op 4 allocs/op
112-
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 182 ns/op 80 B/op 4 allocs/op
113-
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 872 ns/op 320 B/op 9 allocs/op
114-
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 267 ns/op 320 B/op 9 allocs/op
115-
BenchmarkStructSimpleSuccess-8 5000000 274 ns/op 0 B/op 0 allocs/op
116-
BenchmarkStructSimpleSuccessParallel-8 20000000 79.0 ns/op 0 B/op 0 allocs/op
117-
BenchmarkStructSimpleFailure-8 2000000 647 ns/op 424 B/op 9 allocs/op
118-
BenchmarkStructSimpleFailureParallel-8 10000000 224 ns/op 424 B/op 9 allocs/op
119-
BenchmarkStructComplexSuccess-8 1000000 1557 ns/op 128 B/op 8 allocs/op
120-
BenchmarkStructComplexSuccessParallel-8 3000000 473 ns/op 128 B/op 8 allocs/op
121-
BenchmarkStructComplexFailure-8 300000 4373 ns/op 3041 B/op 53 allocs/op
122-
BenchmarkStructComplexFailureParallel-8 1000000 1554 ns/op 3041 B/op 53 allocs/op
114+
BenchmarkStructExceptFailure-8 2000000 682 ns/op 464 B/op 10 allocs/op
115+
BenchmarkStructExceptFailureParallel-8 10000000 244 ns/op 464 B/op 10 allocs/op
116+
BenchmarkStructSimpleCrossFieldSuccess-8 5000000 392 ns/op 72 B/op 3 allocs/op
117+
BenchmarkStructSimpleCrossFieldSuccessParallel-8 20000000 126 ns/op 72 B/op 3 allocs/op
118+
BenchmarkStructSimpleCrossFieldFailure-8 2000000 611 ns/op 304 B/op 8 allocs/op
119+
BenchmarkStructSimpleCrossFieldFailureParallel-8 10000000 214 ns/op 304 B/op 8 allocs/op
120+
BenchmarkStructSimpleCrossStructCrossFieldSuccess-8 3000000 567 ns/op 80 B/op 4 allocs/op
121+
BenchmarkStructSimpleCrossStructCrossFieldSuccessParallel-8 10000000 177 ns/op 80 B/op 4 allocs/op
122+
BenchmarkStructSimpleCrossStructCrossFieldFailure-8 2000000 807 ns/op 320 B/op 9 allocs/op
123+
BenchmarkStructSimpleCrossStructCrossFieldFailureParallel-8 5000000 268 ns/op 320 B/op 9 allocs/op
124+
BenchmarkStructSimpleSuccess-8 5000000 256 ns/op 0 B/op 0 allocs/op
125+
BenchmarkStructSimpleSuccessParallel-8 20000000 76.3 ns/op 0 B/op 0 allocs/op
126+
BenchmarkStructSimpleFailure-8 2000000 625 ns/op 424 B/op 9 allocs/op
127+
BenchmarkStructSimpleFailureParallel-8 10000000 219 ns/op 424 B/op 9 allocs/op
128+
BenchmarkStructComplexSuccess-8 1000000 1431 ns/op 128 B/op 8 allocs/op
129+
BenchmarkStructComplexSuccessParallel-8 3000000 427 ns/op 128 B/op 8 allocs/op
130+
BenchmarkStructComplexFailure-8 300000 4065 ns/op 3041 B/op 53 allocs/op
131+
BenchmarkStructComplexFailureParallel-8 1000000 1478 ns/op 3041 B/op 53 allocs/op
123132
```
124133

125134
Complementary Software

_examples/dive/main.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"gopkg.in/go-playground/validator.v9"
7+
)
8+
9+
// Test ...
10+
type Test struct {
11+
Array []string `validate:"required,gt=0,dive,required"`
12+
Map map[string]string `validate:"required,gt=0,dive,keys,keymax,endkeys,required,max=1000"`
13+
}
14+
15+
// use a single instance of Validate, it caches struct info
16+
var validate *validator.Validate
17+
18+
func main() {
19+
20+
validate = validator.New()
21+
22+
// registering alias so we can see the differences between
23+
// map key, value validation errors
24+
validate.RegisterAlias("keymax", "max=10")
25+
26+
var test Test
27+
28+
val(test)
29+
30+
test.Array = []string{""}
31+
test.Map = map[string]string{"test > than 10": ""}
32+
val(test)
33+
}
34+
35+
func val(test Test) {
36+
fmt.Println("testing")
37+
err := validate.Struct(test)
38+
fmt.Println(err)
39+
}

baked_in.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ func wrapFunc(fn Func) FuncCtx {
3232
var (
3333
restrictedTags = map[string]struct{}{
3434
diveTag: {},
35+
keysTag: {},
36+
endKeysTag: {},
3537
structOnlyTag: {},
3638
omitempty: {},
3739
skipValidationTag: {},

benchmarks_test.go

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func BenchmarkFieldFailureParallel(b *testing.B) {
5959
})
6060
}
6161

62-
func BenchmarkFieldDiveSuccess(b *testing.B) {
62+
func BenchmarkFieldArrayDiveSuccess(b *testing.B) {
6363

6464
validate := New()
6565

@@ -72,7 +72,7 @@ func BenchmarkFieldDiveSuccess(b *testing.B) {
7272
}
7373
}
7474

75-
func BenchmarkFieldDiveSuccessParallel(b *testing.B) {
75+
func BenchmarkFieldArrayDiveSuccessParallel(b *testing.B) {
7676

7777
validate := New()
7878

@@ -86,7 +86,7 @@ func BenchmarkFieldDiveSuccessParallel(b *testing.B) {
8686
})
8787
}
8888

89-
func BenchmarkFieldDiveFailure(b *testing.B) {
89+
func BenchmarkFieldArrayDiveFailure(b *testing.B) {
9090

9191
validate := New()
9292

@@ -98,7 +98,7 @@ func BenchmarkFieldDiveFailure(b *testing.B) {
9898
}
9999
}
100100

101-
func BenchmarkFieldDiveFailureParallel(b *testing.B) {
101+
func BenchmarkFieldArrayDiveFailureParallel(b *testing.B) {
102102

103103
validate := New()
104104

@@ -112,6 +112,112 @@ func BenchmarkFieldDiveFailureParallel(b *testing.B) {
112112
})
113113
}
114114

115+
func BenchmarkFieldMapDiveSuccess(b *testing.B) {
116+
117+
validate := New()
118+
119+
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
120+
121+
b.ResetTimer()
122+
123+
for n := 0; n < b.N; n++ {
124+
validate.Var(m, "required,dive,required")
125+
}
126+
}
127+
128+
func BenchmarkFieldMapDiveSuccessParallel(b *testing.B) {
129+
130+
validate := New()
131+
132+
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
133+
134+
b.ResetTimer()
135+
b.RunParallel(func(pb *testing.PB) {
136+
for pb.Next() {
137+
validate.Var(m, "required,dive,required")
138+
}
139+
})
140+
}
141+
142+
func BenchmarkFieldMapDiveFailure(b *testing.B) {
143+
144+
validate := New()
145+
146+
m := map[string]string{"": "", "val3": "val3"}
147+
148+
b.ResetTimer()
149+
for n := 0; n < b.N; n++ {
150+
validate.Var(m, "required,dive,required")
151+
}
152+
}
153+
154+
func BenchmarkFieldMapDiveFailureParallel(b *testing.B) {
155+
156+
validate := New()
157+
158+
m := map[string]string{"": "", "val3": "val3"}
159+
160+
b.ResetTimer()
161+
b.RunParallel(func(pb *testing.PB) {
162+
for pb.Next() {
163+
validate.Var(m, "required,dive,required")
164+
}
165+
})
166+
}
167+
168+
func BenchmarkFieldMapDiveWithKeysSuccess(b *testing.B) {
169+
170+
validate := New()
171+
172+
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
173+
174+
b.ResetTimer()
175+
176+
for n := 0; n < b.N; n++ {
177+
validate.Var(m, "required,dive,keys,required,endkeys,required")
178+
}
179+
}
180+
181+
func BenchmarkFieldMapDiveWithKeysSuccessParallel(b *testing.B) {
182+
183+
validate := New()
184+
185+
m := map[string]string{"val1": "val1", "val2": "val2", "val3": "val3"}
186+
187+
b.ResetTimer()
188+
b.RunParallel(func(pb *testing.PB) {
189+
for pb.Next() {
190+
validate.Var(m, "required,dive,keys,required,endkeys,required")
191+
}
192+
})
193+
}
194+
195+
func BenchmarkFieldMapDiveWithKeysFailure(b *testing.B) {
196+
197+
validate := New()
198+
199+
m := map[string]string{"": "", "val3": "val3"}
200+
201+
b.ResetTimer()
202+
for n := 0; n < b.N; n++ {
203+
validate.Var(m, "required,dive,keys,required,endkeys,required")
204+
}
205+
}
206+
207+
func BenchmarkFieldMapDiveWithKeysFailureParallel(b *testing.B) {
208+
209+
validate := New()
210+
211+
m := map[string]string{"": "", "val3": "val3"}
212+
213+
b.ResetTimer()
214+
b.RunParallel(func(pb *testing.PB) {
215+
for pb.Next() {
216+
validate.Var(m, "required,dive,keys,required,endkeys,required")
217+
}
218+
})
219+
}
220+
115221
func BenchmarkFieldCustomTypeSuccess(b *testing.B) {
116222

117223
validate := New()

0 commit comments

Comments
 (0)