validator

package module
v0.1.11 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 15, 2025 License: MIT Imports: 12 Imported by: 0

README

validator v0.1.11

This is a JSON validator package. It allows tags to be added to structure definitions, and those structures are then passed to a Define() operation which creates a map of the valid structure definitions. Subsequently, JSON strings can be validated against the structure definitions to report if they are conformant or not.

This is intended to help catch misspelled fields, missing required fields, and invalid field values.

Using the validate tag

Use the Go tag validate to identify what validations will be done on the JSON representation of the structure. The tag is followed by a quoted string containing comma-separate validation operations. These operations are:

Operation Operand Description
required If specified, this field must appear in the JSON
min any The minimum int or float value allowed for this field
max any The maximum int or float value allowed for this field
minlen integer The minimum length of a string value, or smallest allowed array size
maxlen integer The maximum length of a string value. or largest allowed array size
enum strings A list of strings separated by vertical bars enumerating the allowed field values
list The string value can be a list, each of which must match the enum list
matchcase The enumerated values must match case to match the field value
key (items) Specify limits on a map key value (which is always a string)
value (items) Specify rules on a value for an array or map

You can separate enumerated values using commas rather than vertical bars by enclosing the list of enumerated values in parenthesis. That is, enum=red|green|blue is the same as specifying enum=(red,green,blue). Note that leading and trailing spaces in enumerated values are ignored.

some operations cannot be performed on all data types. For example, min and max can be used with a time.Time value to compare the time provided in the JSON to specific time values. However, these are not applicable to fields containing maps. For an array, the validations apply to the array itself (such as minimum or maximum length) and the value= clause defines the validation rules for the individual values in the array. Note that you can use parentheses to delimit lists in the value= clause, such as

type Foo struct {
    Colors []string `validate:"minlen=1,value=(enum=red,green,blue)"`
}

In this example, the JSON representation of a Foo structure must include an array with at least one element (minlen) but each value in the array must also conform to an enumerated list allowing only the values red, green, and blue.

Here is an example of a set of structures that are to be used to process JSON data. The associated json and validate tags indicate how the field names are handled by JSON and the additional validation to be done.



type Address struct {
    Street string `json:"street" validate:"required,minlength=1,maxlen=100"`
    City   string `json:"city"   validate:"required,minlength=1,maxlen=100"`
}

type Person struct {
    Name    string  `json:"name"    validate:"required,minlen=1,maxlen=100"`
    Age     int     `json:"age"     validate:"required,min=18,max=65"`
    Address Address `json:"address" validate:"required"`
}

type Employees struct {
    Department string   `json:"department" validate:"required"`
    Division   string   `json:"division"   validate:"required,enum=HR|Finance|Marketing|Engineering"`
    Staff      []Person `json:"staff"      validate:"minlen=1"`
}

Creating a new Validator object

A validator object is created by passing an instance of the structure to the validator, which builds a data structure that defines how the validation is to be performed. For example,

    employeeValidator, err := validator.New(&Employees{})

The error return can indicate invalid tags or unsupportable data types for validation in the data structure.

The New() function scans the object (a structure, in this case) and creates validation rules for any items specified with structure tags. It can support numeric, string, and boolean fields. It can handle the cases of uuid.UUID, time.Time, and time.Duration when expressed as a string. And it can handle arrays, pointers, and map types.

For integer value types, a default min and max value is automatically created based on the size of the integer type. So a structure of Go type uint8 will automatically have a minimum value of 0 and a maximum value of 255. Similarly, a structure field of type float32 will have a size range based on a 32-bit floating point value. No such checks are done for float64 or int64 data types.

Validating a JSON string

To validate a JSON string to see if it contains a valid representation of the object, use the Validate() method for the validator object previously created.

    // Read the JSON file and validate it's contents.
    b, err := io.ReadFile("my.json")
    if err == nil {
        err = employee.Validate(string(b))
    }

    // JSON text does not violate any specified data requirements...

This (oversimplified) example shows validating the JSON data (which must be represented as a string) to verify that the JSON representation contains the required fields, no misspelled field names, and no invalid values. If the error return is nil, no errors where found.

