Skip to content

Releases: go-playground/validator

Release 8.14

28 Nov 01:03

Choose a tag to compare

Missed some test coverage for new changes, now have 100% test coverage again!

Release 8.13

27 Nov 21:06

Choose a tag to compare

More Optimizations!

  • This essentially reduces the number of cache tag lookups for a structs fields to one.

What will I notice most!

Faster Struct, especially complex struct, validation!

New Benchmarks

$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4                             10000000           162 ns/op           0 B/op          0 allocs/op
BenchmarkFieldFailure-4                              2000000           678 ns/op         400 B/op          4 allocs/op
BenchmarkFieldDiveSuccess-4                           500000          3079 ns/op         480 B/op         27 allocs/op
BenchmarkFieldDiveFailure-4                           300000          3584 ns/op         880 B/op         31 allocs/op
BenchmarkFieldCustomTypeSuccess-4                    5000000           345 ns/op          32 B/op          2 allocs/op
BenchmarkFieldCustomTypeFailure-4                    2000000           650 ns/op         400 B/op          4 allocs/op
BenchmarkFieldOrTagSuccess-4                         1000000          1188 ns/op          16 B/op          1 allocs/op
BenchmarkFieldOrTagFailure-4                         1000000          1088 ns/op         432 B/op          6 allocs/op
BenchmarkStructLevelValidationSuccess-4              2000000           689 ns/op         160 B/op          6 allocs/op
BenchmarkStructLevelValidationFailure-4              1000000          1290 ns/op         592 B/op         11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4             2000000           911 ns/op          80 B/op          5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4             1000000          1446 ns/op         624 B/op         11 allocs/op
BenchmarkStructPartialSuccess-4                      1000000          1221 ns/op         384 B/op         10 allocs/op
BenchmarkStructPartialFailure-4                      1000000          1764 ns/op         800 B/op         15 allocs/op
BenchmarkStructExceptSuccess-4                       2000000           941 ns/op         336 B/op          7 allocs/op
BenchmarkStructExceptFailure-4                       1000000          1237 ns/op         384 B/op         10 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4             2000000           970 ns/op         128 B/op          6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4             1000000          1560 ns/op         560 B/op         11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4  1000000          1542 ns/op         176 B/op          9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4  1000000          2147 ns/op         608 B/op         14 allocs/op
BenchmarkStructSimpleSuccess-4                       2000000           847 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailure-4                       1000000          1497 ns/op         624 B/op         11 allocs/op
BenchmarkStructSimpleSuccessParallel-4               5000000           257 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailureParallel-4               2000000           586 ns/op         624 B/op         11 allocs/op
BenchmarkStructComplexSuccess-4                       300000          5104 ns/op         496 B/op         29 allocs/op
BenchmarkStructComplexFailure-4                       200000          9840 ns/op        3400 B/op         71 allocs/op
BenchmarkStructComplexSuccessParallel-4              1000000          1540 ns/op         496 B/op         29 allocs/op
BenchmarkStructComplexFailureParallel-4               500000          3478 ns/op        3400 B/op         71 allocs/op

Old Benchmarks

