@@ -201,38 +201,143 @@ func ValidateValuer(field reflect.Value) interface{} {
201201}
202202```
203203
204+ Struct Level Validation
205+ ``` go
206+ package main
207+
208+ import (
209+ " fmt"
210+ " reflect"
211+
212+ " gopkg.in/go-playground/validator.v8"
213+ )
214+
215+ // User contains user information
216+ type User struct {
217+ FirstName string ` json:"fname"`
218+ LastName string ` json:"lname"`
219+ Age uint8 ` validate:"gte=0,lte=130"`
220+ Email string ` validate:"required,email"`
221+ FavouriteColor string ` validate:"hexcolor|rgb|rgba"`
222+ Addresses []*Address ` validate:"required,dive,required"` // a person can have a home and cottage...
223+ }
224+
225+ // Address houses a users address information
226+ type Address struct {
227+ Street string ` validate:"required"`
228+ City string ` validate:"required"`
229+ Planet string ` validate:"required"`
230+ Phone string ` validate:"required"`
231+ }
232+
233+ var validate *validator.Validate
234+
235+ func main () {
236+
237+ config := &validator.Config {TagName: " validate" }
238+
239+ validate = validator.New (config)
240+ validate.RegisterStructValidation (UserStructLevelValidation, User{})
241+
242+ validateStruct ()
243+ }
244+
245+ // UserStructLevelValidation contains custom struct level validations that don't always
246+ // make sense at the field validation level. For Example this function validates that either
247+ // FirstName or LastName exist; could have done that with a custom field validation but then
248+ // would have had to add it to both fields duplicating the logic + overhead, this way it's
249+ // only validated once.
250+ //
251+ // NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
252+ // hooks right into validator and you can combine with validation tags and still have a
253+ // common error output format.
254+ func UserStructLevelValidation (v *validator .Validate , structLevel *validator .StructLevel ) {
255+
256+ user := structLevel.CurrentStruct .Interface ().(User)
257+
258+ if len (user.FirstName ) == 0 && len (user.LastName ) == 0 {
259+ structLevel.ReportError (reflect.ValueOf (user.FirstName ), " FirstName" , " fname" , " fnameorlname" )
260+ structLevel.ReportError (reflect.ValueOf (user.LastName ), " LastName" , " lname" , " fnameorlname" )
261+ }
262+
263+ // plus can to more, even with different tag than "fnameorlname"
264+ }
265+
266+ func validateStruct () {
267+
268+ address := &Address{
269+ Street: " Eavesdown Docks" ,
270+ Planet: " Persphone" ,
271+ Phone: " none" ,
272+ City: " Unknown" ,
273+ }
274+
275+ user := &User{
276+ FirstName: " " ,
277+ LastName: " " ,
278+ Age: 45 ,
279+ 280+ FavouriteColor: " #000" ,
281+ Addresses: []*Address{address},
282+ }
283+
284+ // returns nil or ValidationErrors ( map[string]*FieldError )
285+ errs := validate.Struct (user)
286+
287+ if errs != nil {
288+
289+ fmt.Println (errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag
290+ // Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag
291+ err := errs.(validator.ValidationErrors )[" User.FirstName" ]
292+ fmt.Println (err.Field ) // output: FirstName
293+ fmt.Println (err.Tag ) // output: fnameorlname
294+ fmt.Println (err.Kind ) // output: string
295+ fmt.Println (err.Type ) // output: string
296+ fmt.Println (err.Param ) // output:
297+ fmt.Println (err.Value ) // output:
298+
299+ // from here you can create your own error messages in whatever language you wish
300+ return
301+ }
302+
303+ // save user to database
304+ }
305+ ```
306+
204307Benchmarks
205308------
206309###### 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
207310``` go
208311$ go test -cpu=4 -bench=. -benchmem=true
209312PASS
210- BenchmarkFieldSuccess-4 5000000 291 ns/op 16 B /op 1 allocs/op
211- BenchmarkFieldFailure-4 5000000 294 ns/op 16 B /op 1 allocs/op
212- BenchmarkFieldDiveSuccess-4 500000 3498 ns/op 528 B /op 28 allocs/op
213- BenchmarkFieldDiveFailure-4 300000 4094 ns/op 928 B /op 32 allocs/op
214- BenchmarkFieldCustomTypeSuccess-4 3000000 460 ns/op 32 B /op 2 allocs/op
215- BenchmarkFieldCustomTypeFailure-4 2000000 758 ns/op 400 B /op 4 allocs/op
216- BenchmarkFieldOrTagSuccess-4 1000000 1393 ns/op 32 B /op 2 allocs/op
217- BenchmarkFieldOrTagFailure-4 1000000 1181 ns/op 432 B /op 6 allocs/op
218- BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1218 ns/op 80 B /op 5 allocs/op
219- BenchmarkStructSimpleCustomTypeFailure-4 1000000 1748 ns/op 624 B /op 11 allocs/op
220- BenchmarkStructPartialSuccess-4 1000000 1392 ns/op 400 B /op 11 allocs/op
221- BenchmarkStructPartialFailure-4 1000000 1938 ns/op 816 B /op 16 allocs/op
222- BenchmarkStructExceptSuccess-4 2000000 903 ns/op 368 B /op 9 allocs/op
223- BenchmarkStructExceptFailure-4 1000000 1381 ns/op 400 B /op 11 allocs/op
224- BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1215 ns/op 128 B /op 6 allocs/op
225- BenchmarkStructSimpleCrossFieldFailure-4 1000000 1781 ns/op 560 B /op 11 allocs/op
226- BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1801 ns/op 160 B /op 8 allocs/op
227- BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2357 ns/op 592 B /op 13 allocs/op
228- BenchmarkStructSimpleSuccess-4 1000000 1161 ns/op 48 B /op 3 allocs/op
229- BenchmarkStructSimpleFailure-4 1000000 1818 ns/op 624 B /op 11 allocs/op
230- BenchmarkStructSimpleSuccessParallel-4 5000000 375 ns/op 48 B /op 3 allocs/op
231- BenchmarkStructSimpleFailureParallel-4 2000000 757 ns/op 624 B /op 11 allocs/op
232- BenchmarkStructComplexSuccess-4 200000 8053 ns/op 432 B /op 27 allocs/op
233- BenchmarkStructComplexFailure-4 100000 12634 ns/op 3335 B /op 69 allocs/op
234- BenchmarkStructComplexSuccessParallel-4 1000000 2718 ns/op 432 B /op 27 allocs/op
235- BenchmarkStructComplexFailureParallel-4 300000 5086 ns/op 3336 B /op 69 allocs/op
313+ BenchmarkFieldSuccess-4 5000000 305 ns/op 16 B /op 1 allocs/op
314+ BenchmarkFieldFailure-4 5000000 301 ns/op 16 B /op 1 allocs/op
315+ BenchmarkFieldDiveSuccess-4 500000 3544 ns/op 528 B /op 28 allocs/op
316+ BenchmarkFieldDiveFailure-4 300000 4120 ns/op 928 B /op 32 allocs/op
317+ BenchmarkFieldCustomTypeSuccess-4 3000000 465 ns/op 32 B /op 2 allocs/op
318+ BenchmarkFieldCustomTypeFailure-4 2000000 769 ns/op 400 B /op 4 allocs/op
319+ BenchmarkFieldOrTagSuccess-4 1000000 1372 ns/op 32 B /op 2 allocs/op
320+ BenchmarkFieldOrTagFailure-4 1000000 1218 ns/op 432 B /op 6 allocs/op
321+ BenchmarkStructLevelValidationSuccess-4 2000000 840 ns/op 160 B /op 6 allocs/op
322+ BenchmarkStructLevelValidationFailure-4 1000000 1443 ns/op 592 B /op 11 allocs/op
323+ BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1262 ns/op 80 B /op 5 allocs/op
324+ BenchmarkStructSimpleCustomTypeFailure-4 1000000 1812 ns/op 624 B /op 11 allocs/op
325+ BenchmarkStructPartialSuccess-4 1000000 1419 ns/op 400 B /op 11 allocs/op
326+ BenchmarkStructPartialFailure-4 1000000 1967 ns/op 816 B /op 16 allocs/op
327+ BenchmarkStructExceptSuccess-4 2000000 954 ns/op 368 B /op 9 allocs/op
328+ BenchmarkStructExceptFailure-4 1000000 1422 ns/op 400 B /op 11 allocs/op
329+ BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1286 ns/op 128 B /op 6 allocs/op
330+ BenchmarkStructSimpleCrossFieldFailure-4 1000000 1885 ns/op 560 B /op 11 allocs/op
331+ BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1948 ns/op 176 B /op 9 allocs/op
332+ BenchmarkStructSimpleCrossStructCrossFieldFailure-4 500000 2491 ns/op 608 B /op 14 allocs/op
333+ BenchmarkStructSimpleSuccess-4 1000000 1239 ns/op 48 B /op 3 allocs/op
334+ BenchmarkStructSimpleFailure-4 1000000 1891 ns/op 624 B /op 11 allocs/op
335+ BenchmarkStructSimpleSuccessParallel-4 5000000 386 ns/op 48 B /op 3 allocs/op
336+ BenchmarkStructSimpleFailureParallel-4 2000000 842 ns/op 624 B /op 11 allocs/op
337+ BenchmarkStructComplexSuccess-4 200000 8604 ns/op 512 B /op 30 allocs/op
338+ BenchmarkStructComplexFailure-4 100000 13332 ns/op 3416 B /op 72 allocs/op
339+ BenchmarkStructComplexSuccessParallel-4 1000000 2929 ns/op 512 B /op 30 allocs/op
340+ BenchmarkStructComplexFailureParallel-4 300000 5220 ns/op 3416 B /op 72 allocs/op
236341```
237342
238343How to Contribute
0 commit comments