Skip to content

Commit b7430f8

Browse files
author
Dean Karn
authored
Merge pull request #571 from psampaz/json
Add isJSON validation
2 parents 68e8e13 + 0c80f87 commit b7430f8

File tree

5 files changed

+78
-1
lines changed

5 files changed

+78
-1
lines changed

baked_in.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"crypto/sha256"
7+
"encoding/json"
78
"fmt"
89
"net"
910
"net/url"
@@ -166,6 +167,7 @@ var (
166167
"html_encoded": isHTMLEncoded,
167168
"url_encoded": isURLEncoded,
168169
"dir": isDir,
170+
"json": isJSON,
169171
"hostname_port": isHostnamePort,
170172
"lowercase": isLowercase,
171173
"uppercase": isUppercase,
@@ -2011,6 +2013,17 @@ func isDir(fl FieldLevel) bool {
20112013
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
20122014
}
20132015

2016+
// isJSON is the validation function for validating if the current field's value is a valid json string.
2017+
func isJSON(fl FieldLevel) bool {
2018+
field := fl.Field()
2019+
2020+
if field.Kind() == reflect.String {
2021+
val := field.String()
2022+
return json.Valid([]byte(val))
2023+
}
2024+
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
2025+
}
2026+
20142027
// isHostnamePort validates a <dns>:<port> combination for fields typically used for socket address.
20152028
func isHostnamePort(fl FieldLevel) bool {
20162029
val := fl.Field().String()

doc.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,12 @@ does any email provider accept all possibilities.
685685
686686
Usage: email
687687
688+
JSON String
689+
690+
This validates that a string value is valid JSON
691+
692+
Usage: json
693+
688694
File path
689695
690696
This validates that a string value contains a valid file path and that

translations/en/en.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
13221322
},
13231323
},
13241324
{
1325+
tag: "json",
1326+
translation: "{0} must be a valid json string",
1327+
override: false,
1328+
},
1329+
{
13251330
tag: "lowercase",
13261331
translation: "{0} must be a lowercase string",
13271332
override: false,

translations/en/en_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ func TestTranslations(t *testing.T) {
141141
UniqueSlice []string `validate:"unique"`
142142
UniqueArray [3]string `validate:"unique"`
143143
UniqueMap map[string]string `validate:"unique"`
144+
JSONString string `validate:"json"`
144145
LowercaseString string `validate:"lowercase"`
145146
UppercaseString string `validate:"uppercase"`
146147
}
@@ -637,6 +638,10 @@ func TestTranslations(t *testing.T) {
637638
ns: "Test.UniqueMap",
638639
expected: "UniqueMap must contain unique values",
639640
},
641+
{
642+
ns: "Test.JSONString",
643+
expected: "JSONString must be a valid json string",
644+
},
640645
{
641646
ns: "Test.LowercaseString",
642647
expected: "LowercaseString must be a lowercase string",

validator_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9004,6 +9004,54 @@ func TestGetTag(t *testing.T) {
90049004
Equal(t, tag, "mytag")
90059005
}
90069006

9007+
func TestJSONValidation(t *testing.T) {
9008+
tests := []struct {
9009+
param string
9010+
expected bool
9011+
}{
9012+
{`foo`, false},
9013+
{`}{`, false},
9014+
{`{]`, false},
9015+
{`{}`, true},
9016+
{`{"foo":"bar"}`, true},
9017+
{`{"foo":"bar","bar":{"baz":["qux"]}}`, true},
9018+
{`{"foo": 3 "bar": 4}`, false},
9019+
{`{"foo": 3 ,"bar": 4`, false},
9020+
{`{foo": 3, "bar": 4}`, false},
9021+
{`foo`, false},
9022+
{`1`, true},
9023+
{`true`, true},
9024+
{`null`, true},
9025+
{`"null"`, true},
9026+
}
9027+
9028+
validate := New()
9029+
9030+
for i, test := range tests {
9031+
9032+
errs := validate.Var(test.param, "json")
9033+
9034+
if test.expected {
9035+
if !IsEqual(errs, nil) {
9036+
t.Fatalf("Index: %d json failed Error: %s", i, errs)
9037+
}
9038+
} else {
9039+
if IsEqual(errs, nil) {
9040+
t.Fatalf("Index: %d json failed Error: %s", i, errs)
9041+
} else {
9042+
val := getError(errs, "", "")
9043+
if val.Tag() != "json" {
9044+
t.Fatalf("Index: %d json failed Error: %s", i, errs)
9045+
}
9046+
}
9047+
}
9048+
}
9049+
9050+
PanicMatches(t, func() {
9051+
_ = validate.Var(2, "json")
9052+
}, "Bad field type int")
9053+
}
9054+
90079055
func Test_hostnameport_validator(t *testing.T) {
90089056

90099057
type Host struct {
@@ -9107,4 +9155,4 @@ func TestUppercaseValidation(t *testing.T) {
91079155
PanicMatches(t, func() {
91089156
_ = validate.Var(2, "uppercase")
91099157
}, "Bad field type int")
9110-
}
9158+
}

0 commit comments

Comments
 (0)