$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4                             10000000           163 ns/op           0 B/op          0 allocs/op
BenchmarkFieldFailure-4                              2000000           673 ns/op         400 B/op          4 allocs/op
BenchmarkFieldDiveSuccess-4                           500000          3019 ns/op         480 B/op         27 allocs/op
BenchmarkFieldDiveFailure-4                           500000          3553 ns/op         880 B/op         31 allocs/op
BenchmarkFieldCustomTypeSuccess-4                    5000000           347 ns/op          32 B/op          2 allocs/op
BenchmarkFieldCustomTypeFailure-4                    2000000           645 ns/op         400 B/op          4 allocs/op
BenchmarkFieldOrTagSuccess-4                         1000000          1177 ns/op          16 B/op          1 allocs/op
BenchmarkFieldOrTagFailure-4                         1000000          1093 ns/op         432 B/op          6 allocs/op
BenchmarkStructLevelValidationSuccess-4              2000000           702 ns/op         160 B/op          6 allocs/op
BenchmarkStructLevelValidationFailure-4              1000000          1279 ns/op         592 B/op         11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4             1000000          1010 ns/op          80 B/op          5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4             1000000          1544 ns/op         624 B/op         11 allocs/op
BenchmarkStructPartialSuccess-4                      1000000          1249 ns/op         400 B/op         11 allocs/op
BenchmarkStructPartialFailure-4                      1000000          1797 ns/op         816 B/op         16 allocs/op
BenchmarkStructExceptSuccess-4                       2000000           927 ns/op         368 B/op          9 allocs/op
BenchmarkStructExceptFailure-4                       1000000          1259 ns/op         400 B/op         11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4             1000000          1076 ns/op         128 B/op          6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4             1000000          1623 ns/op         560 B/op         11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4  1000000          1582 ns/op         176 B/op          9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4  1000000          2139 ns/op         608 B/op         14 allocs/op
BenchmarkStructSimpleSuccess-4                       1000000          1040 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailure-4                       1000000          1683 ns/op         624 B/op         11 allocs/op
BenchmarkStructSimpleSuccessParallel-4               5000000           356 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailureParallel-4               2000000           831 ns/op         624 B/op         11 allocs/op
BenchmarkStructComplexSuccess-4                       200000          6738 ns/op         512 B/op         30 allocs/op
BenchmarkStructComplexFailure-4                       200000         11387 ns/op        3415 B/op         72 allocs/op
BenchmarkStructComplexSuccessParallel-4               500000          2330 ns/op         512 B/op         30 allocs/op
BenchmarkStructComplexFailureParallel-4               300000          4857 ns/op        3416 B/op         72 allocs/op

P.S. Don't know how much more performant I can make this, but if you have anymore ideas, let me know either in an issue or even just email me.

Release 8.12

26 Nov 13:54

Choose a tag to compare

What Micro Optimization?

Removed defer from the simple Tag Cache lock & unlock and reduced execution time for even single field validation by almost 100ns from 254ns/op to 163ns/op... Not bad for such a simple modification and this effect ripples throughout struct validation.

New Benchmarks

$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4                             10000000 163 ns/op         0 B/op          0 allocs/op
BenchmarkFieldFailure-4                              2000000 673 ns/op       400 B/op          4 allocs/op
BenchmarkFieldDiveSuccess-4                           500000 3019 ns/op      480 B/op         27 allocs/op
BenchmarkFieldDiveFailure-4                           500000 3553 ns/op      880 B/op         31 allocs/op
BenchmarkFieldCustomTypeSuccess-4                    5000000 347 ns/op        32 B/op          2 allocs/op
BenchmarkFieldCustomTypeFailure-4                    2000000 645 ns/op       400 B/op          4 allocs/op
BenchmarkFieldOrTagSuccess-4                         1000000 1177 ns/op       16 B/op          1 allocs/op
BenchmarkFieldOrTagFailure-4                         1000000 1093 ns/op      432 B/op          6 allocs/op
BenchmarkStructLevelValidationSuccess-4              2000000 702 ns/op       160 B/op          6 allocs/op
BenchmarkStructLevelValidationFailure-4              1000000 1279 ns/op      592 B/op         11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4             1000000 1010 ns/op       80 B/op          5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4             1000000 1544 ns/op      624 B/op         11 allocs/op
BenchmarkStructPartialSuccess-4                      1000000 1249 ns/op      400 B/op         11 allocs/op
BenchmarkStructPartialFailure-4                      1000000 1797 ns/op      816 B/op         16 allocs/op
BenchmarkStructExceptSuccess-4                       2000000 927 ns/op       368 B/op          9 allocs/op
BenchmarkStructExceptFailure-4                       1000000 1259 ns/op      400 B/op         11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4             1000000 1076 ns/op      128 B/op          6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4             1000000 1623 ns/op      560 B/op         11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4  1000000 1582 ns/op      176 B/op          9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4  1000000 2139 ns/op      608 B/op         14 allocs/op
BenchmarkStructSimpleSuccess-4                       1000000 1040 ns/op       48 B/op          3 allocs/op
BenchmarkStructSimpleFailure-4                       1000000 1683 ns/op      624 B/op         11 allocs/op
BenchmarkStructSimpleSuccessParallel-4               5000000 356 ns/op        48 B/op          3 allocs/op
BenchmarkStructSimpleFailureParallel-4               2000000 831 ns/op       624 B/op         11 allocs/op
BenchmarkStructComplexSuccess-4                       200000 6738 ns/op      512 B/op         30 allocs/op
BenchmarkStructComplexFailure-4                       200000 11387 ns/op    3415 B/op         72 allocs/op
BenchmarkStructComplexSuccessParallel-4               500000 2330 ns/op      512 B/op         30 allocs/op
BenchmarkStructComplexFailureParallel-4               300000 4857 ns/op     3416 B/op         72 allocs/op