Currently, the validator stops on the first error it finds and reports it.

Programmatic Validators

Validators can be created programmatically rather than by reading tags from Go structure definitions. This is useful if validating a single object that is not part of a structure, for example. Create the validator as usual, by passing in an example of the data type to be validated.

    i := validator.NewType(validator.TypeInteger)

In this example, a validator is created for an integer value (due to the use of the value 0 in the call to New() to create the validator. Note that not all Go types are supported by the validator. For example, you cannot create a validator for non-standard integer or float values, such as Int15 or Float32.

Once you have created the validator, you can set attributes on it. For example, this code sets a minimum and maximum value for the validator.

    i := validator.NewType(validator.TypeInteger).SetMaxValue(1).SetMaxValue(10)

    err := i.Validate(15)
    if err == nil {
        return err
    }
    ...

In this example, the validator is created for an integer value, and has minimum and maximum values set. A test to see if the value 15 is valid is performed. Because the validator requires a value from 1..10, the validator will return an error.

The following modifier functions are used to set the characteristics of a validator.

Function Description
SetMinValue(v) Set the minimum allowed numeric value
SetMAxValue(v) Set the maximum allowed numeric value
SetMinLen(i) Set the minimum string or array length
SetMaxLen(i) Set the maximum string or array length
SetEnum(v...) Set the allowed values for integer or string values
SetField(i, v) Set structure field i to validator v
AddField(v) Add a new structure field to the validator
SetMatchCase(b) Indicate if enumerated strings must match case
SetForeignKey(b) Indicate if undeclared field names are permitted

Import and Export

A validator can be converted to a string representation as a JSON object using the String() function.

A validator can be created by reading a JSON string (typically that was created by the String() function) using the NewFromJSON() function.

These functions make it easier for code to be written to allow externally- created validator definitions in JSON to be integrated into the program.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var Dictionary map[string]*Item = make(map[string]*Item)

Dictionary is the map that retains named validators. When a validator is defined of type struct, it is stored in the Dictionary. This allows recursive references to a validator to be resolved by a dictionary lookup. Because validator definitions are thread-safe, a mutex lock serializes access to the dictionary.

View Source
var ErrArrayLengthOutOfRange = NewError("array length out of range")

Predefined validation errors.

View Source
var ErrEmptyTag = NewError("empty tag")
View Source
var ErrEmptyTagValue = NewError("empty tag value")
View Source
var ErrInvalidBaseTag = NewError("invalid base tag (only allowed on arrays and maps)")
View Source
var ErrInvalidData = NewError("invalid data")
View Source
var ErrInvalidDuration = NewError("invalid duration value")
View Source
var ErrInvalidEnumType = NewError("invalid field type for enum, must be string or int")
View Source
var ErrInvalidEnumeratedValue = NewError("invalid enumerated value")
View Source
var ErrInvalidFieldName = NewError("invalid field name")
View Source
var ErrInvalidInteger = NewError("invalid integer value")
View Source
var ErrInvalidKeyword = NewError("invalid keyword")
View Source
var ErrInvalidListTag = NewError("invalid list tag for item type")
View Source
var ErrInvalidName = NewError("invalid name")
View Source
var ErrInvalidTagName = NewError("invalid tag name")
View Source
var ErrInvalidValidator = NewError("invalid JSON instance of validator")
View Source
var ErrMaxDepthExceeded = NewError("maximum validation depth exceeded")
View Source
var ErrMissingEnumValue = NewError("missing enum values")
View Source
var ErrNameAlreadyExists = NewError("name already exists")
View Source
var ErrNilValidator = NewError("nil validator")
View Source
var ErrNotAMap = NewError("keyword only valid with map type")
View Source
var ErrRequired = NewError("required field missing")
View Source
var ErrUndefinedStructure = NewError("undefined structure")
View Source
var ErrUnimplemented = NewError("unimplemented type")
View Source
var ErrUnsupportedType = NewError("unsupported type")
View Source
var ErrValueLengthOutOfRange = NewError("value length out of range")
View Source
var ErrValueOutOfRange = NewError("value out of range")
View Source
var TypeNames = map[Type]string{
	TypePointer:  "pointer",
	TypeInvalid:  "invalid",
	TypeString:   "string",
	TypeInt:      "int",
	TypeFloat:    "float",
	TypeBool:     "bool",
	TypeStruct:   "struct",
	TypeArray:    "array",
	TypeAny:      "any",
	TypeUUID:     "uuid.UUID",
	TypeTime:     "time.Time",
	TypeDuration: "time.Duration",
	TypeMap:      "map[string]any",
	TypeList:     "stringList",
}

Map used to convert Type values to a string name.

Functions

func Define

func Define(name string, obj any) error

Define a new validator by name. The validator is created using normal reflection, and the resulting validator is stored in the Dictionary. This method serializes access to the dictionary. If the name already exists in the Dictionary, an error is returned.

func DumpJSON added in v0.1.5

func DumpJSON() []byte

DumpJSON is a diagnostic function that allows the user of the validator package to dump the contents of the Dictionary in JSON format.

func ParseDuration added in v0.1.7

func ParseDuration(durationString string) (time.Duration, error)

ParseDuration parses a duration string and returns the corresponding time.Duration. It handles extensions that allow specifying days in a duration, which are converted to hours before using the builtin time.ParseDuration function.

func SetTagName added in v0.1.7

func SetTagName(name string) error

SetTagNAme sets the name of the tag used to define validation rules for fields. The default is "validate". If you need to change the tag string name, this must be called before using a function that reads a structure to create a validator.

func Split added in v0.1.8

func Split(input string, separator string) []string

Split a string into separate components, using a defined separator character. If the separator is not provided, the function defaults to a comma ",". The split ignores separators enclosed within single quotes or parentheses.

func UnMarshal

func UnMarshal(data []byte, value any) error

UnMarshal is a helper function that combines JSON validation and conversion of the representation into the destination structure.

It will return errors if the validation tags on the structure definition are not correctly defines, if the JSON is not correctly formatted, or if the JSON contains fields or values that violate the validation rules.

This is not recommended for use when the same structure is going to be validate many times, as it does not cache the validator structure.

func ValidateByName

func ValidateByName(name string, text string) error

ValidateByName validates a JSON string against a named validator. If the named validator is not found, it returns an error. If the JSON string is valid according to the named validator, it returns nil.

Types

type Item

type Item struct {
	// The name of this item. This is usually the field name in a
	// structure. For other types, this is an empty string.
	Name string `json:"name,omitempty"`

	// IF this validator is a reference to another validator (or even a
	// references to the same validator), this will be the name of an
	// alias object in the dictionary. When set, the only other value
	// set in the validator is the ItemType value.
	Alias string `json:"alias,omitempty"`

	// The type of this item. This controls how the value in the JSON
	// payload being validated is interpreted, and controls what validations
	// are permitted for this item.
	ItemType Type `json:"type,omitempty"`

	// This is a list of the allowed values for this item. This is only
	// used for string, integer, and the key values for map types. IF there
	// are no enumerated values (enums), this will be an empty slice.
	Enums []string `json:"enums,omitempty"`

	// This is a list of the fields in the current structure. This is only
	// used for struct types. This will be an empty slice if there are no fields.
	Fields []*Item `json:"fields,omitempty"`

	// IF this is a pointer to another validator or an array of validators, this
	// is a pointer to the validator for the underlying type. If the current
	// validator is not a pointer or array, this will be nil.
	BaseType *Item `json:"base_type,omitempty"`

	// If there is a rule specifying a minimum length for a string, array, or
	// map key set, this will be the minimum length. The HasMinLength boolean
	// will be true if this value is used.
	MinLength int `json:"min_length,omitempty"`

	// If there is a rule specifying a maximum length for a string, array, or
	// map key set, this will be the maximum length. The HasMaxLength boolean
	// will be true if this value is used.
	MaxLength int `json:"max_length,omitempty"`

	// If there is a rule specifying a minimum value for a numeric field, this
	// will be the minimum value. The HasMinValue boolean will be true if
	// this value is used.
	MinValue any `json:"min_value,omitempty"`

	// If there is a rule specifying a maximum value for a numeric field, this
	// will be the maximum value. The HasMaxValue boolean will be true if
	// this value is used.
	MaxValue any `json:"max_value,omitempty"`

	// If there is a rule specifying that this field must always be present
	// in a json payload, this will be true.
	Required bool `json:"required,omitempty"`

	// If there is a rule specifying that the json can contain field names
	// not explicitly defined in the validator, this will be true. The default
	// is false, which means the json cannot contain field names not explicitly
	// defined in the validator.
	AllowForeignKey bool `json:"allow_foreign_key,omitempty"`

	// If there is a minimum length specified for this item, this is true.
	HasMinLength bool `json:"has_min_length,omitempty"`

	// If there is a maximum length specified for this item, this is true.
	HasMaxLength bool `json:"has_max_length,omitempty"`

	// If there is a minimum value specified for this item, this is true.
	HasMinValue bool `json:"has_min_value,omitempty"`

	// If there is a maximum value specified for this item, this is true.
	HasMaxValue bool `json:"has_max_value,omitempty"`

	// IF there is a rule specifying enumerated values, this is true when
	// the values are case-sensitive. By default, string values are not
	// case-sensitive.
	CaseSensitive bool `json:"case_sensitive,omitempty"`
}

Define an individual validator. This structure is used for any structure, field, map, array, or individual value. It contains flags representing all the validation rules defined for this item.

IF any of these json tag names are changed, the corresponding field in the checkFields() function will need to be updated.

func New

func New(v any) (*Item, error)

New accepts any value and returns a new validator for it. If the value is a structure, the validator will contain any rules specified in structure tags within the structure definition. Additional validation rules can be defined by calling Parse() with a tag string on the validator.

func NewJSON added in v0.1.3

func NewJSON(data []byte) (*Item, error)

NewJSON converts a JSON byte array into a validator item. IF the JSON did not contain a valid validator item, it will return an error.

func NewType added in v0.1.3

func NewType(kind Type) *Item

NewType creates a new validator with the given type. There is no other information stored in the validator, so the caller should use ParseTag() or the explicit rule creation methods to add rules to the validator.

func (*Item) AddField added in v0.1.3

func (i *Item) AddField(v Item) *Item

AddField adds a field validator to an existing structure validator. The field validator must have been previously completely defined (you cannot update a field after it is defined in the structure). The field is appended to the list of existing fields.

func (*Item) Copy added in v0.1.5

func (i *Item) Copy() *Item

Copy creates a deep copy of the Item structure. The copied structure is completely independent of the original structure.

func (*Item) ParseTag

func (item *Item) ParseTag(tag string) error

ParseTag updates an existing validator to reflect the rules specified in the tag string. This tag string is normally extracted from struct field tags automatically when a new validator is created, but can also be used to set validator rules on a non-struct validator type.

func (*Item) SetEnums added in v0.1.3

func (i *Item) SetEnums(values ...any) *Item

SetEnums sets the allowed values for this item. The allowed values can be any string, integer, or any other type that can be converted to a string. This test can be applied to a string value, an array or strings or integers, or a string list where the values are comma-separated.

func (*Item) SetField added in v0.1.3

func (i *Item) SetField(index int, v Item) *Item

SetField adds a field validator to an existing structure validator. The field validator must have been previously completely defined (you cannot update a field after it is defined in the structure). The index is the zero-based index of the field in the structure.

IF the validator is not for a structure or the index is negative, no change is made to the item.

func (*Item) SetForeignKeys added in v0.1.3

func (i *Item) SetForeignKeys(b bool) *Item

SetForeignKeys sets the allow foreign key flag. By default, foreign keys (field names not defined in the validator) are not allowed. If the validator should instead ignore key values not defined in the validator, set this flag to true.

func (*Item) SetMatchCase added in v0.1.3

func (i *Item) SetMatchCase(b bool) *Item

SetMatchCase sets the match case flag. By default, enumerated values are not case-sensitive when they are string values. If these are meant to be case-sensitive, set this flag to true.

func (*Item) SetMaxLength added in v0.1.3

func (i *Item) SetMaxLength(l int) *Item

SetMaxLength sets the maximum allowed length for this item. The maximum length can be any integer value. This applies to string length, array length, or number of map keys.

func (*Item) SetMaxValue added in v0.1.3

func (i *Item) SetMaxValue(v any) *Item

SetMaxValue sets the maximum allowed value for this item. The maximum value can be any numeric value.

func (*Item) SetMinLength added in v0.1.3

func (i *Item) SetMinLength(l int) *Item

SetMinLength sets the minimum allowed length for this item. The minimum length can be any integer value. This applies to string length, array length, or number of map keys.

func (*Item) SetMinValue added in v0.1.3

func (i *Item) SetMinValue(v any) *Item

SetMinValue sets the minimum allowed value for this item. The minimum value can be any numeric value.

func (*Item) SetName added in v0.1.3

func (i *Item) SetName(name string) *Item

SetName sets the field name for this item. The field name defaults to the field name in the struct tag. If the struct tag does not have a name field, it looks to see if there is a json tag and uses the name from that tag. Finally, it assumes the field name is the same as the variable name in the structure. This function can be used to override any of these defaults.

func (*Item) SetRequired added in v0.1.7

func (i *Item) SetRequired(required bool) *Item

SetRequired sets whether this item is required or not. By default, a json payload does not have to explicitly specify fields. If the field must be present in the JSON, then set this to true.

func (*Item) String added in v0.1.3

func (i *Item) String() string

String converts a validator item into a JSON-formatted string.

func (*Item) Validate

func (i *Item) Validate(text string) error

For a given validator, determine if the provided JSON string is valid according to the rules for the given validator. If the JSON is not correctly formed or if a validation rule is violated, an error is returned. IF the provided JSON includes recursive or nested values that exceed the maximum recursion depth, an error is returned. If the JSON string is valid, it returns nil.

type Type

type Type int
const (
	TypeInvalid Type = iota
	TypeString
	TypeInt
	TypeFloat
	TypeBool
	TypeStruct
	TypeArray
	TypePointer
	TypeMap // Any map type falls in this bucket. Only validations are for key values
	TypeUUID
	TypeTime
	TypeAny
	TypeList
	TypeDuration
)

Enumerate all possible types that an item/field can have. Always add to the end to avoid breaking any existing JSON representation of a validator.

func TypeFromString added in v0.1.9

func TypeFromString(name string) (Type, error)

For a given type name, return the corresponding Type value. If the name is not recognized, return TypeInvalid and an error.

func (*Type) String

func (t *Type) String() string

String method for Type to return the string name of the type. Mostly used for debugging purposes.

type ValidationError

type ValidationError struct {
	// contains filtered or unexported fields
}

ValidationError represents a validation error. This includes the original error, the context of the validation (e.g., the field name), the actual value, and the expected values. If any of context, value, or expected values are empty, they are not included in the formatted error message string.

func NewError

func NewError(msg string) *ValidationError

Create a new validation error with the given message.

func (*ValidationError) Context

func (e *ValidationError) Context(context string) *ValidationError

Context adds a context value to the validation error. This returns the validation error with the updated context as a new validation error.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Error returns the formatted error message string. Supporting this function makes validation errors match the error interface in Go. The error is formatted with the original error message, context, value, and expected values. If any of context, value, or expected values are empty, they are not included in the formatted error message string.

func (*ValidationError) Expected

func (e *ValidationError) Expected(expected ...any) *ValidationError

Expected adds expected values to the validation error. This is used when an enumeration rule is violated. The expected values can be a list of values, a slice of values, or a single value. This returns a copy of the validation error with the updated expected values as a new validation error.

func (*ValidationError) Value

func (e *ValidationError) Value(value any) *ValidationError

Value adds a value to the validation error. This returns the validation error with the updated value as a new validation error. If the value is nil, the value field is not included in the formatted error message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL