Repository: networknt/json-schema-validator
Pull request #667 (2023-03-01 10:07:39)

issue-664: Provide/unify schema path for applicator schemas

htdan: > @htdan I merged another PR before this, and there are some conflicts now. Could you resolve the conflicts and add some test cases to prevent somebody from updating this error message format in the future? Thanks.

Sure will do
htdan: @stevehu updated PR, please check

Patch:
diff --git a/src/main/java/com/networknt/schema/AllOfValidator.java b/src/main/java/com/networknt/schema/AllOfValidator.java
index 347149a..8fd6db5 100644
--- a/src/main/java/com/networknt/schema/AllOfValidator.java
+++ b/src/main/java/com/networknt/schema/AllOfValidator.java
@@ -34,7 +34,7 @@ public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
             schemas.add(new JsonSchema(validationContext,
-                            parentSchema.getSchemaPath() + "/" + getValidatorType().getValue() + "/" + i,
+                                       schemaPath + "/" + i,
                                        parentSchema.getCurrentUri(),
                                        schemaNode.get(i),
                                        parentSchema));
diff --git a/src/main/java/com/networknt/schema/AnyOfValidator.java b/src/main/java/com/networknt/schema/AnyOfValidator.java
index c0cf056..c334de4 100644
--- a/src/main/java/com/networknt/schema/AnyOfValidator.java
+++ b/src/main/java/com/networknt/schema/AnyOfValidator.java
@@ -36,7 +36,7 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
             schemas.add(new JsonSchema(validationContext,
-                getChildSchemaPath(i),
+                schemaPath + "/" + i,
                 parentSchema.getCurrentUri(),
                 schemaNode.get(i),
                 parentSchema));
@@ -49,10 +49,6 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
         }
     }
 
-    private String getChildSchemaPath(int childIdx) {
-        return parentSchema.getSchemaPath() + "/" + getValidatorType().getValue() + "/" + childIdx;
-    }
-
     public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
         debug(logger, node, rootNode, at);
 
@@ -79,7 +75,7 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
                 JsonSchema schema = schemas.get(i);
                 state.setMatchedNode(initialHasMatchedNode);
                 Set<ValidationMessage> errors;
-                String typeValidatorName = getChildSchemaPath(i) + "/type";
+                String typeValidatorName = schemas.get(i).getSchemaPath() + "/type";
                 if (schema.getValidators().containsKey(typeValidatorName)) {
                     TypeValidator typeValidator = ((TypeValidator) schema.getValidators().get(typeValidatorName));
                     //If schema has type validator and node type doesn't match with schemaType then ignore it
@@ -174,4 +170,4 @@ public class AnyOfValidator extends BaseJsonValidator implements JsonValidator {
     public void preloadJsonSchema() {
         preloadJsonSchemas(schemas);
     }
-}
+}
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/IfValidator.java b/src/main/java/com/networknt/schema/IfValidator.java
index 00254e7..8b43a33 100644
--- a/src/main/java/com/networknt/schema/IfValidator.java
+++ b/src/main/java/com/networknt/schema/IfValidator.java
@@ -40,12 +40,13 @@ public class IfValidator extends BaseJsonValidator implements JsonValidator {
 
         for (final String keyword : KEYWORDS) {
             final JsonNode node = schemaNode.get(keyword);
+            final String schemaPathOfSchema = parentSchema.schemaPath + "/" + keyword;
             if (keyword.equals("if")) {
-                foundIfSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
+                foundIfSchema = new JsonSchema(validationContext, schemaPathOfSchema, parentSchema.getCurrentUri(), node, parentSchema);
             } else if (keyword.equals("then") && node != null) {
-                foundThenSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
+                foundThenSchema = new JsonSchema(validationContext, schemaPathOfSchema, parentSchema.getCurrentUri(), node, parentSchema);
             } else if (keyword.equals("else") && node != null) {
-                foundElseSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), node, parentSchema);
+                foundElseSchema = new JsonSchema(validationContext, schemaPathOfSchema, parentSchema.getCurrentUri(), node, parentSchema);
             }
         }
 
diff --git a/src/main/java/com/networknt/schema/NotValidator.java b/src/main/java/com/networknt/schema/NotValidator.java
index 1847d85..9b1e3c7 100644
--- a/src/main/java/com/networknt/schema/NotValidator.java
+++ b/src/main/java/com/networknt/schema/NotValidator.java
@@ -29,7 +29,7 @@ public class NotValidator extends BaseJsonValidator implements JsonValidator {
 
     public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
         super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.NOT, validationContext);