Old Benchmarks

$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4                              5000000 254 ns/op         0 B/op          0 allocs/op
BenchmarkFieldFailure-4                              2000000 779 ns/op       400 B/op          4 allocs/op
BenchmarkFieldDiveSuccess-4                           500000 3451 ns/op      480 B/op         27 allocs/op
BenchmarkFieldDiveFailure-4                           300000 3954 ns/op      880 B/op         31 allocs/op
BenchmarkFieldCustomTypeSuccess-4                    3000000 451 ns/op        32 B/op          2 allocs/op
BenchmarkFieldCustomTypeFailure-4                    2000000 751 ns/op       400 B/op          4 allocs/op
BenchmarkFieldOrTagSuccess-4                         1000000 1312 ns/op       16 B/op          1 allocs/op
BenchmarkFieldOrTagFailure-4                         1000000 1206 ns/op      432 B/op          6 allocs/op
BenchmarkStructLevelValidationSuccess-4              2000000 829 ns/op       160 B/op          6 allocs/op
BenchmarkStructLevelValidationFailure-4              1000000 1403 ns/op      592 B/op         11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4             1000000 1238 ns/op       80 B/op          5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4             1000000 1771 ns/op      624 B/op         11 allocs/op
BenchmarkStructPartialSuccess-4                      1000000 1399 ns/op      400 B/op         11 allocs/op
BenchmarkStructPartialFailure-4                      1000000 1926 ns/op      816 B/op         16 allocs/op
BenchmarkStructExceptSuccess-4                       2000000 930 ns/op       368 B/op          9 allocs/op
BenchmarkStructExceptFailure-4                       1000000 1381 ns/op      400 B/op         11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4             1000000 1253 ns/op      128 B/op          6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4             1000000 1849 ns/op      560 B/op         11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4  1000000 1902 ns/op      176 B/op          9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4   500000 2453 ns/op      608 B/op         14 allocs/op
BenchmarkStructSimpleSuccess-4                       1000000 1182 ns/op       48 B/op          3 allocs/op
BenchmarkStructSimpleFailure-4                       1000000 1842 ns/op      624 B/op         11 allocs/op
BenchmarkStructSimpleSuccessParallel-4               5000000 342 ns/op        48 B/op          3 allocs/op
BenchmarkStructSimpleFailureParallel-4               2000000 665 ns/op       624 B/op         11 allocs/op
BenchmarkStructComplexSuccess-4                       200000 7924 ns/op      512 B/op         30 allocs/op
BenchmarkStructComplexFailure-4                       100000 12605 ns/op    3415 B/op         72 allocs/op
BenchmarkStructComplexSuccessParallel-4              1000000 2421 ns/op      512 B/op         30 allocs/op
BenchmarkStructComplexFailureParallel-4               300000 4234 ns/op     3416 B/op         72 allocs/op

P.S.

I've become a little obsessed with efficiency and allocations as of late so don't be surprised to see more improvements keep coming! and who knows maybe there's even an attempt at a zero allocation validator in the works... 😉 😉

Release 8.11

23 Nov 02:40

Choose a tag to compare

