Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ It has the following **unique** features:
- Slice, Array and Map diving, which allows any or all levels of a multidimensional field to be validated.
- Ability to dive into both map keys and values for validation
- Handles type interface by determining it's underlying type prior to validation.
- Handles custom field types such as sql driver Valuer see [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29)
- Handles custom field types such as sql driver [Valuer](https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29) and the [Valuer interface](https://github.com/go-playground/validator/blob/master/_examples/valuer/main.go)
- Alias validation tags, which allows for mapping of several validations to a single tag for easier defining of validations on structs
- Extraction of custom defined Field Name e.g. can specify to extract the JSON name while validating and have it available in the resulting FieldError
- Customizable i18n aware error messages.
Expand Down
57 changes: 57 additions & 0 deletions _examples/valuer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package main

import (
"errors"
"fmt"

"github.com/go-playground/validator/v10"
)

// Nullable wraps a generic value.
type Nullable[T any] struct {
Data T
}

// ValidatorValue returns the inner value that should be validated.
func (n Nullable[T]) ValidatorValue() any {
return n.Data
}

type Config struct {
Name string `validate:"required"`
}

type Record struct {
Config Nullable[Config] `validate:"required"`
}

// use a single instance of Validate, it caches struct info
var validate *validator.Validate

func main() {
validate = validator.New()

// valid case: ValidatorValue unwraps Config and Name passes required.
valid := Record{
Config: Nullable[Config]{
Data: Config{Name: "validator"},
},
}
err := validate.Struct(valid)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
}

// invalid case: Name is empty after unwrapping, so required fails on Config.Name.
invalid := Record{
Config: Nullable[Config]{},
}
err = validate.Struct(invalid)
if err != nil {
fmt.Printf("Err(s):\n%+v\n", err)
var validationErrs validator.ValidationErrors
if errors.As(err, &validationErrs) && len(validationErrs) > 0 {
fmt.Printf("First error namespace: %s\n", validationErrs[0].Namespace())
}
}
}
34 changes: 34 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,40 @@ Custom Validation functions can be added. Example:
// NOTES: using the same tag name as an existing function
// will overwrite the existing one

# Valuer Interface

Custom types can implement the Valuer interface to return the value that should
be validated. This is useful when a type wraps another value and you want
validation to run against the unwrapped value.

type Nullable[T any] struct {
Data T
}

func (n Nullable[T]) ValidatorValue() any {
return n.Data
}

type Config struct {
Name string `validate:"required"`
}

type Record struct {
Config Nullable[Config] `validate:"required"`
}

r := Record{
Config: Nullable[Config]{
Data: Config{Name: "validator"},
},
}

err := validate.Struct(r)

The library also supports types like sql/driver.Valuer using
RegisterCustomTypeFunc. See _examples/valuer/main.go and
_examples/custom/main.go for both approaches.

# Cross-Field Validation

Cross-Field Validation can be done via the following tags:
Expand Down