Add support for if/then/else
diff --git a/errors.go b/errors.go
index d39f019..29ab5f2 100644
--- a/errors.go
+++ b/errors.go
@@ -145,6 +145,16 @@
NumberLTError struct {
ResultErrorFields
}
+
+ // NumberIfThenError. ErrorDetails: -
+ NumberIfThenError struct {
+ ResultErrorFields
+ }
+
+ // NumberIfElseError. ErrorDetails: -
+ NumberIfElseError struct {
+ ResultErrorFields
+ }
)
// newError takes a ResultError type and sets the type, context, description, details, value, and field
@@ -230,6 +240,12 @@
case *NumberLTError:
t = "number_lt"
d = locale.NumberLT()
+ case *NumberIfThenError:
+ t = "number_if_then"
+ d = locale.NumberLT()
+ case *NumberIfElseError:
+ t = "number_if_else"
+ d = locale.NumberLT()
}
err.SetType(t)
diff --git a/json_schema_test_suite/ifthenelse/data_00.json b/json_schema_test_suite/ifthenelse/data_00.json
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_00.json
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_01.json b/json_schema_test_suite/ifthenelse/data_01.json
new file mode 100644
index 0000000..84ed78b
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_01.json
@@ -0,0 +1 @@
+"hello"
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_10.json b/json_schema_test_suite/ifthenelse/data_10.json
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_10.json
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_11.json b/json_schema_test_suite/ifthenelse/data_11.json
new file mode 100644
index 0000000..84ed78b
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_11.json
@@ -0,0 +1 @@
+"hello"
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_20.json b/json_schema_test_suite/ifthenelse/data_20.json
new file mode 100644
index 0000000..c227083
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_20.json
@@ -0,0 +1 @@
+0
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_21.json b/json_schema_test_suite/ifthenelse/data_21.json
new file mode 100644
index 0000000..84ed78b
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_21.json
@@ -0,0 +1 @@
+"hello"
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_30.json b/json_schema_test_suite/ifthenelse/data_30.json
new file mode 100644
index 0000000..d7d17fc
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_30.json
@@ -0,0 +1 @@
+-1
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_31.json b/json_schema_test_suite/ifthenelse/data_31.json
new file mode 100644
index 0000000..32676ca
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_31.json
@@ -0,0 +1 @@
+-100
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_32.json b/json_schema_test_suite/ifthenelse/data_32.json
new file mode 100644
index 0000000..e440e5c
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_32.json
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_40.json b/json_schema_test_suite/ifthenelse/data_40.json
new file mode 100644
index 0000000..d7d17fc
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_40.json
@@ -0,0 +1 @@
+-1
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_41.json b/json_schema_test_suite/ifthenelse/data_41.json
new file mode 100644
index 0000000..bf0d87a
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_41.json
@@ -0,0 +1 @@
+4
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_42.json b/json_schema_test_suite/ifthenelse/data_42.json
new file mode 100644
index 0000000..e440e5c
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_42.json
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_50.json b/json_schema_test_suite/ifthenelse/data_50.json
new file mode 100644
index 0000000..d7d17fc
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_50.json
@@ -0,0 +1 @@
+-1
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_51.json b/json_schema_test_suite/ifthenelse/data_51.json
new file mode 100644
index 0000000..32676ca
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_51.json
@@ -0,0 +1 @@
+-100
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_52.json b/json_schema_test_suite/ifthenelse/data_52.json
new file mode 100644
index 0000000..bf0d87a
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_52.json
@@ -0,0 +1 @@
+4
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_53.json b/json_schema_test_suite/ifthenelse/data_53.json
new file mode 100644
index 0000000..e440e5c
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_53.json
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_60.json b/json_schema_test_suite/ifthenelse/data_60.json
new file mode 100644
index 0000000..32676ca
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_60.json
@@ -0,0 +1 @@
+-100
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/data_61.json b/json_schema_test_suite/ifthenelse/data_61.json
new file mode 100644
index 0000000..e440e5c
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/data_61.json
@@ -0,0 +1 @@
+3
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_0.json b/json_schema_test_suite/ifthenelse/schema_0.json
new file mode 100644
index 0000000..a04989f
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_0.json
@@ -0,0 +1,5 @@
+{
+ "if": {
+ "const": 0
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_1.json b/json_schema_test_suite/ifthenelse/schema_1.json
new file mode 100644
index 0000000..d8162ed
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_1.json
@@ -0,0 +1,5 @@
+{
+ "then": {
+ "const": 0
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_2.json b/json_schema_test_suite/ifthenelse/schema_2.json
new file mode 100644
index 0000000..2971b50
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_2.json
@@ -0,0 +1,5 @@
+{
+ "else": {
+ "const": 0
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_3.json b/json_schema_test_suite/ifthenelse/schema_3.json
new file mode 100644
index 0000000..7628602
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_3.json
@@ -0,0 +1,9 @@
+{
+ "if": {
+ "exclusiveMaximum": true,
+ "maximum": 0
+ },
+ "then": {
+ "minimum": -10
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_4.json b/json_schema_test_suite/ifthenelse/schema_4.json
new file mode 100644
index 0000000..2c6b057
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_4.json
@@ -0,0 +1,9 @@
+{
+ "if": {
+ "exclusiveMaximum": true,
+ "maximum": 0
+ },
+ "else": {
+ "multipleOf": 2
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_5.json b/json_schema_test_suite/ifthenelse/schema_5.json
new file mode 100644
index 0000000..6e0f754
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_5.json
@@ -0,0 +1,12 @@
+{
+ "if": {
+ "exclusiveMaximum": true,
+ "maximum": 0
+ },
+ "then": {
+ "minimum": -10
+ },
+ "else": {
+ "multipleOf": 2
+ }
+}
\ No newline at end of file
diff --git a/json_schema_test_suite/ifthenelse/schema_6.json b/json_schema_test_suite/ifthenelse/schema_6.json
new file mode 100644
index 0000000..1170afa
--- /dev/null
+++ b/json_schema_test_suite/ifthenelse/schema_6.json
@@ -0,0 +1,20 @@
+{
+ "allOf": [
+ {
+ "if": {
+ "exclusiveMaximum": true,
+ "maximum": 0
+ }
+ },
+ {
+ "then": {
+ "minimum": -10
+ }
+ },
+ {
+ "else": {
+ "multipleOf": 2
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/locales.go b/locales.go
index 9446f84..940f879 100644
--- a/locales.go
+++ b/locales.go
@@ -76,6 +76,9 @@
HttpBadStatus() string
ParseError() string
+ NumberIfThen() string
+ NumberIfElse() string
+
// ErrorFormat
ErrorFormat() string
}
@@ -271,6 +274,15 @@
return `Expected: {{.expected}}, given: Invalid JSON`
}
+//If/Else
+func (l DefaultLocale) NumberIfThen() string {
+ return `Must validate "then" as "if" was valid`
+}
+
+func (l DefaultLocale) NumberIfElse() string {
+ return `Must validate "else" as "if" was not valid`
+}
+
const (
STRING_NUMBER = "number"
STRING_ARRAY_OF_STRINGS = "array of strings"
diff --git a/schema.go b/schema.go
index f1fbde3..ff8f64e 100644
--- a/schema.go
+++ b/schema.go
@@ -834,6 +834,54 @@
}
}
+ if existsMapKey(m, KEY_IF) {
+ if isKind(m[KEY_IF], reflect.Map) {
+ newSchema := &subSchema{property: KEY_IF, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetIf(newSchema)
+ err := d.parseSchema(m[KEY_IF], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_IF, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+
+ if existsMapKey(m, KEY_THEN) {
+ if isKind(m[KEY_THEN], reflect.Map) {
+ newSchema := &subSchema{property: KEY_THEN, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetThen(newSchema)
+ err := d.parseSchema(m[KEY_THEN], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_THEN, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+
+ if existsMapKey(m, KEY_ELSE) {
+ if isKind(m[KEY_ELSE], reflect.Map) {
+ newSchema := &subSchema{property: KEY_ELSE, parent: currentSchema, ref: currentSchema.ref}
+ currentSchema.SetElse(newSchema)
+ err := d.parseSchema(m[KEY_ELSE], newSchema)
+ if err != nil {
+ return err
+ }
+ } else {
+ return errors.New(formatErrorDescription(
+ Locale.MustBeOfAn(),
+ ErrorDetails{"x": KEY_ELSE, "y": TYPE_OBJECT},
+ ))
+ }
+ }
+
return nil
}
diff --git a/schema_test.go b/schema_test.go
index 9c40e71..51d6b96 100644
--- a/schema_test.go
+++ b/schema_test.go
@@ -213,6 +213,24 @@
{"phase": "not more complex schema", "test": "match", "schema": "not/schema_2.json", "data": "not/data_20.json", "valid": "true"},
{"phase": "not more complex schema", "test": "other match", "schema": "not/schema_2.json", "data": "not/data_21.json", "valid": "true"},
{"phase": "not more complex schema", "test": "mismatch", "schema": "not/schema_2.json", "data": "not/data_22.json", "valid": "false", "errors": "number_not"},
+ {"phase": "if then else", "test": "ignore if without then or else, valid when valid against lone if", "schema": "ifthenelse/schema_0.json", "data": "ifthenelse/data_00.json", "valid": "true"},
+ {"phase": "if then else", "test": "ignore if without then or else, valid when invailid against lone if", "schema": "ifthenelse/schema_0.json", "data": "ifthenelse/data_01.json", "valid": "true"},
+ {"phase": "if then else", "test": "ignore then without if, valid when valid against lone then", "schema": "ifthenelse/schema_1.json", "data": "ifthenelse/data_10.json", "valid": "true"},
+ {"phase": "if then else", "test": "ignore then without if, valid when invailid against lone then", "schema": "ifthenelse/schema_1.json", "data": "ifthenelse/data_11.json", "valid": "true"},
+ {"phase": "if then else", "test": "ignore else without if, valid when valid against lone else", "schema": "ifthenelse/schema_2.json", "data": "ifthenelse/data_20.json", "valid": "true"},
+ {"phase": "if then else", "test": "ignore else without if, valid when invailid against lone else", "schema": "ifthenelse/schema_2.json", "data": "ifthenelse/data_21.json", "valid": "true"},
+ {"phase": "if then else", "test": "if and then without else, valid through then", "schema": "ifthenelse/schema_3.json", "data": "ifthenelse/data_30.json", "valid": "true"},
+ {"phase": "if then else", "test": "if and then without else, invalid through then", "schema": "ifthenelse/schema_3.json", "data": "ifthenelse/data_31.json", "valid": "false", "errors": "number_if_then"},
+ {"phase": "if then else", "test": "if and then without else, valid when if test fails", "schema": "ifthenelse/schema_3.json", "data": "ifthenelse/data_32.json", "valid": "true"},
+ {"phase": "if then else", "test": "if and else without then, valid when if test passes", "schema": "ifthenelse/schema_4.json", "data": "ifthenelse/data_40.json", "valid": "true"},
+ {"phase": "if then else", "test": "if and else without then, valid through else", "schema": "ifthenelse/schema_4.json", "data": "ifthenelse/data_41.json", "valid": "true"},
+ {"phase": "if then else", "test": "if and else without then, invalid through else", "schema": "ifthenelse/schema_4.json", "data": "ifthenelse/data_42.json", "valid": "false", "errors": "number_if_else"},
+ {"phase": "if then else", "test": "validate against correct branch, then vs else, valid through then", "schema": "ifthenelse/schema_5.json", "data": "ifthenelse/data_50.json", "valid": "true"},
+ {"phase": "if then else", "test": "validate against correct branch, then vs else, invalid through then", "schema": "ifthenelse/schema_5.json", "data": "ifthenelse/data_51.json", "valid": "false", "errors": "number_if_then"},
+ {"phase": "if then else", "test": "validate against correct branch, then vs else, valid through else", "schema": "ifthenelse/schema_5.json", "data": "ifthenelse/data_52.json", "valid": "true"},
+ {"phase": "if then else", "test": "validate against correct branch, then vs else, invalid through else", "schema": "ifthenelse/schema_5.json", "data": "ifthenelse/data_53.json", "valid": "false", "errors": "number_if_else"},
+ {"phase": "if then else", "test": "non-interference across combined schemas, then vs else, valid, but woud have been invalid through then", "schema": "ifthenelse/schema_6.json", "data": "ifthenelse/data_60.json", "valid": "true"},
+ {"phase": "if then else", "test": "non-interference across combined schemas, then vs else, valid, but would have been invalid through else", "schema": "ifthenelse/schema_6.json", "data": "ifthenelse/data_61.json", "valid": "true"},
{"phase": "minProperties validation", "test": "longer is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_00.json", "valid": "true"},
{"phase": "minProperties validation", "test": "exact length is valid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_01.json", "valid": "true"},
{"phase": "minProperties validation", "test": "too short is invalid", "schema": "minProperties/schema_0.json", "data": "minProperties/data_02.json", "valid": "false", "errors": "array_min_properties"},
diff --git a/subSchema.go b/subSchema.go
index 9961d92..42dad87 100644
--- a/subSchema.go
+++ b/subSchema.go
@@ -68,10 +68,12 @@
KEY_ANY_OF = "anyOf"
KEY_ALL_OF = "allOf"
KEY_NOT = "not"
+ KEY_IF = "if"
+ KEY_THEN = "then"
+ KEY_ELSE = "else"
)
type subSchema struct {
-
// basic subSchema meta properties
id *gojsonreference.JsonReference
title *string
@@ -134,6 +136,9 @@
anyOf []*subSchema
allOf []*subSchema
not *subSchema
+ _if *subSchema // if/else are golang keywords
+ _then *subSchema
+ _else *subSchema
}
func (s *subSchema) AddEnum(i interface{}) error {
@@ -181,6 +186,18 @@
s.not = subSchema
}
+func (s *subSchema) SetIf(subSchema *subSchema) {
+ s._if = subSchema
+}
+
+func (s *subSchema) SetThen(subSchema *subSchema) {
+ s._then = subSchema
+}
+
+func (s *subSchema) SetElse(subSchema *subSchema) {
+ s._else = subSchema
+}
+
func (s *subSchema) AddRequired(value string) error {
if isStringInSlice(s.required, value) {
diff --git a/validation.go b/validation.go
index 9afea25..9470e3a 100644
--- a/validation.go
+++ b/validation.go
@@ -374,6 +374,22 @@
}
}
+ if currentSubSchema._if != nil {
+ validationResultIf := currentSubSchema._if.subValidateWithContext(currentNode, context)
+ if currentSubSchema._then != nil && validationResultIf.Valid() {
+ validationResultThen := currentSubSchema._then.subValidateWithContext(currentNode, context)
+ if !validationResultThen.Valid() {
+ result.addError(new(NumberIfThenError), context, currentNode, ErrorDetails{})
+ }
+ }
+ if currentSubSchema._else != nil && !validationResultIf.Valid() {
+ validationResultThen := currentSubSchema._else.subValidateWithContext(currentNode, context)
+ if !validationResultThen.Valid() {
+ result.addError(new(NumberIfElseError), context, currentNode, ErrorDetails{})
+ }
+ }
+ }
+
result.incrementScore()
}