Some Minor updates, no logic changes

  • Fix error with BenchmarkFieldFailure, it was recording a successful run because of a typo.
  • Update some benchmarks, they were showing allocations for the creation of the data not just processing it.

Release 8.10

20 Nov 14:04

Choose a tag to compare

What Now!

  • Add minor optimization for structonly & nostructlevel checks; they are now cached within the tag instead of using strings.Contains

Release 8.9

19 Nov 21:51

Choose a tag to compare

Adding Tag

nostructlevel - Same as structonly tag except that any struct level validations will not run.

When would I ever use that?

Lets say you have a User struct that has struct level validations and you also use the same User struct as a field within another, say as a CreatedBy field that you know will not have any validation errors as it grabs that info directly from the database, however still need to validate the CreatedBy field was set.

Release 8.8.1

19 Nov 17:20

Choose a tag to compare

Fix issue with structOnly + struct level validation

  • when "structonly" tag was set the struct
    level validation wasn't running, this has been corrected.

Release 8.8

16 Nov 18:35

Choose a tag to compare

What Now?

  • Added new helper method for StructLevel validations "ReportValidationErrors"

Why do I care?

Because you can do the following in a struct level validation

func StructValidationTestStructReturnValidationErrors(v *Validate, structLevel *StructLevel) {

    // TestStructReturnValidationErrors is a triple nested struct
    // see tests for full code
    s := structLevel.CurrentStruct.Interface().(TestStructReturnValidationErrors)

    errs := v.Struct(s.Inner1.Inner2)
    if errs == nil {
        return
    }

    structLevel.ReportValidationErrors("Inner1.", errs.(ValidationErrors))
}

Release 8.7

13 Nov 13:51

Choose a tag to compare

Just when you though it couldn't get any better! Whats New?

  • Added Struct Level Validations!

Details

  • You can now register a validation against a struct and can validate situations where field level validation doesn't really make that much sense. I refer to issue #195 it was handled with field validation, but would be better suited to struct level validation as the check is not duplicated for each field.
  • It is fully valid to mix and match tag and struct level validations.
  • Struct Level validations run after the structs tag validations.

Example Usage

package main

import (
    "fmt"
    "reflect"

    "gopkg.in/go-playground/validator.v8"
)

// User contains user information
type User struct {
    FirstName      string     `json:"fname"`
    LastName       string     `json:"lname"`
    Age            uint8      `validate:"gte=0,lte=130"`
    Email          string     `validate:"required,email"`
    FavouriteColor string     `validate:"hexcolor|rgb|rgba"`
    Addresses      []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
}

// Address houses a users address information
type Address struct {
    Street string `validate:"required"`
    City   string `validate:"required"`
    Planet string `validate:"required"`
    Phone  string `validate:"required"`
}

var validate *validator.Validate

func main() {

    config := &validator.Config{TagName: "validate"}

    validate = validator.New(config)
    validate.RegisterStructValidation(UserStructLevelValidation, User{})

    validateStruct()
}

// UserStructLevelValidation contains custom struct level validations that don't always
// make sense at the field validation level. For Example this function validates that either
// FirstName or LastName exist; could have done that with a custom field validation but then
// would have had to add it to both fields duplicating the logic + overhead, this way it's
// only validated once.
//
// NOTE: you may ask why wouldn't I just do this outside of validator, because doing this way
// hooks right into validator and you can combine with validation tags and still have a
// common error output format.
func UserStructLevelValidation(v *validator.Validate, structLevel *validator.StructLevel) {

    user := structLevel.CurrentStruct.Interface().(User)

    if len(user.FirstName) == 0 && len(user.LastName) == 0 {
        structLevel.ReportError(reflect.ValueOf(user.FirstName), "FirstName", "fname", "fnameorlname")
        structLevel.ReportError(reflect.ValueOf(user.LastName), "LastName", "lname", "fnameorlname")
    }

    // plus can to more, even with different tag than "fnameorlname"
}

