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