Repository: networknt/json-schema-validator
Pull request #519 (2022-02-24 08:14:00)

Preserve `#` suffix during metaschema URI normalization


Patch:
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index b8f4baf..e980066 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -48,7 +48,7 @@ public class JsonSchemaFactory {
         private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
         private final Map<String, String> uriMap = new HashMap<String, String>();
         private boolean forceHttps = true;
-		
+        private boolean removeEmptyFragmentSuffix = true;
 
         public Builder() {
             // Adds support for creating {@link URL}s.
@@ -145,6 +145,11 @@ public class JsonSchemaFactory {
             return this;
         }
 
+        public Builder removeEmptyFragmentSuffix(boolean removeEmptyFragmentSuffix) {
+            this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
+            return this;
+        }
+
         public JsonSchemaFactory build() {
             // create builtin keywords with (custom) formats.
             return new JsonSchemaFactory(
@@ -155,7 +160,8 @@ public class JsonSchemaFactory {
                     urnFactory,
                     jsonMetaSchemas,
                     uriMap,
-                    forceHttps
+                    forceHttps,
+                    removeEmptyFragmentSuffix
             );
         }
     }
@@ -169,6 +175,7 @@ public class JsonSchemaFactory {
     private final Map<String, String> uriMap;
     private final ConcurrentMap<URI, JsonSchema> uriSchemaCache = new ConcurrentHashMap<URI, JsonSchema>();
     private final boolean forceHttps;
+    private final boolean removeEmptyFragmentSuffix;
 
 
     private JsonSchemaFactory(
@@ -179,7 +186,8 @@ public class JsonSchemaFactory {
             final URNFactory urnFactory,
             final Map<String, JsonMetaSchema> jsonMetaSchemas,
             final Map<String, String> uriMap,
-            final boolean forceHttps) {
+            final boolean forceHttps,
+            final boolean removeEmptyFragmentSuffix) {
         if (mapper == null) {
             throw new IllegalArgumentException("ObjectMapper must not be null");
         } else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
@@ -203,6 +211,7 @@ public class JsonSchemaFactory {
         this.jsonMetaSchemas = jsonMetaSchemas;
         this.uriMap = uriMap;
         this.forceHttps = forceHttps;
+        this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
     }
 
     /**
@@ -281,7 +290,7 @@ public class JsonSchemaFactory {
 
     private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
         final JsonNode uriNode = schemaNode.get("$schema");
-        final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps);
+        final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps, removeEmptyFragmentSuffix);
         final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.get(uri);
         if (jsonMetaSchema == null) {
             throw new JsonSchemaException("Unknown MetaSchema: " + uri);
@@ -409,12 +418,17 @@ public class JsonSchemaFactory {
         return result;
     }
 
-    static protected String normalizeMetaSchemaUri(String u, boolean forceHttps) {
+    static protected String normalizeMetaSchemaUri(String u, boolean forceHttps, boolean removeEmptyFragmentSuffix) {
         try {
             URI uri = new URI(u);
             String scheme = forceHttps ? "https" : uri.getScheme();
             URI newUri = new URI(scheme, uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, null);
-            return newUri.toString();
+
+            if (!removeEmptyFragmentSuffix && u.endsWith("#")) {
+                return newUri + "#";
+            } else {
+                return newUri.toString();
+            }
         } catch (URISyntaxException e) {
             throw new JsonSchemaException("Wrong MetaSchema URI: " + u);
         }
diff --git a/src/main/java/com/networknt/schema/SpecVersionDetector.java b/src/main/java/com/networknt/schema/SpecVersionDetector.java
index 190ab4e..400008a 100644
--- a/src/main/java/com/networknt/schema/SpecVersionDetector.java
+++ b/src/main/java/com/networknt/schema/SpecVersionDetector.java
@@ -39,7 +39,10 @@ public class SpecVersionDetector {
         if (!jsonNode.has(SCHEMA_TAG))
             throw new JsonSchemaException("Schema tag not present");
 
-        String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(jsonNode.get(SCHEMA_TAG).asText(), true);
+        final boolean forceHttps = true;
+        final boolean removeEmptyFragmentSuffix = true;
+
+        String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(jsonNode.get(SCHEMA_TAG).asText(), forceHttps, removeEmptyFragmentSuffix);
         if (schemaUri.equals(JsonMetaSchema.getV4().getUri()))
             return SpecVersion.VersionFlag.V4;
         else if (schemaUri.equals(JsonMetaSchema.getV6().getUri()))


Test patch:
diff --git a/src/test/java/com/networknt/schema/Issue518Test.java b/src/test/java/com/networknt/schema/Issue518Test.java
new file mode 100644
index 0000000..722e95d
--- /dev/null
+++ b/src/test/java/com/networknt/schema/Issue518Test.java
@@ -0,0 +1,30 @@
+package com.networknt.schema;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+
+public class Issue518Test {
+    private static final JsonMetaSchema igluMetaSchema =
+            JsonMetaSchema
+                    .builder("http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#", JsonMetaSchema.getV7())
+                    .build();
+
+    private static final JsonSchemaFactory FACTORY =
+            JsonSchemaFactory
+                    .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
+                    .addMetaSchema(igluMetaSchema)
+                    .forceHttps(false)
+                    .removeEmptyFragmentSuffix(false)
+                    .build();
+
+    @Test
+    public void testPreservingEmptyFragmentSuffix() {
+        String schemaPath = "/schema/issue518-v7.json";
+        InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
+        JsonSchema schema = FACTORY.getSchema(schemaInputStream);
+
+        Assertions.assertNotNull(schema);
+    }
+}
diff --git a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
index 7cbcf5e..601119d 100644
--- a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
@@ -59,15 +59,53 @@ public class UnknownMetaSchemaTest {
 
     @Test
     public void testNormalize() throws JsonSchemaException {
+        final boolean forceHttps = true;
+        final boolean removeEmptyFragmentSuffix = true;
+
         String uri01 = "http://json-schema.org/draft-07/schema";
         String uri02 = "http://json-schema.org/draft-07/schema#";
         String uri03 = "http://json-schema.org/draft-07/schema?key=value";
         String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
         String expected = "https://json-schema.org/draft-07/schema";
-        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, true));
-        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, true));
-        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, true));
-        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, true));
+
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));
+
+    }
+
+    @Test
+    public void testNormalizeForceHttpsDisabled() throws JsonSchemaException {
+        final boolean forceHttps = false;
+        final boolean removeEmptyFragmentSuffix = true;
+
+        String uri01 = "http://json-schema.org/draft-07/schema";
+        String uri02 = "http://json-schema.org/draft-07/schema#";
+        String uri03 = "http://json-schema.org/draft-07/schema?key=value";
+        String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
+        String expected = "http://json-schema.org/draft-07/schema";
+
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));
+
+    }
+
+    @Test
+    public void testNormalizeRemovingEmptyFragmentSuffixDisabled() throws JsonSchemaException {
+        final boolean forceHttps = true;
+        final boolean removeEmptyFragmentSuffix = false;
+
+        String uri01 = "http://json-schema.org/draft-07/schema#";
+        String uri02 = "http://json-schema.org/draft-07/schema?key=value#";
+        String uri03 = "http://json-schema.org/draft-07/schema?key=value&key2=value2#";
+        String expected = "https://json-schema.org/draft-07/schema#";
+
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
+        Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
 
     }
 }
diff --git a/src/test/resources/schema/issue518-v7.json b/src/test/resources/schema/issue518-v7.json
new file mode 100644
index 0000000..c0d605c
--- /dev/null
+++ b/src/test/resources/schema/issue518-v7.json
@@ -0,0 +1,4 @@
+{
+  "$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#",
+  "type": "object"
+}