func validateStruct() {

    address := &Address{
        Street: "Eavesdown Docks",
        Planet: "Persphone",
        Phone:  "none",
        City:   "Unknown",
    }

    user := &User{
        FirstName:      "",
        LastName:       "",
        Age:            45,
        Email:          "[email protected]",
        FavouriteColor: "#000",
        Addresses:      []*Address{address},
    }

    // returns nil or ValidationErrors ( map[string]*FieldError )
    errs := validate.Struct(user)

    if errs != nil {

        fmt.Println(errs) // output: Key: 'User.LastName' Error:Field validation for 'LastName' failed on the 'fnameorlname' tag
        //                           Key: 'User.FirstName' Error:Field validation for 'FirstName' failed on the 'fnameorlname' tag
        err := errs.(validator.ValidationErrors)["User.FirstName"]
        fmt.Println(err.Field) // output: FirstName
        fmt.Println(err.Tag)   // output: fnameorlname
        fmt.Println(err.Kind)  // output: string
        fmt.Println(err.Type)  // output: string
        fmt.Println(err.Param) // output:
        fmt.Println(err.Value) // output:

        // from here you can create your own error messages in whatever language you wish
        return
    }

    // save user to database
}

New Benchmarks, largely unchanged

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
$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4                              5000000           305 ns/op          16 B/op          1 allocs/op
BenchmarkFieldFailure-4                              5000000           301 ns/op          16 B/op          1 allocs/op
BenchmarkFieldDiveSuccess-4                           500000          3544 ns/op         528 B/op         28 allocs/op
BenchmarkFieldDiveFailure-4                           300000          4120 ns/op         928 B/op         32 allocs/op
BenchmarkFieldCustomTypeSuccess-4                    3000000           465 ns/op          32 B/op          2 allocs/op
BenchmarkFieldCustomTypeFailure-4                    2000000           769 ns/op         400 B/op          4 allocs/op
BenchmarkFieldOrTagSuccess-4                         1000000          1372 ns/op          32 B/op          2 allocs/op
BenchmarkFieldOrTagFailure-4                         1000000          1218 ns/op         432 B/op          6 allocs/op
BenchmarkStructLevelValidationSuccess-4              2000000           840 ns/op         160 B/op          6 allocs/op
BenchmarkStructLevelValidationFailure-4              1000000          1443 ns/op         592 B/op         11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4             1000000          1262 ns/op          80 B/op          5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4             1000000          1812 ns/op         624 B/op         11 allocs/op
BenchmarkStructPartialSuccess-4                      1000000          1419 ns/op         400 B/op         11 allocs/op
BenchmarkStructPartialFailure-4                      1000000          1967 ns/op         816 B/op         16 allocs/op
BenchmarkStructExceptSuccess-4                       2000000           954 ns/op         368 B/op          9 allocs/op
BenchmarkStructExceptFailure-4                       1000000          1422 ns/op         400 B/op         11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4             1000000          1286 ns/op         128 B/op          6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4             1000000          1885 ns/op         560 B/op         11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4  1000000          1948 ns/op         176 B/op          9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4   500000          2491 ns/op         608 B/op         14 allocs/op
BenchmarkStructSimpleSuccess-4                       1000000          1239 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailure-4                       1000000          1891 ns/op         624 B/op         11 allocs/op
BenchmarkStructSimpleSuccessParallel-4               5000000           386 ns/op          48 B/op          3 allocs/op
BenchmarkStructSimpleFailureParallel-4               2000000           842 ns/op         624 B/op         11 allocs/op
BenchmarkStructComplexSuccess-4                       200000          8604 ns/op         512 B/op         30 allocs/op
BenchmarkStructComplexFailure-4                       100000         13332 ns/op        3416 B/op         72 allocs/op
BenchmarkStructComplexSuccessParallel-4              1000000          2929 ns/op         512 B/op         30 allocs/op
BenchmarkStructComplexFailureParallel-4               300000          5220 ns/op        3416 B/op         72 allocs/op

Release 8.6

09 Nov 17:34

Choose a tag to compare

What changed?

  • Nothing functional just updated default error messages to contain single quotes ' instead of "

Why?

  • To reduce noise when transmitting those errors via JSON and so forth.

Thanks @solher for the suggestion.