Repository: go-playground/validator
Pull request #1166 (2023-09-15 16:39:43)

add ISSN validator + tests + documentation
## International Standard Serial Number (ISSN) validator

"_An ISSN is an 8-digit code used to identify newspapers, journals, magazines and periodicals of all kinds and on all media–print and electronic._" -- [issn.org](https://www.issn.org/understanding-the-issn/what-is-an-issn/)

This PR adds a new `issn` validator, along with updating all translation files and tests. This validator is very similar to that of the ISBN so I believe all translations will be okay. The _valid_ ISSNs used in the tests are examples taken from issn.org.

**Make sure that you've checked the boxes below before you submit PR:**
- [X] Tests exist or have been written that cover this particular change.

@go-playground/validator-maintainers

coveralls: 
[![Coverage Status](https://coveralls.io/builds/62690744/badge)](https://coveralls.io/builds/62690744)

coverage: 73.884% (+0.04%) from 73.849% when pulling **9af5485d2a98e70976ba16ed93c59f1bf9c0840f on mrcook:issn-validator** into **8d50f2fd8c2f02fbdf1d44d66171b955c2f4eb50 on go-playground:master**.


Patch:
diff --git a/README.md b/README.md
index 8e80d52..700c995 100644
--- a/README.md
+++ b/README.md
@@ -178,6 +178,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
 | isbn | International Standard Book Number |
 | isbn10 | International Standard Book Number 10 |
 | isbn13 | International Standard Book Number 13 |
+| issn | International Standard Serial Number |
 | iso3166_1_alpha2 | Two-letter country code (ISO 3166-1 alpha-2) |
 | iso3166_1_alpha3 | Three-letter country code (ISO 3166-1 alpha-3) |
 | iso3166_1_alpha_numeric | Numeric country code (ISO 3166-1 numeric) |
diff --git a/_examples/simple/main.go b/_examples/simple/main.go
index 35a56c1..6581412 100644
--- a/_examples/simple/main.go
+++ b/_examples/simple/main.go
@@ -12,7 +12,7 @@ type User struct {
 	LastName       string     `validate:"required"`
 	Age            uint8      `validate:"gte=0,lte=130"`
 	Email          string     `validate:"required,email"`
-	Gender         string     `validate:"oneof=male female prefer_not_to`
+	Gender         string     `validate:"oneof=male female prefer_not_to"`
 	FavouriteColor string     `validate:"iscolor"`                // alias for 'hexcolor|rgb|rgba|hsl|hsla'
 	Addresses      []*Address `validate:"required,dive,required"` // a person can have a home and cottage...
 }
diff --git a/baked_in.go b/baked_in.go
index cc92b78..95f56e0 100644
--- a/baked_in.go
+++ b/baked_in.go
@@ -51,6 +51,7 @@ var (
 		endKeysTag:        {},
 		structOnlyTag:     {},
 		omitempty:         {},
+		omitnil:           {},
 		skipValidationTag: {},
 		utf8HexComma:      {},
 		utf8Pipe:          {},
@@ -149,6 +150,7 @@ var (
 		"isbn":                          isISBN,
 		"isbn10":                        isISBN10,
 		"isbn13":                        isISBN13,
+		"issn":                          isISSN,
 		"eth_addr":                      isEthereumAddress,
 		"eth_addr_checksum":             isEthereumAddressChecksum,
 		"btc_addr":                      isBitcoinAddress,
@@ -373,9 +375,9 @@ func isMAC(fl FieldLevel) bool {
 
 // isCIDRv4 is the validation function for validating if the field's value is a valid v4 CIDR address.
 func isCIDRv4(fl FieldLevel) bool {
-	ip, _, err := net.ParseCIDR(fl.Field().String())
+	ip, net, err := net.ParseCIDR(fl.Field().String())
 
-	return err == nil && ip.To4() != nil
+	return err == nil && ip.To4() != nil && net.IP.Equal(ip)
 }
 
 // isCIDRv6 is the validation function for validating if the field's value is a valid v6 CIDR address.
@@ -508,47 +510,47 @@ func isASCII(fl FieldLevel) bool {
 
 // isUUID5 is the validation function for validating if the field's value is a valid v5 UUID.
 func isUUID5(fl FieldLevel) bool {
-	return uUID5Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID5Regex, fl)
 }
 
 // isUUID4 is the validation function for validating if the field's value is a valid v4 UUID.
 func isUUID4(fl FieldLevel) bool {
-	return uUID4Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID4Regex, fl)
 }
 
 // isUUID3 is the validation function for validating if the field's value is a valid v3 UUID.
 func isUUID3(fl FieldLevel) bool {
-	return uUID3Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID3Regex, fl)
 }
 
 // isUUID is the validation function for validating if the field's value is a valid UUID of any version.
 func isUUID(fl FieldLevel) bool {
-	return uUIDRegex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUIDRegex, fl)
 }
 
 // isUUID5RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v5 UUID.
 func isUUID5RFC4122(fl FieldLevel) bool {
-	return uUID5RFC4122Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID5RFC4122Regex, fl)
 }
 
 // isUUID4RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v4 UUID.
 func isUUID4RFC4122(fl FieldLevel) bool {
-	return uUID4RFC4122Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID4RFC4122Regex, fl)
 }
 
 // isUUID3RFC4122 is the validation function for validating if the field's value is a valid RFC4122 v3 UUID.
 func isUUID3RFC4122(fl FieldLevel) bool {
-	return uUID3RFC4122Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUID3RFC4122Regex, fl)
 }
 
 // isUUIDRFC4122 is the validation function for validating if the field's value is a valid RFC4122 UUID of any version.
 func isUUIDRFC4122(fl FieldLevel) bool {
-	return uUIDRFC4122Regex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uUIDRFC4122Regex, fl)
 }
 
 // isULID is the validation function for validating if the field's value is a valid ULID.
 func isULID(fl FieldLevel) bool {
-	return uLIDRegex.MatchString(fl.Field().String())
+	return fieldMatchesRegexByStringerValOrString(uLIDRegex, fl)
 }
 
 // isMD4 is the validation function for validating if the field's value is a valid MD4.
@@ -650,6 +652,32 @@ func isISBN10(fl FieldLevel) bool {
 	return checksum%11 == 0
 }
 
+// isISSN is the validation function for validating if the field's value is a valid ISSN.
+func isISSN(fl FieldLevel) bool {
+	s := fl.Field().String()
+
+	if !iSSNRegex.MatchString(s) {
+		return false
+	}
+	s = strings.ReplaceAll(s, "-", "")
+
+	pos := 8
+	checksum := 0
+
+	for i := 0; i < 7; i++ {
+		checksum += pos * int(s[i]-'0')
+		pos--
+	}
+
+	if s[7] == 'X' {
+		checksum += 10
+	} else {
+		checksum += int(s[7] - '0')
+	}
+
+	return checksum%11 == 0
+}
+
 // isEthereumAddress is the validation function for validating if the field's value is a valid Ethereum address.
 func isEthereumAddress(fl FieldLevel) bool {
 	address := fl.Field().String()
@@ -1413,6 +1441,15 @@ func isURI(fl FieldLevel) bool {
 	panic(fmt.Sprintf("Bad field type %T", field.Interface()))
 }
 
+// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
+func isFileURL(path string) bool {
+	if !strings.HasPrefix(path, "file:/") {
+		return false
+	}
+	_, err := url.ParseRequestURI(path)
+	return err == nil
+}
+
 // isURL is the validation function for validating if the current field's value is a valid URL.
 func isURL(fl FieldLevel) bool {
 	field := fl.Field()
@@ -1420,12 +1457,16 @@ func isURL(fl FieldLevel) bool {
 	switch field.Kind() {
 	case reflect.String:
 
-		s := field.String()
+		s := strings.ToLower(field.String())
 
 		if len(s) == 0 {
 			return false
 		}
 
+		if isFileURL(s) {
+			return true
+		}
+
 		url, err := url.Parse(s)
 		if err != nil || url.Scheme == "" {
 			return false
diff --git a/cache.go b/cache.go
index bbfd2a4..0f4fa6b 100644
--- a/cache.go
+++ b/cache.go
@@ -20,6 +20,7 @@ const (
 	typeOr
 	typeKeys
 	typeEndKeys
+	typeOmitNil
 )
 
 const (
@@ -252,6 +253,10 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
 			current.typeof = typeOmitEmpty
 			continue
 
+		case omitnil:
+			current.typeof = typeOmitNil
+			continue
+
 		case structOnlyTag:
 			current.typeof = typeStructOnly
 			continue
diff --git a/doc.go b/doc.go
index c4dbb59..b474091 100644
--- a/doc.go
+++ b/doc.go
@@ -194,6 +194,13 @@ such as min or max won't run, but if a value is set validation will run.
 
 	Usage: omitempty
 
+# Omit Nil
+
+Allows to skip the validation if the value is nil (same as omitempty, but
+only for the nil-values).
+
+	Usage: omitnil
+
 # Dive
 
 This tells the validator to dive into a slice, array or map and validate that
diff --git a/regexes.go b/regexes.go
index 6c8f985..af98d8d 100644
--- a/regexes.go
+++ b/regexes.go
@@ -22,6 +22,7 @@ const (
 	base64RawURLRegexString          = "^(?:[A-Za-z0-9-_]{4})*(?:[A-Za-z0-9-_]{2,4})$"
 	iSBN10RegexString                = "^(?:[0-9]{9}X|[0-9]{10})$"
 	iSBN13RegexString                = "^(?:(?:97(?:8|9))[0-9]{10})$"
+	iSSNRegexString                  = "^(?:[0-9]{4}-[0-9]{3}[0-9X])$"
 	uUID3RegexString                 = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
 	uUID4RegexString                 = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
 	uUID5RegexString                 = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
@@ -93,6 +94,7 @@ var (
 	base64RawURLRegex          = regexp.MustCompile(base64RawURLRegexString)
 	iSBN10Regex                = regexp.MustCompile(iSBN10RegexString)
 	iSBN13Regex                = regexp.MustCompile(iSBN13RegexString)
+	iSSNRegex                  = regexp.MustCompile(iSSNRegexString)
 	uUID3Regex                 = regexp.MustCompile(uUID3RegexString)
 	uUID4Regex                 = regexp.MustCompile(uUID4RegexString)
 	uUID5Regex                 = regexp.MustCompile(uUID5RegexString)
diff --git a/translations/ar/ar.go b/translations/ar/ar.go
index 038635a..85ec5ea 100644
--- a/translations/ar/ar.go
+++ b/translations/ar/ar.go
@@ -1116,6 +1116,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "يجب أن يكون {0} رقم ISBN-13 صالح",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "يجب أن يكون {0} رقم ISSN صالح",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "يجب أن يكون {0} UUID صالح",
diff --git a/translations/en/en.go b/translations/en/en.go
index 6cb6d7e..4c34ded 100644
--- a/translations/en/en.go
+++ b/translations/en/en.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/go-playground/locales"
 	ut "github.com/go-playground/universal-translator"
+
 	"github.com/go-playground/validator/v10"
 )
 
@@ -1121,6 +1122,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} must be a valid ISBN-13 number",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} must be a valid ISSN number",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} must be a valid UUID",
diff --git a/translations/es/es.go b/translations/es/es.go
index 155e80f..0be3c5a 100644
--- a/translations/es/es.go
+++ b/translations/es/es.go
@@ -1163,6 +1163,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} debe ser un número ISBN-13 válido",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} debe ser un número ISSN válido",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} debe ser un UUID válido",
diff --git a/translations/fa/fa.go b/translations/fa/fa.go
index 32fc69e..44cf32a 100644
--- a/translations/fa/fa.go
+++ b/translations/fa/fa.go
@@ -1116,6 +1116,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} باید یک شابک(ISBN-13) معتبر باشد",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} باید یک شابک(ISSN) معتبر باشد",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} باید یک UUID معتبر باشد",
diff --git a/translations/fr/fr.go b/translations/fr/fr.go
index eb40e81..9dfafe5 100644
--- a/translations/fr/fr.go
+++ b/translations/fr/fr.go
@@ -1153,6 +1153,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} doit être un numéro ISBN-13 valid",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} doit être un numéro ISSN valid",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} doit être un UUID valid",
diff --git a/translations/id/id.go b/translations/id/id.go
index f881a53..96cd8fc 100644
--- a/translations/id/id.go
+++ b/translations/id/id.go
@@ -1153,6 +1153,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} harus berupa nomor ISBN-13 yang valid",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} harus berupa nomor ISSN yang valid",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} harus berupa UUID yang valid",
diff --git a/translations/it/it.go b/translations/it/it.go
index 3f9030c..5c0a426 100644
--- a/translations/it/it.go
+++ b/translations/it/it.go
@@ -977,6 +977,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} deve essere un numero ISBN-13 valido",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} deve essere un numero ISSN valido",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} deve essere un UUID valido",
diff --git a/translations/ja/ja.go b/translations/ja/ja.go
index f106ff8..dea083a 100644
--- a/translations/ja/ja.go
+++ b/translations/ja/ja.go
@@ -1186,6 +1186,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}は正しいISBN-13番号でなければなりません",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0}は正しいISSN番号でなければなりません",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0}は正しいUUIDでなければなりません",
diff --git a/translations/lv/lv.go b/translations/lv/lv.go
index 0804ff3..ee64afa 100644
--- a/translations/lv/lv.go
+++ b/translations/lv/lv.go
@@ -1121,6 +1121,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} jābūt derīgam ISBN-13 numuram",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} jābūt derīgam ISSN numuram",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} jābūt derīgam UUID",
diff --git a/translations/nl/nl.go b/translations/nl/nl.go
index a49f185..43b3067 100644
--- a/translations/nl/nl.go
+++ b/translations/nl/nl.go
@@ -1153,6 +1153,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} moet een geldig ISBN-13 nummer zijn",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} moet een geldig ISSN nummer zijn",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} moet een geldige UUID zijn",
diff --git a/translations/pt/pt.go b/translations/pt/pt.go
index 31fb1a2..8dca562 100644
--- a/translations/pt/pt.go
+++ b/translations/pt/pt.go
@@ -1158,6 +1158,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} deve ser um número ISBN-13 válido",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} deve ser um número ISSN válido",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} deve ser um UUID válido",
diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go
index e22aeae..079ec5e 100644
--- a/translations/pt_BR/pt_BR.go
+++ b/translations/pt_BR/pt_BR.go
@@ -1153,6 +1153,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} deve ser um número ISBN-13 válido",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} deve ser um número ISSN válido",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} deve ser um UUID válido",
diff --git a/translations/ru/ru.go b/translations/ru/ru.go
index 5cafb08..c34ad28 100644
--- a/translations/ru/ru.go
+++ b/translations/ru/ru.go
@@ -1271,6 +1271,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} должен быть ISBN-13 номером",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} должен быть ISSN номером",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} должен быть UUID",
diff --git a/translations/tr/tr.go b/translations/tr/tr.go
index 7c86258..4edcfb0 100644
--- a/translations/tr/tr.go
+++ b/translations/tr/tr.go
@@ -1153,6 +1153,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} geçerli bir ISBN-13 numarası olmalıdır",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} geçerli bir ISSN numarası olmalıdır",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} geçerli bir UUID olmalıdır",
diff --git a/translations/vi/vi.go b/translations/vi/vi.go
index 170938d..0b11407 100644
--- a/translations/vi/vi.go
+++ b/translations/vi/vi.go
@@ -1116,6 +1116,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0} phải là số ISBN-13",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0} phải là số ISSN",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0} phải là giá trị UUID",
diff --git a/translations/zh/zh.go b/translations/zh/zh.go
index 7f45681..0b084e5 100644
--- a/translations/zh/zh.go
+++ b/translations/zh/zh.go
@@ -1235,6 +1235,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}必须是一个有效的ISBN-13编号",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0}必须是一个有效的ISSN编号",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0}必须是一个有效的UUID",
diff --git a/translations/zh_tw/zh_tw.go b/translations/zh_tw/zh_tw.go
index 40cbec2..e465eb6 100644
--- a/translations/zh_tw/zh_tw.go
+++ b/translations/zh_tw/zh_tw.go
@@ -1146,6 +1146,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
 			translation: "{0}必須是一個有效的ISBN-13編號",
 			override:    false,
 		},
+		{
+			tag:         "issn",
+			translation: "{0}必須是一個有效的ISSN編號",
+			override:    false,
+		},
 		{
 			tag:         "uuid",
 			translation: "{0}必須是一個有效的UUID",
diff --git a/util.go b/util.go
index 4bd947b..1685159 100644
--- a/util.go
+++ b/util.go
@@ -1,7 +1,9 @@
 package validator
 
 import (
+	"fmt"
 	"reflect"
+	"regexp"
 	"strconv"
 	"strings"
 	"time"
@@ -292,3 +294,18 @@ func panicIf(err error) {
 		panic(err.Error())
 	}
 }
+
+// Checks if field value matches regex. If fl.Field can be cast to Stringer, it uses the Stringer interfaces
+// String() return value. Otherwise, it uses fl.Field's String() value.
+func fieldMatchesRegexByStringerValOrString(regex *regexp.Regexp, fl FieldLevel) bool {
+	switch fl.Field().Kind() {
+	case reflect.String:
+		return regex.MatchString(fl.Field().String())
+	default:
+		if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
+			return regex.MatchString(stringer.String())
+		} else {
+			return regex.MatchString(fl.Field().String())
+		}
+	}
+}
diff --git a/validator.go b/validator.go
index 342c4ec..a072d39 100644
--- a/validator.go
+++ b/validator.go
@@ -112,6 +112,10 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
 			return
 		}
 
+		if ct.typeof == typeOmitNil && (kind != reflect.Invalid && current.IsNil()) {
+			return
+		}
+
 		if ct.hasTag {
 			if kind == reflect.Invalid {
 				v.str1 = string(append(ns, cf.altName...))
@@ -233,6 +237,26 @@ OUTER:
 			ct = ct.next
 			continue
 
+		case typeOmitNil:
+			v.slflParent = parent
+			v.flField = current
+			v.cf = cf
+			v.ct = ct
+
+			switch field := v.Field(); field.Kind() {
+			case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+				if field.IsNil() {
+					return
+				}
+			default:
+				if v.fldIsPointer && field.Interface() == nil {
+					return
+				}
+			}
+
+			ct = ct.next
+			continue
+
 		case typeEndKeys:
 			return
 
diff --git a/validator_instance.go b/validator_instance.go
index a4dbdd0..d5a7be1 100644
--- a/validator_instance.go
+++ b/validator_instance.go
@@ -22,6 +22,7 @@ const (
 	structOnlyTag         = "structonly"
 	noStructLevelTag      = "nostructlevel"
 	omitempty             = "omitempty"
+	omitnil               = "omitnil"
 	isdefault             = "isdefault"
 	requiredWithoutAllTag = "required_without_all"
 	requiredWithoutTag    = "required_without"


Test patch:
diff --git a/translations/ar/ar_test.go b/translations/ar/ar_test.go
index 6e2a418..122739b 100644
--- a/translations/ar/ar_test.go
+++ b/translations/ar/ar_test.go
@@ -99,6 +99,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -341,6 +342,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "يجب أن يكون ISBN13 رقم ISBN-13 صالح",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "يجب أن يكون ISSN رقم ISSN صالح",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "لا يمكن أن يحتوي Excludes على النص 'text'",
diff --git a/translations/en/en_test.go b/translations/en/en_test.go
index fb44863..6782f3b 100644
--- a/translations/en/en_test.go
+++ b/translations/en/en_test.go
@@ -101,6 +101,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -354,6 +355,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 must be a valid ISBN-13 number",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN must be a valid ISSN number",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes cannot contain the text 'text'",
diff --git a/translations/es/es_test.go b/translations/es/es_test.go
index 4f98655..557002b 100644
--- a/translations/es/es_test.go
+++ b/translations/es/es_test.go
@@ -101,6 +101,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -331,6 +332,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 debe ser un número ISBN-13 válido",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN debe ser un número ISSN válido",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes no puede contener el texto 'text'",
diff --git a/translations/fa/fa_test.go b/translations/fa/fa_test.go
index 456ebe4..0886b4f 100644
--- a/translations/fa/fa_test.go
+++ b/translations/fa/fa_test.go
@@ -99,6 +99,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -340,6 +341,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 باید یک شابک(ISBN-13) معتبر باشد",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN باید یک شابک(ISSN) معتبر باشد",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes نمیتواند شامل 'text' باشد",
diff --git a/translations/fr/fr_test.go b/translations/fr/fr_test.go
index 73a1849..497255a 100644
--- a/translations/fr/fr_test.go
+++ b/translations/fr/fr_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string    `validate:"isbn"`
 		ISBN10            string    `validate:"isbn10"`
 		ISBN13            string    `validate:"isbn13"`
+		ISSN              string    `validate:"issn"`
 		UUID              string    `validate:"uuid"`
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
@@ -324,6 +325,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 doit être un numéro ISBN-13 valid",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN doit être un numéro ISSN valid",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes ne doit pas contenir le texte 'text'",
diff --git a/translations/id/id_test.go b/translations/id/id_test.go
index 6806e10..4a5b70f 100644
--- a/translations/id/id_test.go
+++ b/translations/id/id_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string    `validate:"isbn"`
 		ISBN10            string    `validate:"isbn10"`
 		ISBN13            string    `validate:"isbn13"`
+		ISSN              string    `validate:"issn"`
 		UUID              string    `validate:"uuid"`
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
@@ -324,6 +325,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 harus berupa nomor ISBN-13 yang valid",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN harus berupa nomor ISSN yang valid",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes tidak boleh berisi teks 'text'",
diff --git a/translations/it/it_test.go b/translations/it/it_test.go
index 12fe78c..bd449c5 100644
--- a/translations/it/it_test.go
+++ b/translations/it/it_test.go
@@ -99,6 +99,7 @@ func TestTranslations(t *testing.T) {
 		ISBN                string            `validate:"isbn"`
 		ISBN10              string            `validate:"isbn10"`
 		ISBN13              string            `validate:"isbn13"`
+		ISSN                string            `validate:"issn"`
 		UUID                string            `validate:"uuid"`
 		UUID3               string            `validate:"uuid3"`
 		UUID4               string            `validate:"uuid4"`
@@ -351,6 +352,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 deve essere un numero ISBN-13 valido",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN deve essere un numero ISSN valido",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes non deve contenere il testo 'text'",
diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go
index d236e68..afb507a 100644
--- a/translations/ja/ja_test.go
+++ b/translations/ja/ja_test.go
@@ -101,6 +101,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -347,6 +348,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13は正しいISBN-13番号でなければなりません",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSNは正しいISSN番号でなければなりません",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludesには'text'というテキストを含むことはできません",
diff --git a/translations/lv/lv_test.go b/translations/lv/lv_test.go
index ca99784..99b0f9f 100644
--- a/translations/lv/lv_test.go
+++ b/translations/lv/lv_test.go
@@ -101,6 +101,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -346,6 +347,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 jābūt derīgam ISBN-13 numuram",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN jābūt derīgam ISSN numuram",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes nedrīkst saturēt tekstu 'text'",
diff --git a/translations/nl/nl_test.go b/translations/nl/nl_test.go
index 9cfeb5b..e8f90e2 100644
--- a/translations/nl/nl_test.go
+++ b/translations/nl/nl_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string    `validate:"isbn"`
 		ISBN10            string    `validate:"isbn10"`
 		ISBN13            string    `validate:"isbn13"`
+		ISSN              string    `validate:"issn"`
 		UUID              string    `validate:"uuid"`
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
@@ -324,6 +325,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 moet een geldig ISBN-13 nummer zijn",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN moet een geldig ISSN nummer zijn",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes mag niet de tekst 'text' bevatten",
diff --git a/translations/pt/pt_test.go b/translations/pt/pt_test.go
index 96c8318..4620d25 100644
--- a/translations/pt/pt_test.go
+++ b/translations/pt/pt_test.go
@@ -101,6 +101,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -339,6 +340,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 deve ser um número ISBN-13 válido",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN deve ser um número ISSN válido",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes não deve conter o texto 'text'",
diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go
index a0384cf..7e5565a 100644
--- a/translations/pt_BR/pt_BR_test.go
+++ b/translations/pt_BR/pt_BR_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string    `validate:"isbn"`
 		ISBN10            string    `validate:"isbn10"`
 		ISBN13            string    `validate:"isbn13"`
+		ISSN              string    `validate:"issn"`
 		UUID              string    `validate:"uuid"`
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
@@ -328,6 +329,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 deve ser um número ISBN-13 válido",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN deve ser um número ISSN válido",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes não deve conter o texto 'text'",
diff --git a/translations/ru/ru_test.go b/translations/ru/ru_test.go
index 223966f..c0ff6f0 100644
--- a/translations/ru/ru_test.go
+++ b/translations/ru/ru_test.go
@@ -116,6 +116,7 @@ func TestTranslations(t *testing.T) {
 		ISBN                    string            `validate:"isbn"`
 		ISBN10                  string            `validate:"isbn10"`
 		ISBN13                  string            `validate:"isbn13"`
+		ISSN                    string            `validate:"issn"`
 		UUID                    string            `validate:"uuid"`
 		UUID3                   string            `validate:"uuid3"`
 		UUID4                   string            `validate:"uuid4"`
@@ -358,6 +359,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 должен быть ISBN-13 номером",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN должен быть ISSN номером",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes не должен содержать текст 'text'",
diff --git a/translations/tr/tr_test.go b/translations/tr/tr_test.go
index e18ac6a..f21e32b 100644
--- a/translations/tr/tr_test.go
+++ b/translations/tr/tr_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -330,6 +331,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 geçerli bir ISBN-13 numarası olmalıdır",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN geçerli bir ISSN numarası olmalıdır",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes, 'text' metnini içeremez",
diff --git a/translations/vi/vi_test.go b/translations/vi/vi_test.go
index d26c42d..a767fc3 100644
--- a/translations/vi/vi_test.go
+++ b/translations/vi/vi_test.go
@@ -99,6 +99,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string            `validate:"isbn"`
 		ISBN10            string            `validate:"isbn10"`
 		ISBN13            string            `validate:"isbn13"`
+		ISSN              string            `validate:"issn"`
 		UUID              string            `validate:"uuid"`
 		UUID3             string            `validate:"uuid3"`
 		UUID4             string            `validate:"uuid4"`
@@ -336,6 +337,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13 phải là số ISBN-13",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN phải là số ISSN",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes không được chứa chuỗi 'text'",
diff --git a/translations/zh/zh_test.go b/translations/zh/zh_test.go
index aaf512d..2b8ee05 100644
--- a/translations/zh/zh_test.go
+++ b/translations/zh/zh_test.go
@@ -105,6 +105,7 @@ func TestTranslations(t *testing.T) {
 		ISBN                  string    `validate:"isbn"`
 		ISBN10                string    `validate:"isbn10"`
 		ISBN13                string    `validate:"isbn13"`
+		ISSN                  string    `validate:"issn"`
 		UUID                  string    `validate:"uuid"`
 		UUID3                 string    `validate:"uuid3"`
 		UUID4                 string    `validate:"uuid4"`
@@ -345,6 +346,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13必须是一个有效的ISBN-13编号",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN必须是一个有效的ISSN编号",
+		},
 		{
 			ns:       "Test.EndsWith",
 			expected: "EndsWith必须以文本'end'结尾",
diff --git a/translations/zh_tw/zh_tw_test.go b/translations/zh_tw/zh_tw_test.go
index f15f9f2..e106363 100644
--- a/translations/zh_tw/zh_tw_test.go
+++ b/translations/zh_tw/zh_tw_test.go
@@ -100,6 +100,7 @@ func TestTranslations(t *testing.T) {
 		ISBN              string    `validate:"isbn"`
 		ISBN10            string    `validate:"isbn10"`
 		ISBN13            string    `validate:"isbn13"`
+		ISSN              string    `validate:"issn"`
 		UUID              string    `validate:"uuid"`
 		UUID3             string    `validate:"uuid3"`
 		UUID4             string    `validate:"uuid4"`
@@ -327,6 +328,10 @@ func TestTranslations(t *testing.T) {
 			ns:       "Test.ISBN13",
 			expected: "ISBN13必須是一個有效的ISBN-13編號",
 		},
+		{
+			ns:       "Test.ISSN",
+			expected: "ISSN必須是一個有效的ISSN編號",
+		},
 		{
 			ns:       "Test.Excludes",
 			expected: "Excludes不能包含文字'text'",
diff --git a/validator_test.go b/validator_test.go
index 09ff1df..2826ae7 100644
--- a/validator_test.go
+++ b/validator_test.go
@@ -2575,19 +2575,26 @@ func TestCIDRv4Validation(t *testing.T) {
 		param    string
 		expected bool
 	}{
-		{"10.0.0.0/0", true},
-		{"10.0.0.1/8", true},
-		{"172.16.0.1/16", true},
-		{"192.168.0.1/24", true},
-		{"192.168.255.254/24", true},
+		{"0.0.0.0/0", true},
+		{"10.0.0.0/0", false},
+		{"10.0.0.0/8", true},
+		{"10.0.0.1/8", false},
+		{"172.16.0.0/16", true},
+		{"172.16.0.1/16", false},
+		{"192.168.0.0/24", true},
+		{"192.168.0.1/24", false},
+		{"192.168.255.0/24", true},
+		{"192.168.255.254/24", false},
 		{"192.168.255.254/48", false},
 		{"192.168.255.256/24", false},
-		{"172.16.255.254/16", true},
+		{"172.16.0.0/16", true},
+		{"172.16.255.254/16", false},
 		{"172.16.256.255/16", false},
 		{"2001:cdba:0000:0000:0000:0000:3257:9652/64", false},
 		{"2001:cdba:0000:0000:0000:0000:3257:9652/256", false},
 		{"2001:cdba:0:0:0:0:3257:9652/32", false},
 		{"2001:cdba::3257:9652/16", false},
+		{"172.56.1.0/16", false},
 	}
 
 	validate := New()
@@ -4098,6 +4105,23 @@ func TestUUID3Validation(t *testing.T) {
 	}
 }
 
+type uuidTestType struct {
+	val string
+}
+
+func (u uuidTestType) String() string {
+	return u.val
+}
+
+type uuidAlias string
+
+func (u uuidAlias) String() string {
+	return "This is a UUID " + string(u)
+}
+
+var _ fmt.Stringer = uuidTestType{}
+var _ fmt.Stringer = uuidAlias("")
+
 func TestUUIDValidation(t *testing.T) {
 	tests := []struct {
 		param    string
@@ -4134,6 +4158,31 @@ func TestUUIDValidation(t *testing.T) {
 			}
 		}
 	}
+
+	// Test UUID validation on uuid structs type that implements Stringer interface.
+	structWithValidUUID := struct {
+		UUID uuidTestType `validate:"uuid"`
+	}{
+		UUID: uuidTestType{val: "a987fbc9-4bed-3078-cf07-9141ba07c9f3"},
+	}
+	structWithInvalidUUID := struct {
+		UUID uuidTestType `validate:"uuid"`
+	}{
+		UUID: uuidTestType{val: "934859"},
+	}
+
+	if err := validate.Struct(structWithValidUUID); err != nil {
+		t.Fatalf("UUID failed Error: %s", err)
+	}
+	if err := validate.Struct(structWithInvalidUUID); err == nil {
+		t.Fatal("UUID failed Error expected but received nil")
+	}
+
+	// Test on Alias type with Stringer interface.
+	alias := uuidAlias("a987fbc9-4bed-3078-cf07-9141ba07c9f3")
+	if err := validate.Var(alias, "uuid"); err != nil {
+		t.Fatalf("UUID failed Error: %s", err)
+	}
 }
 
 func TestUUID5RFC4122Validation(t *testing.T) {
@@ -4800,6 +4849,42 @@ func TestISBN10Validation(t *testing.T) {
 	}
 }
 
+func TestISSNValidation(t *testing.T) {
+	tests := []struct {
+		param    string
+		expected bool
+	}{
+		{"", false},
+		{"foo", false},
+		{"20519990", false},
+		{"2051-9991", false},
+		{"2051-999X", false},
+		{"1050-124X", true},
+		{"0317-8471", true},
+	}
+
+	validate := New()
+
+	for i, test := range tests {
+		errs := validate.Var(test.param, "issn")
+
+		if test.expected {
+			if !IsEqual(errs, nil) {
+				t.Fatalf("Index: %d ISSN failed Error: %s", i, errs)
+			}
+		} else {
+			if IsEqual(errs, nil) {
+				t.Fatalf("Index: %d ISSN failed Error: %s", i, errs)
+			} else {
+				val := getError(errs, "", "")
+				if val.Tag() != "issn" {
+					t.Fatalf("Index: %d ISSN failed Error: %s", i, errs)
+				}
+			}
+		}
+	}
+}
+
 func TestExcludesRuneValidation(t *testing.T) {
 	tests := []struct {
 		Value       string `validate:"excludesrune=☻"`
@@ -8090,6 +8175,12 @@ func TestUrl(t *testing.T) {
 		{"./rel/test/dir", false},
 		{"irc:", false},
 		{"http://", false},
+		{"file://path/to/file.txt", true},
+		{"file:///c:/Windows/file.txt", true},
+		{"file://localhost/path/to/file.txt", true},
+		{"file://localhost/c:/WINDOWS/file.txt", true},
+		{"file://", true},
+		{"file:////remotehost/path/file.txt", true},
 	}
 
 	validate := New()
@@ -13147,14 +13238,14 @@ func TestSpiceDBValueFormatValidation(t *testing.T) {
 		tag      string
 		expected bool
 	}{
-		//Must be an asterisk OR a string containing alphanumeric characters and a restricted set a special symbols: _ | / - = +
+		// Must be an asterisk OR a string containing alphanumeric characters and a restricted set a special symbols: _ | / - = +
 		{"*", "spicedb=id", true},
 		{`azAZ09_|/-=+`, "spicedb=id", true},
 		{`a*`, "spicedb=id", false},
 		{`/`, "spicedb=id", true},
 		{"*", "spicedb", true},
 
-		//Must begin and end with a lowercase letter, may also contain numbers and underscores between, min length 3, max length 64
+		// Must begin and end with a lowercase letter, may also contain numbers and underscores between, min length 3, max length 64
 		{"a", "spicedb=permission", false},
 		{"1", "spicedb=permission", false},
 		{"a1", "spicedb=permission", false},
@@ -13164,7 +13255,7 @@ func TestSpiceDBValueFormatValidation(t *testing.T) {
 		{"abcdefghijklmnopqrstuvwxyz_0123456789_abcdefghijklmnopqrstuvwxyz", "spicedb=permission", true},
 		{"abcdefghijklmnopqrstuvwxyz_01234_56789_abcdefghijklmnopqrstuvwxyz", "spicedb=permission", false},
 
-		//Object types follow the same rules as permissions for the type name plus an optional prefix up to 63 characters with a /
+		// Object types follow the same rules as permissions for the type name plus an optional prefix up to 63 characters with a /
 		{"a", "spicedb=type", false},
 		{"1", "spicedb=type", false},
 		{"a1", "spicedb=type", false},
@@ -13557,3 +13648,47 @@ func TestTimeRequired(t *testing.T) {
 	NotEqual(t, err, nil)
 	AssertError(t, err.(ValidationErrors), "TestTime.Time", "TestTime.Time", "Time", "Time", "required")
 }
+
+func TestOmitNilAndRequired(t *testing.T) {
+	type (
+		OmitEmpty struct {
+			Str    string  `validate:"omitempty,required,min=10"`
+			StrPtr *string `validate:"omitempty,required,min=10"`
+			Inner  *OmitEmpty
+		}
+		OmitNil struct {
+			Str    string  `validate:"omitnil,required,min=10"`
+			StrPtr *string `validate:"omitnil,required,min=10"`
+			Inner  *OmitNil
+		}
+	)
+
+	var (
+		validate = New(WithRequiredStructEnabled())
+		valid    = "this is the long string to pass the validation rule"
+	)
+
+	t.Run("compare using valid data", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{Str: valid, StrPtr: &valid, Inner: &OmitEmpty{Str: valid, StrPtr: &valid}})
+		err2 := validate.Struct(OmitNil{Str: valid, StrPtr: &valid, Inner: &OmitNil{Str: valid, StrPtr: &valid}})
+
+		Equal(t, err1, nil)
+		Equal(t, err2, nil)
+	})
+
+	t.Run("compare fully empty omitempty and omitnil", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{})
+		err2 := validate.Struct(OmitNil{})
+
+		Equal(t, err1, nil)
+		AssertError(t, err2, "OmitNil.Str", "OmitNil.Str", "Str", "Str", "required")
+	})
+
+	t.Run("validate in deep", func(t *testing.T) {
+		err1 := validate.Struct(OmitEmpty{Str: valid, Inner: &OmitEmpty{}})
+		err2 := validate.Struct(OmitNil{Str: valid, Inner: &OmitNil{}})
+
+		Equal(t, err1, nil)
+		AssertError(t, err2, "OmitNil.Inner.Str", "OmitNil.Inner.Str", "Str", "Str", "required")
+	})
+}