diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java index b8f4baf7e..e98006683 100644 --- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java +++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java @@ -48,7 +48,7 @@ public static class Builder { private final Map jsonMetaSchemas = new HashMap(); private final Map uriMap = new HashMap(); private boolean forceHttps = true; - + private boolean removeEmptyFragmentSuffix = true; public Builder() { // Adds support for creating {@link URL}s. @@ -145,6 +145,11 @@ public Builder forceHttps(boolean forceHttps) { 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 JsonSchemaFactory build() { urnFactory, jsonMetaSchemas, uriMap, - forceHttps + forceHttps, + removeEmptyFragmentSuffix ); } } @@ -169,6 +175,7 @@ public JsonSchemaFactory build() { private final Map uriMap; private final ConcurrentMap uriSchemaCache = new ConcurrentHashMap(); private final boolean forceHttps; + private final boolean removeEmptyFragmentSuffix; private JsonSchemaFactory( @@ -179,7 +186,8 @@ private JsonSchemaFactory( final URNFactory urnFactory, final Map jsonMetaSchemas, final Map 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 @@ private JsonSchemaFactory( this.jsonMetaSchemas = jsonMetaSchemas; this.uriMap = uriMap; this.forceHttps = forceHttps; + this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix; } /** @@ -281,7 +290,7 @@ protected ValidationContext createValidationContext(final JsonNode schemaNode) { 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 @@ private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNo 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 190ab4e57..400008aab 100644 --- a/src/main/java/com/networknt/schema/SpecVersionDetector.java +++ b/src/main/java/com/networknt/schema/SpecVersionDetector.java @@ -39,7 +39,10 @@ public static SpecVersion.VersionFlag detect(JsonNode jsonNode) { 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())) 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 000000000..722e95d3b --- /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 7cbcf5edf..601119d84 100644 --- a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java +++ b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java @@ -59,15 +59,53 @@ public void testSchema3() throws IOException { @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 000000000..c0d605cbc --- /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" +}