Repository: networknt/json-schema-validator
Pull request #497 (2022-01-14 06:52:48)

parent schema of additionItems can be correctly referenced (#493)
From Issue #493 , the root cause is that the 'parent schema' of an additionalItems was always be 'null'. So that when looking for the $ref, only those inside 'additionalItems' can be got successfully.

Please let me know if the code I change makes sense and doesn't cause any side effects.


Patch:
diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java
index 80c7042..0785efe 100644
--- a/src/main/java/com/networknt/schema/ItemsValidator.java
+++ b/src/main/java/com/networknt/schema/ItemsValidator.java
@@ -58,7 +58,7 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
                 if (addItemNode.isBoolean()) {
                     additionalItems = addItemNode.asBoolean();
                 } else if (addItemNode.isObject()) {
-                    foundAdditionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUri(), addItemNode);
+                    foundAdditionalSchema = new JsonSchema(validationContext, "#", parentSchema.getCurrentUri(), addItemNode, parentSchema);
                 }
             }
         }
@@ -197,4 +197,4 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
             additionalSchema.initializeValidators();
         }
     }
-}
\ No newline at end of file
+}


Test patch:
diff --git a/src/test/java/com/networknt/schema/Issue493Test.java b/src/test/java/com/networknt/schema/Issue493Test.java
new file mode 100644
index 0000000..6bc65d6
--- /dev/null
+++ b/src/test/java/com/networknt/schema/Issue493Test.java
@@ -0,0 +1,94 @@
+package com.networknt.schema;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+class Issue493Test
+{
+
+    private static JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
+    private static String schemaPath1 = "/schema/issue493.json";
+
+    private JsonNode getJsonNodeFromJsonData (String jsonFilePath)
+            throws Exception
+    {
+        InputStream content = getClass().getResourceAsStream(jsonFilePath);
+        ObjectMapper mapper = new ObjectMapper();
+        return mapper.readTree(content);
+    }
+
+    @Test
+    @DisplayName("Test valid with required item only")
+    void testValidJson1 ()
+            throws Exception
+    {
+        InputStream schemaInputStream = Issue493Test.class.getResourceAsStream(schemaPath1);
+        JsonSchema schema = factory.getSchema(schemaInputStream);
+        JsonNode node = getJsonNodeFromJsonData("/data/issue493-valid-1.json");
+        Set<ValidationMessage> errors = schema.validate(node);
+        Assertions.assertTrue(errors.isEmpty());
+    }
+
+    @Test
+    @DisplayName("Test valid with optional item")
+    void testValidJson2 ()
+            throws Exception
+    {
+        InputStream schemaInputStream = Issue493Test.class.getResourceAsStream(schemaPath1);
+        JsonSchema schema = factory.getSchema(schemaInputStream);
+        JsonNode node = getJsonNodeFromJsonData("/data/issue493-valid-2.json");
+        Set<ValidationMessage> errors = schema.validate(node);
+        Assertions.assertTrue(errors.isEmpty());
+    }
+
+    @Test
+    @DisplayName("Test invalid with required item but wrong type")
+    void testInvalidJson1 ()
+            throws Exception
+    {
+        InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
+        JsonSchema schema = factory.getSchema(schemaInputStream);
+        JsonNode node = getJsonNodeFromJsonData("/data/issue493-invalid-1.json");
+        Set<ValidationMessage> errors = schema.validate(node);
+        Assertions.assertEquals(2, errors.size());
+
+        Set<String> allErrorMessages = new HashSet<>();
+        errors.forEach(vm -> {
+            allErrorMessages.add(vm.getMessage());
+        });
+        assertThat(allErrorMessages,
+                   Matchers.containsInAnyOrder("$.parameters[0].value: string found, integer expected",
+                                               "$.parameters[0].value: does not match the regex pattern ^\\{\\{.+\\}\\}$"));
+    }
+
+    @Test
+    @DisplayName("Test invalid with optional item but wrong type")
+    void testInvalidJson2 ()
+            throws Exception
+    {
+        InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
+        JsonSchema schema = factory.getSchema(schemaInputStream);
+        JsonNode node = getJsonNodeFromJsonData("/data/issue493-invalid-2.json");
+        Set<ValidationMessage> errors = schema.validate(node);
+        Assertions.assertEquals(2, errors.size());
+
+        Set<String> allErrorMessages = new HashSet<>();
+        errors.forEach(vm -> {
+            allErrorMessages.add(vm.getMessage());
+        });
+        assertThat(allErrorMessages,
+                   Matchers.containsInAnyOrder("$.parameters[1].value: string found, integer expected",
+                                               "$.parameters[1].value: does not match the regex pattern ^\\{\\{.+\\}\\}$"));
+    }
+}
diff --git a/src/test/resources/data/issue493-invalid-1.json b/src/test/resources/data/issue493-invalid-1.json
new file mode 100644
index 0000000..3ad7b7d
--- /dev/null
+++ b/src/test/resources/data/issue493-invalid-1.json
@@ -0,0 +1,8 @@
+{
+  "parameters": [
+    {
+      "name": "param-required",
+      "value": "wrongtype"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/test/resources/data/issue493-invalid-2.json b/src/test/resources/data/issue493-invalid-2.json
new file mode 100644
index 0000000..970f4f0
--- /dev/null
+++ b/src/test/resources/data/issue493-invalid-2.json
@@ -0,0 +1,12 @@
+{
+  "parameters": [
+    {
+      "name": "param-required",
+      "value": 123
+    },
+    {
+      "name": "param-optional",
+      "value": "wrongtype"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/test/resources/data/issue493-valid-1.json b/src/test/resources/data/issue493-valid-1.json
new file mode 100644
index 0000000..6f852db
--- /dev/null
+++ b/src/test/resources/data/issue493-valid-1.json
@@ -0,0 +1,12 @@
+{
+  "parameters": [
+    {
+      "name": "param-required",
+      "value": 123
+    },
+    {
+      "name": "param-optional",
+      "value": "{{test}}"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/test/resources/data/issue493-valid-2.json b/src/test/resources/data/issue493-valid-2.json
new file mode 100644
index 0000000..6f852db
--- /dev/null
+++ b/src/test/resources/data/issue493-valid-2.json
@@ -0,0 +1,12 @@
+{
+  "parameters": [
+    {
+      "name": "param-required",
+      "value": 123
+    },
+    {
+      "name": "param-optional",
+      "value": "{{test}}"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/src/test/resources/schema/issue493.json b/src/test/resources/schema/issue493.json
new file mode 100644
index 0000000..d24a0b1
--- /dev/null
+++ b/src/test/resources/schema/issue493.json
@@ -0,0 +1,72 @@
+{
+  "$schema": "https://json-schema.org/draft/2019-09/schema",
+  "type": "object",
+  "additionalProperties": false,
+  "properties": {
+    "parameters": {
+      "type": "array",
+      "uniqueItems": true,
+      "items": [
+        {
+          "type": "object",
+          "required": [
+            "name",
+            "value"
+          ],
+          "properties": {
+            "name": {
+              "const": "param-required"
+            },
+            "value": {
+              "anyOf": [
+                {
+                  "$ref": "#/$defs/test-ref1"
+                },
+                {
+                  "$ref": "#/$defs/test-ref2"
+                }
+              ]
+            }
+          }
+        }
+      ],
+      "additionalItems": {
+        "oneOf": [
+          {
+            "type": "object",
+            "required": [
+              "name",
+              "value"
+            ],
+            "properties": {
+              "name": {
+                "const": "param-optional"
+              },
+              "value": {
+                "anyOf": [
+                  {
+                    "$ref": "#/$defs/test-ref1"
+                  },
+                  {
+                    "$ref": "#/$defs/test-ref2"
+                  }
+                ]
+              }
+            }
+          }
+        ]
+      },
+      "minItems": 1,
+      "maxItems": 2
+    }
+  },
+  "$defs": {
+    "test-ref1": {
+      "type": "string",
+      "pattern": "^\\{\\{.+\\}\\}$"
+    },
+    "test-ref2": {
+      "type": "integer"
+    }
+  }
+}
\ No newline at end of file