-        schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode, parentSchema);
+        schema = new JsonSchema(validationContext, schemaPath, parentSchema.getCurrentUri(), schemaNode, parentSchema);
 
         parseErrorCode(getValidatorType().getErrorCodeKey());
     }
diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java
index 9b9de6b..6f9c6c6 100644
--- a/src/main/java/com/networknt/schema/OneOfValidator.java
+++ b/src/main/java/com/networknt/schema/OneOfValidator.java
@@ -119,7 +119,7 @@ public class OneOfValidator extends BaseJsonValidator implements JsonValidator {
         int size = schemaNode.size();
         for (int i = 0; i < size; i++) {
             JsonNode childNode = schemaNode.get(i);
-            JsonSchema childSchema = new JsonSchema(validationContext,  parentSchema.getSchemaPath() + "/" + getValidatorType().getValue() + "/" + i, parentSchema.getCurrentUri(), childNode, parentSchema);
+            JsonSchema childSchema = new JsonSchema(validationContext,  schemaPath + "/" + i, parentSchema.getCurrentUri(), childNode, parentSchema);
             schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
         }
         parseErrorCode(getValidatorType().getErrorCodeKey());


Test patch:
diff --git a/src/test/java/com/networknt/schema/Issue664Test.java b/src/test/java/com/networknt/schema/Issue664Test.java
new file mode 100644
index 0000000..813fd74
--- /dev/null
+++ b/src/test/java/com/networknt/schema/Issue664Test.java
@@ -0,0 +1,42 @@
+package com.networknt.schema;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+class Issue664Test {
+    protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) {
+        JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
+        return factory.getSchema(schemaContent);
+    }
+
+    protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception {
+        ObjectMapper mapper = new ObjectMapper();
+        return mapper.readTree(content);
+    }
+
+    @Test
+    void shouldHaveFullSchemaPaths() throws Exception {
+        String schemaPath = "/schema/issue664-v7.json";
+        String dataPath = "/data/issue664.json";
+        InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
+        JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
+        InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
+        JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
+        List<String> errorSchemaPaths = schema.validate(node).stream().map(ValidationMessage::getSchemaPath).collect(Collectors.toList());
+
+        List<String> expectedSchemaPaths = Arrays.asList(
+                "#/items/allOf/0/anyOf/0/oneOf/0/not",
+                "#/items/allOf/1/else/properties/postal_code/pattern",
+                "#/items/allOf/1/then/properties/postal_code/pattern");
+        MatcherAssert.assertThat(errorSchemaPaths, Matchers.containsInAnyOrder(expectedSchemaPaths.toArray()));
+    }
+}
diff --git a/src/test/resources/data/issue664.json b/src/test/resources/data/issue664.json
new file mode 100644
index 0000000..386edb3
--- /dev/null
+++ b/src/test/resources/data/issue664.json
@@ -0,0 +1,10 @@
+[
+  {
+    "country": "United Kingdom",
+    "postal_code": "UB87LL"
+  },
+  {
+    "country": "United States of America",
+    "postal_code": "1234"
+  }
+]
\ No newline at end of file
diff --git a/src/test/resources/schema/issue664-v7.json b/src/test/resources/schema/issue664-v7.json
new file mode 100644
index 0000000..a6a2c52
--- /dev/null
+++ b/src/test/resources/schema/issue664-v7.json
@@ -0,0 +1,61 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "type": "array",
+  "items": {
+    "type": "object",
+    "required": [
+      "country",
+      "postal_code"
+    ],
+    "properties": {
+      "country": {
+        "type": "string"
+      },
+      "postal_code": {
+        "type": "string"
+      }
+    },
+    "allOf": [
+      {
+        "anyOf": [
+          {
+            "oneOf": [
+              {
+                "not": {
+                  "properties": {
+                    "country": {
+                      "const": "United Kingdom"
+                    }
+                  }
+                }
+              }
+            ]
+          }
+        ]
+      },
+      {
+        "if": {
+          "properties": {
+            "country": {
+              "const": "United States of America"
+            }
+          }
+        },
+        "then": {
+          "properties": {
+            "postal_code": {
+              "pattern": "[0-9]{5}(-[0-9]{4})?"
+            }
+          }
+        },
+        "else": {
+          "properties": {
+            "postal_code": {
+              "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]"
+            }
+          }
+        }
+      }
+    ]
+  }
+}
\ No newline at end of file