From 5fcbb7788e8ffdac2a2e1018476cafb52d0b63dc Mon Sep 17 00:00:00 2001 From: Justin Tay <49700559+justin-tay@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:56:59 +0800 Subject: [PATCH] Fix minimum maximum validators with type array --- .../schema/keyword/BaseKeywordValidator.java | 18 +++++++-- .../keyword/ExclusiveMaximumValidator.java | 2 +- .../keyword/ExclusiveMinimumValidator.java | 2 +- .../schema/keyword/MaximumValidator.java | 2 +- .../schema/keyword/MinimumValidator.java | 2 +- .../schema/ExclusiveMaximumValidatorTest.java | 40 +++++++++++++++++++ .../schema/ExclusiveMinimumValidatorTest.java | 13 ++++++ .../schema/MaximumValidatorTest.java | 16 ++++++++ .../schema/MinimumValidatorTest.java | 17 ++++++++ 9 files changed, 105 insertions(+), 7 deletions(-) create mode 100644 src/test/java/com/networknt/schema/ExclusiveMaximumValidatorTest.java diff --git a/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java b/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java index 342b11364..62f864366 100644 --- a/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java +++ b/src/main/java/com/networknt/schema/keyword/BaseKeywordValidator.java @@ -17,6 +17,7 @@ package com.networknt.schema.keyword; import tools.jackson.databind.JsonNode; + import com.networknt.schema.ErrorMessages; import com.networknt.schema.Schema; import com.networknt.schema.MessageSourceError; @@ -83,12 +84,23 @@ public Schema getParentSchema() { return this.parentSchema; } - protected String getNodeFieldType() { + protected boolean hasType(String type) { JsonNode typeField = this.getParentSchema().getSchemaNode().get("type"); if (typeField != null) { - return typeField.asString(); + switch (typeField.getNodeType()) { + case STRING: + return type.equals(typeField.stringValue()); + case ARRAY: + for (JsonNode item : typeField.values()) { + if (item.isString() && type.equals(item.stringValue())) { + return true; + } + } + default: + return false; + } } - return null; + return false; } protected void preloadSchemas(final Collection schemas) { diff --git a/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java b/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java index c73dee01b..9a4fb4810 100644 --- a/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java +++ b/src/main/java/com/networknt/schema/keyword/ExclusiveMaximumValidator.java @@ -41,7 +41,7 @@ public ExclusiveMaximumValidator(SchemaLocation schemaLocation, final JsonNode s throw new SchemaException("exclusiveMaximum value is not a number"); } final String maximumText = schemaNode.asString(); - if ((schemaNode.isLong() || schemaNode.isInt()) && (JsonType.INTEGER.toString().equals(getNodeFieldType()))) { + if ((schemaNode.isLong() || schemaNode.isInt()) && hasType(JsonType.INTEGER.toString())) { // "integer", and within long range final long lm = schemaNode.asLong(); typedMaximum = new ThresholdMixin() { diff --git a/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java b/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java index 600e37dce..6b0170649 100644 --- a/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java +++ b/src/main/java/com/networknt/schema/keyword/ExclusiveMinimumValidator.java @@ -45,7 +45,7 @@ public ExclusiveMinimumValidator(SchemaLocation schemaLocation, final JsonNode s throw new SchemaException("exclusiveMinimum value is not a number"); } final String minimumText = schemaNode.asString(); - if ((schemaNode.isLong() || schemaNode.isInt()) && JsonType.INTEGER.toString().equals(getNodeFieldType())) { + if ((schemaNode.isLong() || schemaNode.isInt()) && hasType(JsonType.INTEGER.toString())) { // "integer", and within long range final long lmin = schemaNode.asLong(); typedMinimum = new ThresholdMixin() { diff --git a/src/main/java/com/networknt/schema/keyword/MaximumValidator.java b/src/main/java/com/networknt/schema/keyword/MaximumValidator.java index 2d38e2f08..0dd131ebc 100644 --- a/src/main/java/com/networknt/schema/keyword/MaximumValidator.java +++ b/src/main/java/com/networknt/schema/keyword/MaximumValidator.java @@ -54,7 +54,7 @@ public MaximumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode } final String maximumText = schemaNode.asString(); - if ((schemaNode.isLong() || schemaNode.isInt()) && (JsonType.INTEGER.toString().equals(getNodeFieldType()))) { + if ((schemaNode.isLong() || schemaNode.isInt()) && hasType(JsonType.INTEGER.toString())) { // "integer", and within long range final long lm = schemaNode.asLong(); this.typedMaximum = new ThresholdMixin() { diff --git a/src/main/java/com/networknt/schema/keyword/MinimumValidator.java b/src/main/java/com/networknt/schema/keyword/MinimumValidator.java index 25012b7da..578511409 100644 --- a/src/main/java/com/networknt/schema/keyword/MinimumValidator.java +++ b/src/main/java/com/networknt/schema/keyword/MinimumValidator.java @@ -58,7 +58,7 @@ public MinimumValidator(SchemaLocation schemaLocation, final JsonNode schemaNode } final String minimumText = schemaNode.asString(); - if ((schemaNode.isLong() || schemaNode.isInt()) && JsonType.INTEGER.toString().equals(getNodeFieldType())) { + if ((schemaNode.isLong() || schemaNode.isInt()) && hasType(JsonType.INTEGER.toString())) { // "integer", and within long range final long lmin = schemaNode.asLong(); this.typedMinimum = new ThresholdMixin() { diff --git a/src/test/java/com/networknt/schema/ExclusiveMaximumValidatorTest.java b/src/test/java/com/networknt/schema/ExclusiveMaximumValidatorTest.java new file mode 100644 index 000000000..287b3e0be --- /dev/null +++ b/src/test/java/com/networknt/schema/ExclusiveMaximumValidatorTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.networknt.schema; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import com.networknt.schema.dialect.Dialects; + +/** + * Test ExclusiveMaximumValidator validator. + */ +class ExclusiveMaximumValidatorTest { + @Test + void exclusiveMaximum() { + final String schemaString = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": ["null", "integer"], + "exclusiveMaximum": 10 + } + """; + final SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft7()); + assertEquals(1, schemaRegistry.getSchema(schemaString).validate("10", InputFormat.JSON).size()); + } +} diff --git a/src/test/java/com/networknt/schema/ExclusiveMinimumValidatorTest.java b/src/test/java/com/networknt/schema/ExclusiveMinimumValidatorTest.java index 48e17dfff..f5da433ed 100644 --- a/src/test/java/com/networknt/schema/ExclusiveMinimumValidatorTest.java +++ b/src/test/java/com/networknt/schema/ExclusiveMinimumValidatorTest.java @@ -92,4 +92,17 @@ void draftV7ShouldNotAllowExclusiveMinimumBoolean() { SchemaRegistry factory = SchemaRegistry.withDialect(dialect); assertThrows(SchemaException.class, () -> factory.getSchema(schemaData)); } + + @Test + void exclusiveMinimum() { + final String schemaString = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": ["null", "integer"], + "exclusiveMinimum": 10 + } + """; + final SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft7()); + assertEquals(1, schemaRegistry.getSchema(schemaString).validate("10", InputFormat.JSON).size()); + } } diff --git a/src/test/java/com/networknt/schema/MaximumValidatorTest.java b/src/test/java/com/networknt/schema/MaximumValidatorTest.java index ada7c642a..c7c96f4a5 100644 --- a/src/test/java/com/networknt/schema/MaximumValidatorTest.java +++ b/src/test/java/com/networknt/schema/MaximumValidatorTest.java @@ -21,11 +21,14 @@ import tools.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; +import com.networknt.schema.dialect.Dialects; + import java.io.IOException; import java.math.BigDecimal; import java.util.List; import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -326,6 +329,19 @@ private static void expectSomeMessages(String[][] values, String schemaTemplate, assertFalse(messages.isEmpty(), format(MaximumValidatorTest.NEGATIVE_TEST_CASE_TEMPLATE, value, maximum)); } } + + @Test + void maximumWithArrayType() { + final String schemaString = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": ["null", "integer"], + "maximum": 10 + } + """; + final SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft7()); + assertEquals(1, schemaRegistry.getSchema(schemaString).validate("11", InputFormat.JSON).size()); + } } diff --git a/src/test/java/com/networknt/schema/MinimumValidatorTest.java b/src/test/java/com/networknt/schema/MinimumValidatorTest.java index 55f63351d..d5ef94c71 100644 --- a/src/test/java/com/networknt/schema/MinimumValidatorTest.java +++ b/src/test/java/com/networknt/schema/MinimumValidatorTest.java @@ -18,6 +18,7 @@ import static com.networknt.schema.MaximumValidatorTest.augmentWithQuotes; import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -28,6 +29,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import com.networknt.schema.dialect.Dialects; + import tools.jackson.databind.DeserializationFeature; import tools.jackson.databind.JsonNode; import tools.jackson.databind.ObjectMapper; @@ -298,6 +301,20 @@ private void expectNoMessages(String[][] values, String integer, ObjectMapper ma assertTrue(messages.isEmpty(), format(MinimumValidatorTest.POSITIVT_MESSAGE_TEMPLATE, value, minimum)); } } + + @Test + void minimumWithArrayType() { + final String schemaString = """ + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": ["null", "integer"], + "minimum": 10 + } + """; + final SchemaRegistry schemaRegistry = SchemaRegistry.withDialect(Dialects.getDraft7()); + assertEquals(1, schemaRegistry.getSchema(schemaString).validate("9", InputFormat.JSON).size()); + } + }