From 033c5b73e4b4790507f197e9e156dc563895bba2 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Fri, 12 Jan 2024 08:58:30 +0800
Subject: [PATCH 01/65] Fix ref
---
src/main/java/com/networknt/schema/JsonSchema.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index bdf2d4763..4f00147b2 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -81,7 +81,8 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc
.putIfAbsent(this.currentUri != null ? this.currentUri.toString() : this.id, this);
}
if (this.anchor != null) {
- this.validationContext.getSchemaResources().putIfAbsent(this.currentUri.toString() + "#" + anchor, this);
+ this.validationContext.getSchemaResources()
+ .putIfAbsent(this.schemaLocation.getAbsoluteIri().toString() + "#" + anchor, this);
}
getValidators();
}
From a5a4e9fbb031a49b86a164609c66af9cc018b9bd Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 9 Jan 2024 17:34:46 +0800
Subject: [PATCH 02/65] Refactor
---
.../networknt/schema/BaseJsonValidator.java | 9 +-
.../java/com/networknt/schema/JsonSchema.java | 79 +----
.../networknt/schema/JsonSchemaFactory.java | 269 +++++-------------
.../com/networknt/schema/RefValidator.java | 58 +---
.../com/networknt/schema/SchemaLocation.java | 3 +
.../schema/SchemaValidatorsConfig.java | 49 +---
.../networknt/schema/ValidationContext.java | 37 +--
...URIFetcher.java => AbsoluteIriMapper.java} | 54 ++--
.../schema/uri/ClasspathSchemaLoader.java | 48 ++++
.../schema/uri/ClasspathURLFactory.java | 63 ----
.../schema/uri/ClasspathURLFetcher.java | 41 ---
.../schema/uri/ClasspathURLStreamHandler.java | 102 -------
.../schema/uri/DefaultSchemaLoader.java | 55 ++++
.../schema/uri/InputStreamSource.java | 33 +++
.../schema/uri/MapAbsoluteIriMapper.java | 26 ++
.../schema/uri/PrefixAbsoluteIriMapper.java | 25 ++
.../networknt/schema/uri/SchemaLoader.java | 26 ++
.../com/networknt/schema/uri/URIFactory.java | 39 ---
.../schema/uri/URISchemeFactory.java | 102 -------
.../schema/uri/URISchemeFetcher.java | 52 ----
.../networknt/schema/uri/URITranslator.java | 161 -----------
.../com/networknt/schema/uri/URLFactory.java | 64 -----
.../com/networknt/schema/uri/URLFetcher.java | 90 ------
.../networknt/schema/uri/URNURIFactory.java | 30 --
.../networknt/schema/uri/UriSchemaLoader.java | 31 ++
.../com/networknt/schema/walk/WalkEvent.java | 6 +-
.../schema/AbstractJsonSchemaTestSuite.java | 14 +-
.../schema/BaseJsonSchemaValidatorTest.java | 3 +-
.../com/networknt/schema/CustomUriTest.java | 31 +-
.../schema/CyclicDependencyTest.java | 5 +-
.../com/networknt/schema/Issue285Test.java | 16 +-
.../com/networknt/schema/Issue314Test.java | 1 -
.../schema/Issue366FailFastTest.java | 7 +-
.../schema/Issue366FailSlowTest.java | 7 +-
.../com/networknt/schema/Issue428Test.java | 3 +-
.../com/networknt/schema/Issue461Test.java | 5 +-
.../com/networknt/schema/Issue518Test.java | 2 -
.../com/networknt/schema/Issue619Test.java | 14 +-
.../com/networknt/schema/Issue665Test.java | 12 +-
.../com/networknt/schema/Issue824Test.java | 13 +-
.../com/networknt/schema/Issue928Test.java | 12 +-
.../schema/JsonSchemaFactoryUriCacheTest.java | 25 +-
.../schema/OpenAPI30JsonSchemaTest.java | 3 +-
...ursiveReferenceValidatorExceptionTest.java | 3 +-
.../networknt/schema/SharedConfigTest.java | 2 +-
.../com/networknt/schema/UriMappingTest.java | 87 ++----
.../java/com/networknt/schema/UrnTest.java | 26 +-
47 files changed, 477 insertions(+), 1366 deletions(-)
rename src/main/java/com/networknt/schema/uri/{URIFetcher.java => AbsoluteIriMapper.java} (66%)
create mode 100644 src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
delete mode 100644 src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java
delete mode 100644 src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
delete mode 100644 src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
create mode 100644 src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
create mode 100644 src/main/java/com/networknt/schema/uri/InputStreamSource.java
create mode 100644 src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
create mode 100644 src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
create mode 100644 src/main/java/com/networknt/schema/uri/SchemaLoader.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URIFactory.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URISchemeFactory.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URISchemeFetcher.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URITranslator.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URLFactory.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URLFetcher.java
delete mode 100644 src/main/java/com/networknt/schema/uri/URNURIFactory.java
create mode 100644 src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java
index 98596cbcb..724e03d7d 100644
--- a/src/main/java/com/networknt/schema/BaseJsonValidator.java
+++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java
@@ -98,14 +98,9 @@ private static JsonSchema obtainSubSchemaNode(final JsonNode schemaNode, final V
return null;
}
- final URI uri;
- try {
- uri = validationContext.getURIFactory().create(node.textValue());
- } catch (IllegalArgumentException e) {
- return null;
- }
+ final SchemaLocation schemaLocation = SchemaLocation.of(node.textValue());
- return validationContext.getJsonSchemaFactory().getSchema(uri, validationContext.getConfig());
+ return validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
}
protected static boolean equals(double n1, double n2) {
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index 4f00147b2..fa05efcf6 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -44,16 +44,6 @@ public class JsonSchema extends BaseJsonValidator {
private boolean validatorsLoaded = false;
private boolean dynamicAnchor = false;
- /**
- * This is the current uri of this schema. This uri could refer to the uri of this schema's file
- * or it could potentially be a uri that has been altered by an id. An 'id' is able to completely overwrite
- * the current uri or add onto it. This is necessary so that '$ref's are able to be relative to a
- * combination of the current schema file's uri and 'id' uris visible to this schema.
- *
- * This can be null. If it is null, then the creation of relative uris will fail. However, an absolute
- * 'id' would still be able to specify an absolute uri.
- */
- private URI currentUri;
private JsonValidator requiredValidator = null;
private TypeValidator typeValidator;
@@ -62,23 +52,22 @@ public class JsonSchema extends BaseJsonValidator {
private final String id;
private final String anchor;
- static JsonSchema from(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, URI currentUri, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
- return new JsonSchema(validationContext, schemaLocation, evaluationPath, currentUri, schemaNode, parent, suppressSubSchemaRetrieval);
+ static JsonSchema from(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
+ return new JsonSchema(validationContext, schemaLocation, evaluationPath, schemaNode, parent, suppressSubSchemaRetrieval);
}
- private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, URI currentUri,
- JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
- super(schemaLocation, evaluationPath, schemaNode, parent, null, null, validationContext,
- suppressSubSchemaRetrieval);
+ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath,
+ JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
+ super(schemaLocation.resolve(validationContext.resolveSchemaId(schemaNode)), evaluationPath, schemaNode, parent,
+ null, null, validationContext, suppressSubSchemaRetrieval);
this.validationContext = validationContext;
this.metaSchema = validationContext.getMetaSchema();
- this.currentUri = combineCurrentUriWithIds(currentUri, schemaNode);
initializeConfig();
this.id = validationContext.resolveSchemaId(this.schemaNode);
this.anchor = validationContext.getMetaSchema().readAnchor(this.schemaNode);
if (this.id != null) {
this.validationContext.getSchemaResources()
- .putIfAbsent(this.currentUri != null ? this.currentUri.toString() : this.id, this);
+ .putIfAbsent(this.schemaLocation != null ? this.schemaLocation.toString() : this.id, this);
}
if (this.anchor != null) {
this.validationContext.getSchemaResources()
@@ -105,7 +94,6 @@ protected JsonSchema(JsonSchema copy) {
this.metaSchema = copy.metaSchema;
this.validatorsLoaded = copy.validatorsLoaded;
this.dynamicAnchor = copy.dynamicAnchor;
- this.currentUri = copy.currentUri;
this.requiredValidator = copy.requiredValidator;
this.typeValidator = copy.typeValidator;
this.keywordWalkListenerRunner = copy.keywordWalkListenerRunner;
@@ -126,8 +114,7 @@ protected JsonSchema(JsonSchema copy) {
*/
public JsonSchema fromRef(JsonSchema refEvaluationParentSchema, JsonNodePath refEvaluationPath) {
JsonSchema copy = new JsonSchema(this);
- copy.validationContext = new ValidationContext(copy.validationContext.getURIFactory(),
- copy.getValidationContext().getURNFactory(), copy.getValidationContext().getMetaSchema(),
+ copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(),
copy.getValidationContext().getJsonSchemaFactory(),
refEvaluationParentSchema.validationContext.getConfig(),
copy.getValidationContext().getSchemaReferences(), copy.getValidationContext().getSchemaResources());
@@ -145,8 +132,7 @@ public JsonSchema fromRef(JsonSchema refEvaluationParentSchema, JsonNodePath ref
public JsonSchema withConfig(SchemaValidatorsConfig config) {
if (!this.getValidationContext().getConfig().equals(config)) {
JsonSchema copy = new JsonSchema(this);
- copy.validationContext = new ValidationContext(copy.validationContext.getURIFactory(),
- copy.getValidationContext().getURNFactory(), copy.getValidationContext().getMetaSchema(),
+ copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(),
copy.getValidationContext().getJsonSchemaFactory(), config,
copy.getValidationContext().getSchemaReferences(),
copy.getValidationContext().getSchemaResources());
@@ -164,37 +150,6 @@ ValidationContext getValidationContext() {
return this.validationContext;
}
- private URI combineCurrentUriWithIds(URI currentUri, JsonNode schemaNode) {
- final String id = this.validationContext.resolveSchemaId(schemaNode);
- if (id == null) {
- return currentUri;
- } else if (isUriFragmentWithNoContext(currentUri, id)) {
- return null;
- } else {
- try {
- return this.validationContext.getURIFactory().create(currentUri, id);
- } catch (IllegalArgumentException e) {
- SchemaLocation path = schemaLocation.append(this.metaSchema.getIdKeyword());
- ValidationMessage validationMessage = ValidationMessage.builder().code(ValidatorTypeCode.ID.getValue())
- .type(ValidatorTypeCode.ID.getValue()).instanceLocation(path.getFragment())
- .evaluationPath(path.getFragment())
- .arguments(currentUri == null ? "null" : currentUri.toString(), id)
- .messageFormatter(args -> this.validationContext.getConfig().getMessageSource().getMessage(
- ValidatorTypeCode.ID.getValue(), this.validationContext.getConfig().getLocale(), args))
- .build();
- throw new JsonSchemaException(validationMessage);
- }
- }
- }
-
- private static boolean isUriFragmentWithNoContext(URI currentUri, String id) {
- return id.startsWith("#") && (currentUri == null || currentUri.toString().startsWith("#"));
- }
-
- public URI getCurrentUri() {
- return this.currentUri;
- }
-
/**
* Find the schema node for $ref attribute.
*
@@ -274,23 +229,9 @@ public boolean isSchemaResourceRoot() {
return true;
}
// The schema should not cross
- if (getCurrentUri() != null && getParentSchema().getCurrentUri() == null) {
- return true;
- }
- if (getCurrentUri() == null && getParentSchema().getCurrentUri() != null) {
+ if (!getSchemaLocation().getAbsoluteIri().equals(getParentSchema().getSchemaLocation().getAbsoluteIri())) {
return true;
}
- if (getCurrentUri() != null && getParentSchema().getCurrentUri() != null) {
- if (!Objects.equals(getCurrentUri().getScheme(), getParentSchema().getCurrentUri().getScheme())) {
- return true;
- }
- if (!Objects.equals(getCurrentUri().getHost(), getParentSchema().getCurrentUri().getHost())) {
- return true;
- }
- if (!Objects.equals(getCurrentUri().getPath(), getParentSchema().getCurrentUri().getPath())) {
- return true;
- }
- }
return false;
}
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 12453f4ac..67b795c22 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -20,8 +20,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.networknt.schema.uri.*;
-import com.networknt.schema.uri.URITranslator.CompositeURITranslator;
-import com.networknt.schema.urn.URNFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,12 +27,13 @@
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.function.Consumer;
public class JsonSchemaFactory {
private static final Logger logger = LoggerFactory
@@ -45,36 +44,14 @@ public static class Builder {
private ObjectMapper objectMapper = null;
private YAMLMapper yamlMapper = null;
private String defaultMetaSchemaURI;
- private final Map uriFactoryMap = new HashMap();
- private final Map uriFetcherMap = new HashMap();
- private URNFactory urnFactory;
private final ConcurrentMap jsonMetaSchemas = new ConcurrentHashMap();
- private final Map uriMap = new HashMap();
+ private List schemaLoaders = new ArrayList<>();
+ private List absoluteIriMappers = new ArrayList<>();
private boolean enableUriSchemaCache = true;
- private final CompositeURITranslator uriTranslators = new CompositeURITranslator();
public Builder() {
- // Adds support for creating {@link URL}s.
- final URIFactory urlFactory = new URLFactory();
- for (final String scheme : URLFactory.SUPPORTED_SCHEMES) {
- this.uriFactoryMap.put(scheme, urlFactory);
- }
- // Adds support for creating URNs.
- this.uriFactoryMap.put(URNURIFactory.SCHEME, new URNURIFactory());
-
- // Adds support for fetching with {@link URL}s.
- final URIFetcher urlFetcher = new URLFetcher();
- for (final String scheme : URLFetcher.SUPPORTED_SCHEMES) {
- this.uriFetcherMap.put(scheme, urlFetcher);
- }
-
- // Adds support for creating and fetching with classpath {@link URL}s.
- final URIFactory classpathURLFactory = new ClasspathURLFactory();
- final URIFetcher classpathURLFetcher = new ClasspathURLFetcher();
- for (final String scheme : ClasspathURLFactory.SUPPORTED_SCHEMES) {
- this.uriFactoryMap.put(scheme, classpathURLFactory);
- this.uriFetcherMap.put(scheme, classpathURLFetcher);
- }
+ this.schemaLoaders.add(new ClasspathSchemaLoader());
+ this.schemaLoaders.add(new UriSchemaLoader());
}
public Builder objectMapper(final ObjectMapper objectMapper) {
@@ -92,42 +69,6 @@ public Builder defaultMetaSchemaURI(final String defaultMetaSchemaURI) {
return this;
}
- /**
- * Maps a number of schemes to a {@link URIFactory}.
- *
- * @param uriFactory the uri factory that will be used for the given schemes.
- * @param schemes the scheme that the uri factory will be assocaited with.
- * @return this builder.
- */
- public Builder uriFactory(final URIFactory uriFactory, final String... schemes) {
- return uriFactory(uriFactory, Arrays.asList(schemes));
- }
-
- public Builder uriFactory(final URIFactory uriFactory, final Iterable schemes) {
- for (final String scheme : schemes) {
- this.uriFactoryMap.put(scheme, uriFactory);
- }
- return this;
- }
-
- /**
- * Maps a number of schemes to a {@link URIFetcher}.
- *
- * @param uriFetcher the uri fetcher that will be used for the given schemes.
- * @param schemes the scheme that the uri fetcher will be assocaited with.
- * @return this builder.
- */
- public Builder uriFetcher(final URIFetcher uriFetcher, final String... schemes) {
- return uriFetcher(uriFetcher, Arrays.asList(schemes));
- }
-
- public Builder uriFetcher(final URIFetcher uriFetcher, final Iterable schemes) {
- for (final String scheme : schemes) {
- this.uriFetcherMap.put(scheme, uriFetcher);
- }
- return this;
- }
-
public Builder addMetaSchema(final JsonMetaSchema jsonMetaSchema) {
this.jsonMetaSchemas.put(normalizeMetaSchemaUri(jsonMetaSchema.getUri()) , jsonMetaSchema);
return this;
@@ -140,49 +81,18 @@ public Builder addMetaSchemas(final Collection extends JsonMetaSchema> jsonMet
return this;
}
- /**
- * @deprecated Use {@code addUriTranslator} instead.
- * @param map the map of uri mappings.
- * @return this builder.
- */
- @Deprecated
- public Builder addUriMappings(final Map map) {
- this.uriMap.putAll(map);
- return this;
- }
-
- public Builder addUriTranslator(URITranslator translator) {
- if (null != translator) {
- this.uriTranslators.add(translator);
- }
- return this;
- }
-
- public Builder addUrnFactory(URNFactory urnFactory) {
- this.urnFactory = urnFactory;
- return this;
- }
-
- /**
- * @deprecated No longer necessary.
- * @param forceHttps ignored.
- * @return this builder.
- */
- public Builder forceHttps(boolean forceHttps) {
+ public Builder enableUriSchemaCache(boolean enableUriSchemaCache) {
+ this.enableUriSchemaCache = enableUriSchemaCache;
return this;
}
-
- /**
- * @deprecated No longer necessary.
- * @param removeEmptyFragmentSuffix ignored.
- * @return this builder.
- */
- public Builder removeEmptyFragmentSuffix(boolean removeEmptyFragmentSuffix) {
+
+ public Builder schemaLoaders(Consumer> schemaLoaderCustomizer) {
+ schemaLoaderCustomizer.accept(this.schemaLoaders);
return this;
}
-
- public Builder enableUriSchemaCache(boolean enableUriSchemaCache) {
- this.enableUriSchemaCache = enableUriSchemaCache;
+
+ public Builder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
+ absoluteIriCustomizer.accept(this.absoluteIriMappers);
return this;
}
@@ -192,13 +102,10 @@ public JsonSchemaFactory build() {
objectMapper == null ? new ObjectMapper() : objectMapper,
yamlMapper == null ? new YAMLMapper(): yamlMapper,
defaultMetaSchemaURI,
- new URISchemeFactory(uriFactoryMap),
- new URISchemeFetcher(uriFetcherMap),
- urnFactory,
+ schemaLoaders,
+ absoluteIriMappers,
jsonMetaSchemas,
- uriMap,
- enableUriSchemaCache,
- uriTranslators
+ enableUriSchemaCache
);
}
}
@@ -206,13 +113,11 @@ public JsonSchemaFactory build() {
private final ObjectMapper jsonMapper;
private final YAMLMapper yamlMapper;
private final String defaultMetaSchemaURI;
- private final URISchemeFactory uriFactory;
- private final URISchemeFetcher uriFetcher;
- private final CompositeURITranslator uriTranslators;
- private final URNFactory urnFactory;
+ private final List schemaLoaders;
+ private final List absoluteIriMappers;
+ private final SchemaLoader schemaLoader;
private final Map jsonMetaSchemas;
- private final Map uriMap;
- private final ConcurrentMap uriSchemaCache = new ConcurrentHashMap<>();
+ private final ConcurrentMap uriSchemaCache = new ConcurrentHashMap<>();
private final boolean enableUriSchemaCache;
@@ -220,42 +125,35 @@ private JsonSchemaFactory(
final ObjectMapper jsonMapper,
final YAMLMapper yamlMapper,
final String defaultMetaSchemaURI,
- final URISchemeFactory uriFactory,
- final URISchemeFetcher uriFetcher,
- final URNFactory urnFactory,
+ List schemaLoaders,
+ final List absoluteIriMappers,
final Map jsonMetaSchemas,
- final Map uriMap,
- final boolean enableUriSchemaCache,
- final CompositeURITranslator uriTranslators) {
+ final boolean enableUriSchemaCache) {
if (jsonMapper == null) {
throw new IllegalArgumentException("ObjectMapper must not be null");
} else if (yamlMapper == null) {
throw new IllegalArgumentException("YAMLMapper must not be null");
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
throw new IllegalArgumentException("defaultMetaSchemaURI must not be null or empty");
- } else if (uriFactory == null) {
- throw new IllegalArgumentException("URIFactory must not be null");
- } else if (uriFetcher == null) {
- throw new IllegalArgumentException("URIFetcher must not be null");
+ } else if (schemaLoaders == null) {
+ throw new IllegalArgumentException("SchemaLoaders must not be null");
} else if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
} else if (jsonMetaSchemas.get(normalizeMetaSchemaUri(defaultMetaSchemaURI)) == null) {
throw new IllegalArgumentException("Meta Schema for default Meta Schema URI must be provided");
- } else if (uriMap == null) {
- throw new IllegalArgumentException("URL Mappings must not be null");
- } else if (uriTranslators == null) {
- throw new IllegalArgumentException("URI Translators must not be null");
}
this.jsonMapper = jsonMapper;
this.yamlMapper = yamlMapper;
this.defaultMetaSchemaURI = defaultMetaSchemaURI;
- this.uriFactory = uriFactory;
- this.uriFetcher = uriFetcher;
- this.urnFactory = urnFactory;
+ this.schemaLoaders = schemaLoaders;
+ this.absoluteIriMappers = absoluteIriMappers;
+ this.schemaLoader = new DefaultSchemaLoader(schemaLoaders, absoluteIriMappers);
this.jsonMetaSchemas = jsonMetaSchemas;
- this.uriMap = uriMap;
this.enableUriSchemaCache = enableUriSchemaCache;
- this.uriTranslators = uriTranslators;
+ }
+
+ public SchemaLoader getSchemaLoader() {
+ return this.schemaLoader;
}
/**
@@ -309,39 +207,24 @@ public static Builder builder(final JsonSchemaFactory blueprint) {
.addMetaSchemas(blueprint.jsonMetaSchemas.values())
.defaultMetaSchemaURI(blueprint.defaultMetaSchemaURI)
.objectMapper(blueprint.jsonMapper)
- .yamlMapper(blueprint.yamlMapper)
- .addUriMappings(blueprint.uriMap);
-
- for (URITranslator translator: blueprint.uriTranslators) {
- builder = builder.addUriTranslator(translator);
- }
- for (Map.Entry entry : blueprint.uriFactory.getURIFactories().entrySet()) {
- builder = builder.uriFactory(entry.getValue(), entry.getKey());
- }
- for (Map.Entry entry : blueprint.uriFetcher.getURIFetchers().entrySet()) {
- builder = builder.uriFetcher(entry.getValue(), entry.getKey());
- }
+ .yamlMapper(blueprint.yamlMapper);
+ builder.schemaLoaders = blueprint.schemaLoaders;
+ builder.absoluteIriMappers = blueprint.absoluteIriMappers;
return builder;
}
- protected JsonSchema newJsonSchema(final URI schemaUri, final JsonNode schemaNode, final SchemaValidatorsConfig config) {
+ protected JsonSchema newJsonSchema(final SchemaLocation schemaUri, final JsonNode schemaNode, final SchemaValidatorsConfig config) {
final ValidationContext validationContext = createValidationContext(schemaNode, config);
return doCreate(validationContext, getSchemaLocation(schemaUri, schemaNode, validationContext),
- new JsonNodePath(validationContext.getConfig().getPathType()), schemaUri, schemaNode, null, false);
- }
-
- public JsonSchema create(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, URI currentUri, JsonNode schemaNode, JsonSchema parentSchema) {
- return doCreate(validationContext,
- null == schemaLocation ? getSchemaLocation(currentUri, schemaNode, validationContext) : schemaLocation,
- evaluationPath, currentUri, schemaNode, parentSchema, false);
+ new JsonNodePath(validationContext.getConfig().getPathType()), schemaNode, null, false);
}
public JsonSchema create(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema) {
- return create(validationContext, schemaLocation, evaluationPath, parentSchema.getCurrentUri(), schemaNode, parentSchema);
+ return doCreate(validationContext, schemaLocation, evaluationPath, schemaNode, parentSchema, false);
}
- private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, URI currentUri, JsonNode schemaNode, JsonSchema parentSchema, boolean suppressSubSchemaRetrieval) {
- return JsonSchema.from(validationContext, schemaLocation, evaluationPath, currentUri, schemaNode, parentSchema, suppressSubSchemaRetrieval);
+ private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, boolean suppressSubSchemaRetrieval) {
+ return JsonSchema.from(validationContext, schemaLocation, evaluationPath, schemaNode, parentSchema, suppressSubSchemaRetrieval);
}
/**
@@ -352,7 +235,7 @@ private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation
* @param validationContext the validationContext
* @return the schema location
*/
- protected SchemaLocation getSchemaLocation(URI schemaRetrievalUri, JsonNode schemaNode,
+ protected SchemaLocation getSchemaLocation(SchemaLocation schemaRetrievalUri, JsonNode schemaNode,
ValidationContext validationContext) {
String schemaLocation = validationContext.resolveSchemaId(schemaNode);
if (schemaLocation == null && schemaRetrievalUri != null) {
@@ -363,7 +246,7 @@ protected SchemaLocation getSchemaLocation(URI schemaRetrievalUri, JsonNode sche
protected ValidationContext createValidationContext(final JsonNode schemaNode, SchemaValidatorsConfig config) {
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
- return new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, config);
+ return new ValidationContext(jsonMetaSchema, this, config);
}
private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
@@ -384,17 +267,6 @@ private JsonMetaSchema fromId(String id) {
.orElseThrow(() -> new JsonSchemaException("Unknown MetaSchema: " + id));
}
- /**
- * @return A shared {@link URI} factory that is used for creating the URI references in schemas.
- */
- public URIFactory getUriFactory() {
- return this.uriFactory;
- }
-
- public URITranslator getUriTranslator() {
- return this.uriTranslators.with(URITranslator.map(uriMap));
- }
-
public JsonSchema getSchema(final String schema, final SchemaValidatorsConfig config) {
try {
final JsonNode schemaNode = jsonMapper.readTree(schema);
@@ -423,31 +295,23 @@ public JsonSchema getSchema(final InputStream schemaStream) {
return getSchema(schemaStream, null);
}
- public JsonSchema getSchema(final URI schemaUri, final SchemaValidatorsConfig config) {
- final URITranslator uriTranslator = null == config ? getUriTranslator()
- : config.getUriTranslator().with(getUriTranslator());
-
- final URI mappedUri;
- try {
- mappedUri = this.uriFactory.create(uriTranslator.translate(schemaUri).toString());
- } catch (IllegalArgumentException e) {
- logger.error("Failed to create URI.", e);
- throw new JsonSchemaException(e);
- }
-
+ public JsonSchema getSchema(final SchemaLocation schemaUri, final SchemaValidatorsConfig config) {
if (enableUriSchemaCache) {
- JsonSchema cachedUriSchema = uriSchemaCache.computeIfAbsent(mappedUri, key -> {
- return getMappedSchema(schemaUri, config, mappedUri);
+ JsonSchema cachedUriSchema = uriSchemaCache.computeIfAbsent(schemaUri, key -> {
+ return getMappedSchema(schemaUri, config);
});
return cachedUriSchema.withConfig(config);
}
- return getMappedSchema(schemaUri, config, mappedUri);
+ return getMappedSchema(schemaUri, config);
}
- protected JsonSchema getMappedSchema(final URI schemaUri, SchemaValidatorsConfig config, final URI mappedUri) {
- try (InputStream inputStream = this.uriFetcher.fetch(mappedUri)) {
+ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValidatorsConfig config) {
+ try (InputStream inputStream = this.schemaLoader.getSchema(schemaUri).getInputStream()) {
+ if (inputStream == null) {
+ throw new IOException("Cannot load schema uri");
+ }
final JsonNode schemaNode;
- if (isYaml(mappedUri)) {
+ if (isYaml(schemaUri)) {
schemaNode = yamlMapper.readTree(inputStream);
} else {
schemaNode = jsonMapper.readTree(inputStream);
@@ -458,34 +322,33 @@ protected JsonSchema getMappedSchema(final URI schemaUri, SchemaValidatorsConfig
JsonSchema jsonSchema;
SchemaLocation schemaLocation = SchemaLocation.of(schemaUri.toString());
if (idMatchesSourceUri(jsonMetaSchema, schemaNode, schemaUri) || schemaUri.getFragment() == null
- || "".equals(schemaUri.getFragment())) {
- ValidationContext validationContext = new ValidationContext(this.uriFactory, this.urnFactory, jsonMetaSchema, this, config);
- jsonSchema = doCreate(validationContext, schemaLocation, evaluationPath, mappedUri, schemaNode, null, true /* retrieved via id, resolving will not change anything */);
+ || schemaUri.getFragment().getNameCount() == 0) {
+ ValidationContext validationContext = new ValidationContext(jsonMetaSchema, this, config);
+ jsonSchema = doCreate(validationContext, schemaLocation, evaluationPath, schemaNode, null, true /* retrieved via id, resolving will not change anything */);
} else {
// Subschema
final ValidationContext validationContext = createValidationContext(schemaNode, config);
- URI documentUri = "".equals(schemaUri.getSchemeSpecificPart()) ? new URI(schemaUri.getScheme(), schemaUri.getUserInfo(), schemaUri.getHost(), schemaUri.getPort(), schemaUri.getPath(), schemaUri.getQuery(), null) : new URI(schemaUri.getScheme(), schemaUri.getSchemeSpecificPart(), null);
SchemaLocation documentLocation = new SchemaLocation(schemaLocation.getAbsoluteIri());
- JsonSchema document = doCreate(validationContext, documentLocation, evaluationPath, documentUri, schemaNode, null, false);
+ JsonSchema document = doCreate(validationContext, documentLocation, evaluationPath, schemaNode, null, false);
JsonNode subSchemaNode = document.getRefSchemaNode("#" + schemaLocation.getFragment().toString());
if (subSchemaNode != null) {
- jsonSchema = doCreate(validationContext, schemaLocation, evaluationPath, mappedUri, subSchemaNode, document, false);
+ jsonSchema = doCreate(validationContext, schemaLocation, evaluationPath, subSchemaNode, document, false);
} else {
throw new JsonSchemaException("Unable to find subschema");
}
}
return jsonSchema;
- } catch (IOException | URISyntaxException e) {
+ } catch (IOException e) {
logger.error("Failed to load json schema from {}", schemaUri, e);
throw new JsonSchemaException(e);
}
}
- public JsonSchema getSchema(final URI schemaUri) {
+ public JsonSchema getSchema(final SchemaLocation schemaUri) {
return getSchema(schemaUri, new SchemaValidatorsConfig());
}
- public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
+ public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
return newJsonSchema(schemaUri, jsonNode, config);
}
@@ -494,7 +357,7 @@ public JsonSchema getSchema(final JsonNode jsonNode, final SchemaValidatorsConfi
return newJsonSchema(null, jsonNode, config);
}
- public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode) {
+ public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode) {
return newJsonSchema(schemaUri, jsonNode, null);
}
@@ -502,7 +365,7 @@ public JsonSchema getSchema(final JsonNode jsonNode) {
return newJsonSchema(null, jsonNode, null);
}
- private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNode schema, final URI schemaUri) {
+ private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNode schema, final SchemaLocation schemaUri) {
String id = metaSchema.readId(schema);
if (id == null || id.isEmpty()) {
return false;
@@ -512,8 +375,8 @@ private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNo
return result;
}
- private boolean isYaml(final URI schemaUri) {
- final String schemeSpecificPart = schemaUri.getSchemeSpecificPart();
+ private boolean isYaml(final SchemaLocation schemaUri) {
+ final String schemeSpecificPart = schemaUri.toString();
final int idx = schemeSpecificPart.lastIndexOf('.');
if (idx == -1) {
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 1e5c74ee5..7ee733511 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -18,12 +18,9 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.CollectorContext.Scope;
-import com.networknt.schema.uri.URIFactory;
-import com.networknt.schema.urn.URNFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
@@ -58,25 +55,13 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
// This will determine the correct absolute uri for the refUri. This decision will take into
// account the current uri of the parent schema.
- URI schemaUri = determineSchemaUri(validationContext.getURIFactory(), parentSchema, refUri);
- if (schemaUri == null) {
- // the URNFactory is optional
- if (validationContext.getURNFactory() == null) {
- return null;
- }
- // If the uri dose't determinate try to determinate with urn factory
- schemaUri = determineSchemaUrn(validationContext.getURNFactory(), refUri);
- if (schemaUri == null) {
- return null;
- }
- }
-
- URI schemaUriFinal = schemaUri;
+ SchemaLocation schemaLocation = parentSchema.getSchemaLocation().resolve(refUri);
+ String schemaUriFinal = schemaLocation.toString();
// This should retrieve schemas regardless of the protocol that is in the uri.
return new JsonSchemaRef(new CachedSupplier<>(() -> {
JsonSchema schemaResource = validationContext.getSchemaResources().get(schemaUriFinal.toString());
if (schemaResource == null) {
- schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaUriFinal, validationContext.getConfig());
+ schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
if (schemaResource != null) {
if (!schemaResource.getValidationContext().getSchemaResources().isEmpty()) {
validationContext.getSchemaResources()
@@ -110,7 +95,7 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
if (parentSchema.getId() != null && parentSchema.parentSchema != null) {
base = parentSchema.parentSchema;
}
- if (base.getCurrentUri() != null) {
+ if (base.getSchemaLocation() != null) {
String absoluteIri = SchemaLocation.resolve(base.getSchemaLocation(), refValue);
// Schema resource needs to update the parent and evaluation path
return new JsonSchemaRef(new CachedSupplier<>(() -> {
@@ -154,7 +139,7 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
if (node != null) {
SchemaLocation path = null;
JsonSchema currentParent = parent;
- URI currentUri = parent.getCurrentUri();
+ SchemaLocation currentUri = parent.getSchemaLocation();
if (refValue.startsWith(REF_CURRENT)) {
// relative to document
path = new SchemaLocation(parent.schemaLocation.getAbsoluteIri(),
@@ -168,12 +153,12 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
if (id != null) {
if (id.contains(":")) {
// absolute
- currentUri = URI.create(id);
+ currentUri = currentUri.resolve(id);
path = SchemaLocation.of(id);
} else {
// relative
String absoluteUri = path.getAbsoluteIri().resolve(id).toString();
- currentUri = URI.create(absoluteUri);
+ currentUri = currentUri.resolve(absoluteUri);
path = SchemaLocation.of(absoluteUri);
}
}
@@ -194,38 +179,11 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
path = path.append(parts[x]);
}
}
- return validationContext.newSchema(path, evaluationPath, currentUri, node, currentParent);
+ return validationContext.newSchema(path, evaluationPath, node, currentParent);
}
throw null;
}
- private static URI determineSchemaUri(final URIFactory uriFactory, final JsonSchema parentSchema, final String refUri) {
- URI schemaUri;
- // $ref prevents a sibling $id from changing the base uri
- JsonSchema parent = parentSchema.getParentSchema(); // just the parentSchema is the sibling $id with this $ref
- final URI currentUri = parent != null ? parent.getCurrentUri() : parentSchema.getCurrentUri();
- try {
- if (currentUri == null) {
- schemaUri = uriFactory.create(refUri);
- } else {
- schemaUri = uriFactory.create(currentUri, refUri);
- }
- } catch (IllegalArgumentException e) {
- schemaUri = null;
- }
- return schemaUri;
- }
-
- private static URI determineSchemaUrn(final URNFactory urnFactory, final String refUri) {
- URI schemaUrn;
- try {
- schemaUrn = urnFactory.create(refUri);
- } catch (IllegalArgumentException e) {
- schemaUrn = null;
- }
- return schemaUrn;
- }
-
@Override
public Set validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
CollectorContext collectorContext = executionContext.getCollectorContext();
diff --git a/src/main/java/com/networknt/schema/SchemaLocation.java b/src/main/java/com/networknt/schema/SchemaLocation.java
index c0ecda4d3..8daf9c6b1 100644
--- a/src/main/java/com/networknt/schema/SchemaLocation.java
+++ b/src/main/java/com/networknt/schema/SchemaLocation.java
@@ -130,6 +130,9 @@ public static SchemaLocation of(String iri) {
* @return the resolved schema location
*/
public SchemaLocation resolve(String absoluteIriReferenceOrFragment) {
+ if (absoluteIriReferenceOrFragment == null) {
+ return this;
+ }
if ("#".equals(absoluteIriReferenceOrFragment)) {
return new SchemaLocation(this.getAbsoluteIri(), JSON_POINTER);
}
diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
index 3f90acaa4..9c7655542 100644
--- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
+++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
@@ -19,8 +19,11 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.i18n.DefaultMessageSource;
import com.networknt.schema.i18n.MessageSource;
-import com.networknt.schema.uri.URITranslator;
-import com.networknt.schema.uri.URITranslator.CompositeURITranslator;
+import com.networknt.schema.uri.AbsoluteIriMapper;
+import com.networknt.schema.uri.ClasspathSchemaLoader;
+import com.networknt.schema.uri.DefaultSchemaLoader;
+import com.networknt.schema.uri.SchemaLoader;
+import com.networknt.schema.uri.UriSchemaLoader;
import com.networknt.schema.walk.JsonSchemaWalkListener;
import java.util.ArrayList;
@@ -89,16 +92,6 @@ public class SchemaValidatorsConfig {
*/
private final Map strictness = new HashMap<>(0);
- /**
- * Map of public, normally internet accessible schema URLs to alternate
- * locations; this allows for offline validation of schemas that refer to public
- * URLs. This is merged with any mappings the {@link JsonSchemaFactory} may have
- * been built with.
- */
- private Map uriMappings = new HashMap<>();
-
- private CompositeURITranslator uriTranslators = new CompositeURITranslator();
-
/**
* When a field is set as nullable in the OpenAPI specification, the schema
* validator validates that it is nullable however continues with validation
@@ -154,7 +147,7 @@ public class SchemaValidatorsConfig {
// These are costly in terms of performance so we provide a way to disable them.
private boolean disableUnevaluatedItems = false;
private boolean disableUnevaluatedProperties = false;
-
+
public SchemaValidatorsConfig disableUnevaluatedAnalysis() {
disableUnevaluatedItems();
disableUnevaluatedProperties();
@@ -242,36 +235,6 @@ public ApplyDefaultsStrategy getApplyDefaultsStrategy() {
return this.applyDefaultsStrategy;
}
- public CompositeURITranslator getUriTranslator() {
- return this.uriTranslators
- .with(URITranslator.map(this.uriMappings));
- }
-
- public void addUriTranslator(URITranslator uriTranslator) {
- if (null != uriTranslator) {
- this.uriTranslators.add(uriTranslator);
- }
- }
-
- /**
- * @deprecated Use {@code getUriTranslator()} instead
- * @return Map of public, normally internet accessible schema URLs
- */
- @Deprecated
- public Map getUriMappings() {
- // return a copy of the mappings
- return new HashMap<>(this.uriMappings);
- }
-
- /**
- * @deprecated Use {@code addUriTranslator()} instead
- * @param uriMappings Map of public, normally internet accessible schema URLs
- */
- @Deprecated
- public void setUriMappings(Map uriMappings) {
- this.uriMappings = uriMappings;
- }
-
public boolean isHandleNullableField() {
return this.handleNullableField;
}
diff --git a/src/main/java/com/networknt/schema/ValidationContext.java b/src/main/java/com/networknt/schema/ValidationContext.java
index 1e465b05f..31284fcb6 100644
--- a/src/main/java/com/networknt/schema/ValidationContext.java
+++ b/src/main/java/com/networknt/schema/ValidationContext.java
@@ -16,45 +16,34 @@
package com.networknt.schema;
-import java.net.URI;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.SpecVersion.VersionFlag;
-import com.networknt.schema.uri.URIFactory;
-import com.networknt.schema.urn.URNFactory;
public class ValidationContext {
- private final URIFactory uriFactory;
- private final URNFactory urnFactory;
private final JsonMetaSchema metaSchema;
private final JsonSchemaFactory jsonSchemaFactory;
private final SchemaValidatorsConfig config;
private final ConcurrentMap schemaReferences;
private final ConcurrentMap schemaResources;
- public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaSchema metaSchema,
- JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config) {
- this(uriFactory, urnFactory, metaSchema, jsonSchemaFactory, config, new ConcurrentHashMap<>(),
- new ConcurrentHashMap<>());
+ public ValidationContext(JsonMetaSchema metaSchema,
+ JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config) {
+ this(metaSchema, jsonSchemaFactory, config, new ConcurrentHashMap<>(), new ConcurrentHashMap<>());
}
-
- public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaSchema metaSchema,
- JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config,
- ConcurrentMap schemaReferences, ConcurrentMap schemaResources) {
- if (uriFactory == null) {
- throw new IllegalArgumentException("URIFactory must not be null");
- }
+
+ public ValidationContext(JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchemaFactory,
+ SchemaValidatorsConfig config, ConcurrentMap schemaReferences,
+ ConcurrentMap schemaResources) {
if (metaSchema == null) {
throw new IllegalArgumentException("JsonMetaSchema must not be null");
}
if (jsonSchemaFactory == null) {
throw new IllegalArgumentException("JsonSchemaFactory must not be null");
}
- this.uriFactory = uriFactory;
- this.urnFactory = urnFactory;
this.metaSchema = metaSchema;
this.jsonSchemaFactory = jsonSchemaFactory;
this.config = config == null ? new SchemaValidatorsConfig() : config;
@@ -66,10 +55,6 @@ public JsonSchema newSchema(SchemaLocation schemaLocation, JsonNodePath evaluati
return getJsonSchemaFactory().create(this, schemaLocation, evaluationPath, schemaNode, parentSchema);
}
- public JsonSchema newSchema(SchemaLocation schemaLocation, JsonNodePath evaluationPath, URI currentUri, JsonNode schemaNode, JsonSchema parentSchema) {
- return getJsonSchemaFactory().create(this, schemaLocation, evaluationPath, currentUri, schemaNode, parentSchema);
- }
-
public JsonValidator newValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath,
String keyword /* keyword */, JsonNode schemaNode, JsonSchema parentSchema) {
return this.metaSchema.newValidator(this, schemaLocation, evaluationPath, keyword, schemaNode, parentSchema);
@@ -79,14 +64,6 @@ public String resolveSchemaId(JsonNode schemaNode) {
return this.metaSchema.readId(schemaNode);
}
- public URIFactory getURIFactory() {
- return this.uriFactory;
- }
-
- public URNFactory getURNFactory() {
- return this.urnFactory;
- }
-
public JsonSchemaFactory getJsonSchemaFactory() {
return this.jsonSchemaFactory;
}
diff --git a/src/main/java/com/networknt/schema/uri/URIFetcher.java b/src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java
similarity index 66%
rename from src/main/java/com/networknt/schema/uri/URIFetcher.java
rename to src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java
index 942117cd3..bed7d0dd1 100644
--- a/src/main/java/com/networknt/schema/uri/URIFetcher.java
+++ b/src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java
@@ -1,28 +1,26 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-
-/**
- * The URIFetcher interface defines how file streams are able to be fetched given a {@link URI}.
- */
-public interface URIFetcher {
- InputStream fetch(URI uri) throws IOException;
-}
+/*
+ * 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.uri;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Maps absolute IRI.
+ */
+@FunctionalInterface
+public interface AbsoluteIriMapper {
+ AbsoluteIri map(AbsoluteIri absoluteIRI);
+}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
new file mode 100644
index 000000000..c1f49cb3b
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
@@ -0,0 +1,48 @@
+/*
+ * 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.uri;
+
+import java.io.InputStream;
+
+import com.networknt.schema.SchemaLocation;
+
+/**
+ * Loads from classpath.
+ */
+public class ClasspathSchemaLoader implements SchemaLoader {
+
+ @Override
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ String scheme = schemaLocation.getAbsoluteIri().getScheme();
+ if (scheme.startsWith("classpath") || scheme.startsWith("resource")) {
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = SchemaLoader.class.getClassLoader();
+ }
+ ClassLoader loader = classLoader;
+ String name = schemaLocation.getAbsoluteIri().toString().substring(scheme.length() + 1);
+ return () -> {
+ InputStream result = loader.getResourceAsStream(name);
+ if (result == null) {
+ result = loader.getResourceAsStream(name.substring(1));
+ }
+ return result;
+ };
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java b/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java
deleted file mode 100644
index 47e2fc862..000000000
--- a/src/main/java/com/networknt/schema/uri/ClasspathURLFactory.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.net.*;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A URIFactory that uses URL for creating {@link URI}s.
- */
-public final class ClasspathURLFactory implements URIFactory {
- static final URLStreamHandler STREAM_HANDLER = new ClasspathURLStreamHandler();
-
- public static final Set SUPPORTED_SCHEMES = Collections.unmodifiableSet(
- ClasspathURLStreamHandler.SUPPORTED_SCHEMES);
-
- public static URL convert(final URI uri) throws MalformedURLException {
- return new URL(null, uri.toString(), STREAM_HANDLER);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public URI create(final String uri) {
- try {
- return new URL(null, uri, STREAM_HANDLER).toURI();
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public URI create(final URI baseURI, final String segment) {
- try {
- return new URL(convert(baseURI), segment, STREAM_HANDLER).toURI();
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- }
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java b/src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
deleted file mode 100644
index b93fe95e9..000000000
--- a/src/main/java/com/networknt/schema/uri/ClasspathURLFetcher.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A URIfetcher that uses {@link URL#openStream()} for fetching and assumes given {@link URI}s
- * are actualy {@link URL}s.
- */
-public final class ClasspathURLFetcher implements URIFetcher {
- // This fetcher handles the {@link URL}s created with the {@link ClasspathURIFactory}.
- public static final Set SUPPORTED_SCHEMES = Collections.unmodifiableSet(ClasspathURLFactory.SUPPORTED_SCHEMES);
-
- /**
- * {@inheritDoc}
- */
- @Override
- public InputStream fetch(final URI uri) throws IOException {
- return ClasspathURLFactory.convert(uri).openStream();
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java b/src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
deleted file mode 100644
index dbe64975f..000000000
--- a/src/main/java/com/networknt/schema/uri/ClasspathURLStreamHandler.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * An {@link URLStreamHandler} capable of loading resources from the classpath.
- *
- * @author Kenneth Waldenstrom
- */
-class ClasspathURLStreamHandler extends URLStreamHandler {
- public static final Set SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet(
- Arrays.asList("classpath", "resource")));
-
- @Override
- protected URLConnection openConnection(final URL pURL) throws IOException {
- return new ClassPathURLConnection(pURL);
- }
-
- class ClassPathURLConnection extends URLConnection {
-
- private Class> mHost = null;
-
- protected ClassPathURLConnection(URL pURL) {
- super(pURL);
- }
-
- @Override
- public final void connect() throws IOException {
- String className = url.getHost();
- try {
- if (className != null && className.length() > 0) {
- mHost = Class.forName(className);
- }
- connected = true;
- } catch (ClassNotFoundException e) {
- throw new IOException("Class not found: " + e.toString());
- }
- }
-
- @Override
- public final InputStream getInputStream() throws IOException {
- if (!connected) {
- connect();
- }
- return getResourceAsStream(url);
- }
-
- private InputStream getResourceAsStream(URL pURL) throws IOException {
- String path = pURL.getPath();
-
- if (path.startsWith("/")) {
- path = path.substring(1);
- }
-
- InputStream stream = null;
- if (mHost != null) {
- stream = mHost.getClassLoader().getResourceAsStream(path);
- } else {
- ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
- if (contextClassLoader != null) {
- stream = contextClassLoader.getResourceAsStream(path);
- }
- if (stream == null) {
- stream = getClass().getClassLoader().getResourceAsStream(path);
- }
- if (stream == null) {
- stream = ClassLoader.getSystemResourceAsStream(path);
- }
- }
- if (stream == null) {
- throw new IOException("Resource " + path + " not found in classpath.");
- }
- return stream;
- }
- }
-
-
-}
diff --git a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
new file mode 100644
index 000000000..408fec9e3
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
@@ -0,0 +1,55 @@
+/*
+ * 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.uri;
+
+import java.util.List;
+
+import com.networknt.schema.AbsoluteIri;
+import com.networknt.schema.SchemaLocation;
+
+/**
+ * Default {@link SchemaLoader}.
+ */
+public class DefaultSchemaLoader implements SchemaLoader {
+ private final List schemaLoaders;
+ private final List absoluteIriMappers;
+
+ public DefaultSchemaLoader(List schemaLoaders, List absoluteIriMappers) {
+ this.schemaLoaders = schemaLoaders;
+ this.absoluteIriMappers = absoluteIriMappers;
+ }
+
+ @Override
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ AbsoluteIri absoluteIri = schemaLocation.getAbsoluteIri();
+ SchemaLocation mappedSchemaLocation = schemaLocation;
+ for (AbsoluteIriMapper mapper : absoluteIriMappers) {
+ AbsoluteIri mapped = mapper.map(absoluteIri);
+ if (mapped != null) {
+ mappedSchemaLocation = new SchemaLocation(mapped, schemaLocation.getFragment());
+ break;
+ }
+ }
+ for (SchemaLoader loader : schemaLoaders) {
+ InputStreamSource result = loader.getSchema(mappedSchemaLocation);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/networknt/schema/uri/InputStreamSource.java b/src/main/java/com/networknt/schema/uri/InputStreamSource.java
new file mode 100644
index 000000000..149cb8235
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/InputStreamSource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2023 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.uri;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * InputStream source.
+ */
+@FunctionalInterface
+public interface InputStreamSource {
+ /**
+ * Opens a new inputstream to the resource.
+ *
+ * @return a new inputstream
+ * @throws IOException
+ */
+ InputStream getInputStream() throws IOException;
+}
diff --git a/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
new file mode 100644
index 000000000..3eaba98f5
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
@@ -0,0 +1,26 @@
+package com.networknt.schema.uri;
+
+import java.util.Map;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Map implementation of {@link AbsoluteIriMapper}.
+ */
+public class MapAbsoluteIriMapper implements AbsoluteIriMapper {
+ private final Map mappings;
+
+ public MapAbsoluteIriMapper(Map mappings) {
+ this.mappings = mappings;
+ }
+
+ @Override
+ public AbsoluteIri map(AbsoluteIri absoluteIRI) {
+ String mapped = this.mappings.get(absoluteIRI.toString());
+ if (mapped != null) {
+ return AbsoluteIri.of(mapped);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
new file mode 100644
index 000000000..e5cdc1708
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
@@ -0,0 +1,25 @@
+package com.networknt.schema.uri;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Prefix implementation of {@link AbsoluteIriMapper}.
+ */
+public class PrefixAbsoluteIriMapper implements AbsoluteIriMapper {
+ private final String source;
+ private final String replacement;
+
+ public PrefixAbsoluteIriMapper(String source, String replacement) {
+ this.source = source;
+ this.replacement = replacement;
+ }
+
+ @Override
+ public AbsoluteIri map(AbsoluteIri absoluteIRI) {
+ String absoluteIRIString = absoluteIRI != null ? absoluteIRI.toString() : null;
+ if (absoluteIRIString != null && absoluteIRIString.startsWith(source)) {
+ return AbsoluteIri.of(replacement + absoluteIRIString.substring(source.length()));
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoader.java b/src/main/java/com/networknt/schema/uri/SchemaLoader.java
new file mode 100644
index 000000000..f9e27d5a5
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoader.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2023 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.uri;
+
+import com.networknt.schema.SchemaLocation;
+
+/**
+ * Loader for schema.
+ */
+@FunctionalInterface
+public interface SchemaLoader {
+ InputStreamSource getSchema(SchemaLocation schemaLocation);
+}
diff --git a/src/main/java/com/networknt/schema/uri/URIFactory.java b/src/main/java/com/networknt/schema/uri/URIFactory.java
deleted file mode 100644
index ff5e35bcd..000000000
--- a/src/main/java/com/networknt/schema/uri/URIFactory.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.net.URI;
-
-/**
- * The URIFactory interface defines how {@link URI}s are able to be combined and created.
- */
-public interface URIFactory {
- /**
- * @param uri Some uri string.
- * @return The converted {@link URI}.
- * @throws IllegalArgumentException if there was a problem creating the {@link URI} with the given data.
- */
- URI create(String uri);
-
- /**
- * @param baseURI The base {@link URI}.
- * @param segment The segment to add to the base {@link URI}.
- * @return The combined {@link URI}.
- * @throws IllegalArgumentException if there was a problem creating the {@link URI} with the given data.
- */
- URI create(URI baseURI, String segment);
-}
diff --git a/src/main/java/com/networknt/schema/uri/URISchemeFactory.java b/src/main/java/com/networknt/schema/uri/URISchemeFactory.java
deleted file mode 100644
index 3e7d5d3db..000000000
--- a/src/main/java/com/networknt/schema/uri/URISchemeFactory.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.net.URI;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * The URISchemaFactory will proxy to other {@link URIFactory}s based on the scheme being used.
- */
-public class URISchemeFactory implements URIFactory {
- private static final Pattern URI_SCHEME_PATTERN = Pattern.compile("^([a-z][a-z0-9+\\.\\-\\\\]*):");
-
- private final Map uriFactories;
-
- public URISchemeFactory(final Map uriFactories) {
- if (uriFactories == null) {
- throw new IllegalArgumentException("URIFactory map must not be null");
- }
- this.uriFactories = uriFactories;
- }
-
- public Map getURIFactories() {
- return this.uriFactories;
- }
-
- private static String getScheme(final String uri) {
- final Matcher matcher = URI_SCHEME_PATTERN.matcher(uri);
- if (matcher.find()) {
- return matcher.group(1);
- }
- return null;
- }
-
- private URIFactory getFactory(final String scheme) {
- final URIFactory uriFactory = this.uriFactories.get(scheme);
- if (uriFactory == null) {
- throw new IllegalArgumentException(String.format("Unsupported URI scheme encountered: %s", scheme));
- }
- return uriFactory;
- }
-
- /**
- * @param uri String
- * @return URI
- */
- @Override
- public URI create(final String uri) {
- final String scheme = getScheme(uri);
- if (scheme == null) {
- throw new IllegalArgumentException(String.format("Couldn't find URI scheme: %s", uri));
- }
-
- final URIFactory uriFactory = this.getFactory(scheme);
- return uriFactory.create(uri);
- }
-
- /**
- * @param baseURI base URI
- * @param segment URI segment
- * @return URI
- */
- @Override
- public URI create(final URI baseURI, final String segment) {
- if (baseURI == null) {
- return this.create(segment);
- }
-
- // We first attempt to get the scheme in case the segment is an absolute URI path.
- String scheme = getScheme(segment);
- if (scheme == null) {
- // In this case, the segment is relative to the baseURI.
- scheme = baseURI.getScheme();
- final URIFactory uriFactory = this.getFactory(scheme);
- return uriFactory.create(baseURI, segment);
- }
-
- if ("urn".equals(scheme)) {
- return URI.create(segment);
- }
-
- // In this case, the segment is an absolute URI path.
- final URIFactory uriFactory = this.getFactory(scheme);
- return uriFactory.create(segment);
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java b/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java
deleted file mode 100644
index 9e313d7d0..000000000
--- a/src/main/java/com/networknt/schema/uri/URISchemeFetcher.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.util.Map;
-
-/**
- * The URISchemeFetcher will proxy to other {@link URIFetcher}s based on the scheme being used.
- */
-public class URISchemeFetcher implements URIFetcher {
- private final Map uriFetchers;
-
- public URISchemeFetcher(final Map uriFetchers) {
- if (uriFetchers == null) {
- throw new IllegalArgumentException("URIFetcher map must not be null");
- }
- this.uriFetchers = uriFetchers;
- }
-
- public Map getURIFetchers() {
- return this.uriFetchers;
- }
-
- /**
- * @param uri URI
- * @return InputStream
- */
- public InputStream fetch(final URI uri) throws IOException {
- final URIFetcher uriFetcher = this.uriFetchers.get(uri.getScheme());
- if (uriFetcher == null) {
- throw new IllegalArgumentException(String.format("Unsupported URI scheme encountered: %s", uri.getScheme()));
- }
- return uriFetcher.fetch(uri);
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/URITranslator.java b/src/main/java/com/networknt/schema/uri/URITranslator.java
deleted file mode 100644
index aea812010..000000000
--- a/src/main/java/com/networknt/schema/uri/URITranslator.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package com.networknt.schema.uri;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-@FunctionalInterface
-public interface URITranslator {
- static final URITranslator NOOP = original -> original;
-
- /**
- * Translates one URI into another.
- * @param original the URI to translate
- * @return the translated URI or the original URI if it did not match
- * the conditions triggering translation
- */
- URI translate(URI original);
-
- /**
- * Creates a simple mapping from one URI to another.
- * @param source the URI to match
- * @param target the URI to return when matched
- * @return a new URITranslator
- */
- static URITranslator map(String source, String target) {
- return map(URI.create(source), URI.create(target));
- }
-
- /**
- * Creates a simple mapping from one URI to another.
- * @param source the URI to match
- * @param target the URI to return when matched
- * @return a new URITranslator
- */
- static URITranslator map(URI source, URI target) {
- return original -> Objects.equals(source, original) ? target : original;
- }
-
- /**
- * Creates a map-based mapping from one URI to another.
- * @param uriMappings the mappings to build
- * @return a new URITranslator
- */
- static URITranslator map(Map uriMappings) {
- return new MappingURITranslator(uriMappings);
- }
-
- /**
- * Creates a CompositeURITranslator.
- * @param uriTranslators the translators to combine
- * @return a new CompositeURITranslator
- */
- static CompositeURITranslator combine(URITranslator... uriTranslators) {
- return new CompositeURITranslator(uriTranslators);
- }
-
- /**
- * Creates a mapping from one URI to another by replacing the beginning of the URI.
- *
- * For example, replace http with https.
- *
- * @param source the search string
- * @param target the replacement string
- * @return a new URITranslator
- */
- static URITranslator prefix(String source, String target) {
- return new PrefixReplacer(source, target);
- }
-
- /**
- * Creates a CompositeURITranslator.
- * @param uriTranslators the translators to combine
- * @return a new CompositeURITranslator
- */
- static CompositeURITranslator combine(Collection extends URITranslator> uriTranslators) {
- return new CompositeURITranslator(uriTranslators);
- }
-
- class CompositeURITranslator extends ArrayList implements URITranslator {
- private static final long serialVersionUID = 1L;
-
- public CompositeURITranslator() {
- super();
- }
-
- public CompositeURITranslator(URITranslator...translators) {
- this(Arrays.asList(translators));
- }
-
- public CompositeURITranslator(Collection extends URITranslator> c) {
- super(c);
- }
-
- @Override
- public URI translate(URI original) {
- URI result = original;
- for (URITranslator translator: this) {
- result = translator.translate(result);
- }
- return result;
- }
-
- public CompositeURITranslator with(URITranslator translator) {
- if (null != translator) {
- return new CompositeURITranslator(this, translator);
- }
- return this;
- }
- }
-
- /**
- * Provides support for legacy map-based translations
- */
- class MappingURITranslator implements URITranslator {
- private final Map mappings;
-
- public MappingURITranslator(Map uriMappings) {
- this.mappings = new HashMap<>();
- if (null != uriMappings) {
- uriMappings.forEach((k, v) -> this.mappings.put(URI.create(k), URI.create(v)));
- }
- }
-
- @Override
- public URI translate(URI original) {
- return this.mappings.getOrDefault(original, original);
- }
-
- }
-
- /**
- * Replaces the beginning of a URI
- */
- class PrefixReplacer implements URITranslator {
- private final String src;
- private final String tgt;
-
- public PrefixReplacer(String src, String tgt) {
- this.src = src;
- this.tgt = tgt;
- }
-
- @Override
- public URI translate(URI original) {
- if (null != original) {
- String o = original.toString();
- if (o.startsWith(src)) {
- o = tgt + o.substring(src.length());
- return URI.create(o);
- }
- }
-
- return original;
- }
-
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/URLFactory.java b/src/main/java/com/networknt/schema/uri/URLFactory.java
deleted file mode 100644
index 61fa56d1a..000000000
--- a/src/main/java/com/networknt/schema/uri/URLFactory.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A URIFactory that uses URL for creating {@link URI}s.
- */
-public final class URLFactory implements URIFactory {
- // These supported schemes are defined in {@link #URL(String, String, int, String)}.
- public static final Set SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<>(
- Arrays.asList("http", "https", "ftp", "file", "jar")));
-
- /**
- * @param uri String
- * @return URI
- */
- @Override
- public URI create(final String uri) {
- try {
- return URI.create(uri);
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- }
- }
-
- /**
- * @param baseURI URI
- * @param segment String
- * @return URI
- */
- @Override
- public URI create(final URI baseURI, final String segment) {
- try {
- return new URL(baseURI.toURL(), segment).toURI();
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- } catch (URISyntaxException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- }
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/URLFetcher.java b/src/main/java/com/networknt/schema/uri/URLFetcher.java
deleted file mode 100644
index d5bb8d944..000000000
--- a/src/main/java/com/networknt/schema/uri/URLFetcher.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2016 Network New Technologies Inc.
- *
- * 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.uri;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * A URIfetcher that uses {@link URL#openStream()} for fetching and assumes given {@link URI}s are actualy {@link URL}s.
- */
-public final class URLFetcher implements URIFetcher {
-
- // These supported schemes are defined in {@link #URL(String, String, int, String)}.
- // This fetcher also supports the {@link URL}s created with the {@link ClasspathURIFactory}.
- public static final Set SUPPORTED_SCHEMES = URLFactory.SUPPORTED_SCHEMES;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public InputStream fetch(final URI uri) throws IOException {
- URLConnection conn = uri.toURL().openConnection();
- return this.openConnectionCheckRedirects(conn);
- }
-
- // https://www.cs.mun.ca/java-api-1.5/guide/deployment/deployment-guide/upgrade-guide/article-17.html
- private InputStream openConnectionCheckRedirects(URLConnection c) throws IOException {
- boolean redir;
- int redirects = 0;
- InputStream in = null;
- do {
- if (c instanceof HttpURLConnection) {
- ((HttpURLConnection) c).setInstanceFollowRedirects(false);
- }
- // We want to open the input stream before getting headers
- // because getHeaderField() et al swallow IOExceptions.
- in = c.getInputStream();
- redir = false;
- if (c instanceof HttpURLConnection) {
- HttpURLConnection http = (HttpURLConnection) c;
- int stat = http.getResponseCode();
- if (stat >= 300 && stat <= 307 && stat != 306
- && stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
- URL base = http.getURL();
- String loc = http.getHeaderField("Location");
- URL target = null;
- if (loc != null) {
- target = new URL(base, loc);
- }
- http.disconnect();
- // Redirection should be allowed only for HTTP and HTTPS
- // and should be limited to 5 redirections at most.
- if (target == null
- || !(target.getProtocol().equals("http")
- || target.getProtocol().equals("https"))
- || redirects >= 5) {
- throw new SecurityException("illegal URL redirect");
- }
- redir = true;
- c = target.openConnection();
- redirects++;
- }
- }
- }
- while (redir);
- return in;
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/URNURIFactory.java b/src/main/java/com/networknt/schema/uri/URNURIFactory.java
deleted file mode 100644
index 8ea7914a3..000000000
--- a/src/main/java/com/networknt/schema/uri/URNURIFactory.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.networknt.schema.uri;
-
-import java.net.URI;
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A URIFactory that handles "urn" scheme of {@link URI}s.
- */
-public final class URNURIFactory implements URIFactory {
-
- public static final String SCHEME = "urn";
-
- @Override
- public URI create(final String uri) {
- try {
- return URI.create(uri);
- } catch (IllegalArgumentException e) {
- throw new IllegalArgumentException("Unable to create URI.", e);
- }
- }
-
- @Override
- public URI create(final URI baseURI, final String segment) {
- String urnPart = baseURI.getRawSchemeSpecificPart();
- int pos = urnPart.indexOf(':');
- String namespace = pos < 0 ? urnPart : urnPart.substring(0, pos);
- return URI.create(SCHEME + ":" + namespace + ":" + segment);
- }
-}
diff --git a/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java b/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
new file mode 100644
index 000000000..bf69037df
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
@@ -0,0 +1,31 @@
+/*
+ * 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.uri;
+
+import java.net.URI;
+
+import com.networknt.schema.SchemaLocation;
+
+/**
+ * Loads from uri.
+ */
+public class UriSchemaLoader implements SchemaLoader {
+ @Override
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ URI uri = URI.create(schemaLocation.getAbsoluteIri().toString());
+ return () -> uri.toURL().openStream();
+ }
+}
diff --git a/src/main/java/com/networknt/schema/walk/WalkEvent.java b/src/main/java/com/networknt/schema/walk/WalkEvent.java
index 9569e9e6a..bf7cf6ca6 100644
--- a/src/main/java/com/networknt/schema/walk/WalkEvent.java
+++ b/src/main/java/com/networknt/schema/walk/WalkEvent.java
@@ -9,8 +9,6 @@
import com.networknt.schema.SchemaValidatorsConfig;
import com.networknt.schema.ValidationContext;
-import java.net.URI;
-
/**
* Encapsulation of Walk data that is passed into the {@link JsonSchemaWalkListener}.
*/
@@ -64,11 +62,11 @@ public JsonNodePath getInstanceLocation() {
return instanceLocation;
}
- public JsonSchema getRefSchema(URI schemaUri) {
+ public JsonSchema getRefSchema(SchemaLocation schemaUri) {
return currentJsonSchemaFactory.getSchema(schemaUri, validationContext.getConfig());
}
- public JsonSchema getRefSchema(URI schemaUri, SchemaValidatorsConfig schemaValidatorsConfig) {
+ public JsonSchema getRefSchema(SchemaLocation schemaUri, SchemaValidatorsConfig schemaValidatorsConfig) {
if (schemaValidatorsConfig != null) {
return currentJsonSchemaFactory.getSchema(schemaUri, schemaValidatorsConfig);
} else {
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index f5d072196..f95be7dfa 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -21,14 +21,14 @@
import com.networknt.schema.suite.TestCase;
import com.networknt.schema.suite.TestSource;
import com.networknt.schema.suite.TestSpec;
-import com.networknt.schema.uri.URITranslator;
+import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
+
import org.junit.jupiter.api.AssertionFailureBuilder;
import org.junit.jupiter.api.DynamicNode;
import org.opentest4j.AssertionFailedError;
import java.io.IOException;
import java.io.UncheckedIOException;
-import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -184,10 +184,10 @@ private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, Test
return JsonSchemaFactory
.builder(base)
.objectMapper(this.mapper)
- .addUriTranslator(URITranslator.combine(
- URITranslator.prefix("https://", "http://"),
- URITranslator.prefix("http://json-schema.org", "resource:")
- ))
+ .absoluteIriMappers(absoluteIriMappers -> {
+ absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://", "http://"));
+ absoluteIriMappers.add(new PrefixAbsoluteIriMapper("http://json-schema.org", "resource:"));
+ })
.build();
}
@@ -217,7 +217,7 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS
}
}
- URI testCaseFileUri = URI.create("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));
+ SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));
JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testSpec.getTestCase().getSchema(), config);
return dynamicTest(testSpec.getDescription(), () -> executeAndReset(schema, testSpec));
diff --git a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
index 3fc48fe48..ad74d5e32 100644
--- a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
+++ b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
@@ -21,7 +21,6 @@
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -68,7 +67,7 @@ public static JsonSchema getJsonSchemaFromStringContent(String schemaContent) {
public static JsonSchema getJsonSchemaFromUrl(String uri) throws URISyntaxException {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
- return factory.getSchema(new URI(uri));
+ return factory.getSchema(SchemaLocation.of(uri));
}
public static JsonSchema getJsonSchemaFromJsonNode(JsonNode jsonNode) {
diff --git a/src/test/java/com/networknt/schema/CustomUriTest.java b/src/test/java/com/networknt/schema/CustomUriTest.java
index ded82508b..3df482c01 100644
--- a/src/test/java/com/networknt/schema/CustomUriTest.java
+++ b/src/test/java/com/networknt/schema/CustomUriTest.java
@@ -1,18 +1,11 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.JsonSchema;
-import com.networknt.schema.JsonSchemaFactory;
-import com.networknt.schema.SpecVersion;
-import com.networknt.schema.ValidationMessage;
-import com.networknt.schema.uri.URIFactory;
-import com.networknt.schema.uri.URIFetcher;
+import com.networknt.schema.uri.InputStreamSource;
+import com.networknt.schema.uri.SchemaLoader;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Set;
@@ -38,27 +31,15 @@ public void customUri() throws Exception {
private JsonSchemaFactory buildJsonSchemaFactory() {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .uriFetcher(new CustomUriFetcher(), "custom").uriFactory(new CustomUriFactory(), "custom").build();
+ .schemaLoaders(schemaLoaders -> schemaLoaders.add(0, new CustomUriFetcher())).build();
}
- private static class CustomUriFetcher implements URIFetcher {
+ private static class CustomUriFetcher implements SchemaLoader {
private static final String SCHEMA = "{\"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\"$id\":\"custom:date\",\"type\":\"string\",\"format\":\"date\"}";
@Override
- public InputStream fetch(final URI uri) throws IOException {
- return new ByteArrayInputStream(SCHEMA.getBytes(StandardCharsets.UTF_8));
- }
- }
-
- private static class CustomUriFactory implements URIFactory {
- @Override
- public URI create(final String uri) {
- return URI.create(uri);
- }
-
- @Override
- public URI create(final URI baseURI, final String segment) {
- return baseURI.resolve(segment);
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ return () -> new ByteArrayInputStream(SCHEMA.getBytes(StandardCharsets.UTF_8));
}
}
}
diff --git a/src/test/java/com/networknt/schema/CyclicDependencyTest.java b/src/test/java/com/networknt/schema/CyclicDependencyTest.java
index 017a30862..8dd294d67 100644
--- a/src/test/java/com/networknt/schema/CyclicDependencyTest.java
+++ b/src/test/java/com/networknt/schema/CyclicDependencyTest.java
@@ -3,8 +3,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
-import java.net.URI;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CyclicDependencyTest {
@@ -34,9 +32,8 @@ public void whenDependencyBetweenSchemaThenValidationSuccessful() throws Excepti
" ]\n" +
"}";
- URI jsonSchemaLocation = URI.create("resource:/draft4/issue258/Master.json");
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
- JsonSchema schema = schemaFactory.getSchema(jsonSchemaLocation, config);
+ JsonSchema schema = schemaFactory.getSchema(SchemaLocation.of("resource:/draft4/issue258/Master.json"), config);
assertEquals(0, schema.validate(new ObjectMapper().readTree(jsonObject)).size());
}
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index 02257a127..2c1b6980b 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -1,13 +1,12 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.URITranslator;
+import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Set;
@@ -19,10 +18,10 @@ public class Issue285Test {
private JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
.objectMapper(mapper)
- .addUriTranslator(URITranslator.combine(
- URITranslator.prefix("http://json-schema.org", "resource:"),
- URITranslator.prefix("https://json-schema.org", "resource:")
- ))
+ .absoluteIriMappers(absoluteIriMappers -> {
+ absoluteIriMappers.add(new PrefixAbsoluteIriMapper("http://json-schema.org", "resource:"));
+ absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://json-schema.org", "resource:"));
+ })
.build();
@@ -101,9 +100,8 @@ public void nestedValidation() throws IOException {
// In this case a nested type declaration isn't valid and should raise an error.
// The result is not as expected and we get no validation error.
@Test
- @Disabled
public void nestedTypeValidation() throws IOException, URISyntaxException {
- URI uri = new URI("https://json-schema.org/draft/2019-09/schema");
+ SchemaLocation uri = SchemaLocation.of("https://json-schema.org/draft/2019-09/schema");
JsonSchema jsonSchema = schemaFactory.getSchema(uri);
Set validationMessages = jsonSchema.validate(mapper.readTree(invalidNestedSchema));
@@ -126,7 +124,7 @@ public void nestedTypeValidation() throws IOException, URISyntaxException {
// The result is as expected and we get no validation error: '[$.type: does not have a value in the enumeration [array, boolean, integer, null, number, object, string], $.type: should be valid to any of the schemas array]'.
@Test
public void typeValidation() throws IOException, URISyntaxException {
- URI uri = new URI("https://json-schema.org/draft/2019-09/schema");
+ SchemaLocation uri = SchemaLocation.of("https://json-schema.org/draft/2019-09/schema");
JsonSchema jsonSchema = schemaFactory.getSchema(uri);
Set validationMessages = jsonSchema.validate(mapper.readTree(invalidSchema));
diff --git a/src/test/java/com/networknt/schema/Issue314Test.java b/src/test/java/com/networknt/schema/Issue314Test.java
index 5260d3b89..1ac0200ee 100644
--- a/src/test/java/com/networknt/schema/Issue314Test.java
+++ b/src/test/java/com/networknt/schema/Issue314Test.java
@@ -12,7 +12,6 @@ public class Issue314Test {
"http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0",
JsonMetaSchema.getV7())
.build())
- .forceHttps(false)
.build();
@Test
diff --git a/src/test/java/com/networknt/schema/Issue366FailFastTest.java b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
index 051159765..6267f51f1 100644
--- a/src/test/java/com/networknt/schema/Issue366FailFastTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
@@ -33,7 +32,7 @@ private void setupSchema() throws IOException {
schemaValidatorsConfig.setTypeLoose(false);
- URI uri = getSchema();
+ SchemaLocation uri = getSchema();
InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json");
JsonNode testCases = objectMapper.readValue(in, JsonNode.class);
@@ -100,7 +99,7 @@ public void neitherValid() throws Exception {
});
}
- private URI getSchema() {
- return URI.create("classpath:" + "/draft7/issue366_schema.json");
+ private SchemaLocation getSchema() {
+ return SchemaLocation.of("classpath:" + "/draft7/issue366_schema.json");
}
}
diff --git a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
index 04773e794..4b6814909 100644
--- a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
@@ -7,7 +7,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
@@ -32,7 +31,7 @@ private void setupSchema() throws IOException {
schemaValidatorsConfig.setTypeLoose(false);
- URI uri = getSchema();
+ SchemaLocation uri = getSchema();
InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json");
JsonNode testCases = objectMapper.readValue(in, JsonNode.class);
@@ -99,7 +98,7 @@ public void neitherValid() throws Exception {
assertEquals(errors.size(),3);
}
- private URI getSchema() {
- return URI.create("classpath:" + "/draft7/issue366_schema.json");
+ private SchemaLocation getSchema() {
+ return SchemaLocation.of("classpath:" + "/draft7/issue366_schema.json");
}
}
diff --git a/src/test/java/com/networknt/schema/Issue428Test.java b/src/test/java/com/networknt/schema/Issue428Test.java
index f6ca37eed..5c9114329 100644
--- a/src/test/java/com/networknt/schema/Issue428Test.java
+++ b/src/test/java/com/networknt/schema/Issue428Test.java
@@ -6,7 +6,6 @@
import org.junit.jupiter.api.Test;
import java.io.InputStream;
-import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -19,7 +18,7 @@ public class Issue428Test extends HTTPServiceSupport {
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).objectMapper(mapper).build();
private void runTestFile(String testCaseFile) throws Exception {
- final URI testCaseFileUri = URI.create("classpath:" + testCaseFile);
+ final SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + testCaseFile);
InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(testCaseFile);
ArrayNode testCases = mapper.readValue(in, ArrayNode.class);
diff --git a/src/test/java/com/networknt/schema/Issue461Test.java b/src/test/java/com/networknt/schema/Issue461Test.java
index 5328f65e9..239f61739 100644
--- a/src/test/java/com/networknt/schema/Issue461Test.java
+++ b/src/test/java/com/networknt/schema/Issue461Test.java
@@ -9,14 +9,13 @@
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;
public class Issue461Test {
protected ObjectMapper mapper = new ObjectMapper();
- protected JsonSchema getJsonSchemaFromStreamContentV7(URI schemaUri) {
+ protected JsonSchema getJsonSchemaFromStreamContentV7(SchemaLocation schemaUri) {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
SchemaValidatorsConfig svc = new SchemaValidatorsConfig();
svc.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new Walker());
@@ -25,7 +24,7 @@ protected JsonSchema getJsonSchemaFromStreamContentV7(URI schemaUri) {
@Test
public void shouldWalkWithValidation() throws URISyntaxException, IOException {
- JsonSchema schema = getJsonSchemaFromStreamContentV7(new URI("resource:/draft-07/schema#"));
+ JsonSchema schema = getJsonSchemaFromStreamContentV7(SchemaLocation.of("resource:/draft-07/schema#"));
JsonNode data = mapper.readTree(Issue461Test.class.getResource("/data/issue461-v7.json"));
ValidationResult result = schema.walk(data, true);
Assertions.assertTrue(result.getValidationMessages().isEmpty());
diff --git a/src/test/java/com/networknt/schema/Issue518Test.java b/src/test/java/com/networknt/schema/Issue518Test.java
index 722e95d3b..7015eac23 100644
--- a/src/test/java/com/networknt/schema/Issue518Test.java
+++ b/src/test/java/com/networknt/schema/Issue518Test.java
@@ -15,8 +15,6 @@ public class Issue518Test {
JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
.addMetaSchema(igluMetaSchema)
- .forceHttps(false)
- .removeEmptyFragmentSuffix(false)
.build();
@Test
diff --git a/src/test/java/com/networknt/schema/Issue619Test.java b/src/test/java/com/networknt/schema/Issue619Test.java
index 13e63efab..6c075d943 100644
--- a/src/test/java/com/networknt/schema/Issue619Test.java
+++ b/src/test/java/com/networknt/schema/Issue619Test.java
@@ -54,7 +54,7 @@ public void bundledSchemaLoadsAndValidatesCorrectly_Ref() {
@Test
public void bundledSchemaLoadsAndValidatesCorrectly_Uri() throws Exception {
- JsonSchema rootSchema = factory.getSchema(new URI("resource:schema/issue619.json"));
+ JsonSchema rootSchema = factory.getSchema(SchemaLocation.of("resource:schema/issue619.json"));
assertTrue(rootSchema.validate(one).isEmpty());
assertTrue(rootSchema.validate(two).isEmpty());
@@ -72,7 +72,7 @@ public void uriWithEmptyFragment_Ref() {
@Test
public void uriWithEmptyFragment_Uri() throws Exception {
- JsonSchema rootSchema = factory.getSchema(new URI("resource:schema/issue619.json#"));
+ JsonSchema rootSchema = factory.getSchema(SchemaLocation.of("resource:schema/issue619.json#"));
assertTrue(rootSchema.validate(one).isEmpty());
assertTrue(rootSchema.validate(two).isEmpty());
@@ -90,7 +90,7 @@ public void uriThatPointsToTwoShouldOnlyValidateTwo_Ref() {
@Test
public void uriThatPointsToOneShouldOnlyValidateOne_Uri() throws Exception {
- JsonSchema oneSchema = factory.getSchema(new URI("resource:schema/issue619.json#/definitions/one"));
+ JsonSchema oneSchema = factory.getSchema(SchemaLocation.of("resource:schema/issue619.json#/definitions/one"));
assertTrue(oneSchema.validate(one).isEmpty());
assertFalse(oneSchema.validate(two).isEmpty());
@@ -108,7 +108,7 @@ public void uriThatPointsToNodeThatInTurnReferencesOneShouldOnlyValidateOne_Ref(
@Test
public void uriThatPointsToNodeThatInTurnReferencesOneShouldOnlyValidateOne_Uri() throws Exception {
- JsonSchema oneSchema = factory.getSchema(new URI("resource:schema/issue619.json#/definitions/refToOne"));
+ JsonSchema oneSchema = factory.getSchema(SchemaLocation.of("resource:schema/issue619.json#/definitions/refToOne"));
assertTrue(oneSchema.validate(one).isEmpty());
assertFalse(oneSchema.validate(two).isEmpty());
@@ -130,7 +130,7 @@ public void uriThatPointsToSchemaWithIdThatHasDifferentUri_Uri() throws Exceptio
JsonNode oneArray = getJsonNodeFromStringContent("[[1]]");
JsonNode textArray = getJsonNodeFromStringContent("[[\"a\"]]");
- JsonSchema schemaWithIdFromUri = factory.getSchema(new URI("resource:tests/draft4/refRemote.json#/3/schema"));
+ JsonSchema schemaWithIdFromUri = factory.getSchema(SchemaLocation.of("resource:tests/draft4/refRemote.json#/3/schema"));
assertTrue(schemaWithIdFromUri.validate(oneArray).isEmpty());
assertFalse(schemaWithIdFromUri.validate(textArray).isEmpty());
}
@@ -144,7 +144,7 @@ public void uriThatPointsToSchemaThatDoesNotExistShouldFail_Ref() {
@Test
public void uriThatPointsToSchemaThatDoesNotExistShouldFail_Uri() {
- assertThrows(JsonSchemaException.class, () -> factory.getSchema(new URI("resource:data/schema-that-does-not-exist.json#/definitions/something")));
+ assertThrows(JsonSchemaException.class, () -> factory.getSchema(SchemaLocation.of("resource:data/schema-that-does-not-exist.json#/definitions/something")));
}
@Test
@@ -156,6 +156,6 @@ public void uriThatPointsToNodeThatDoesNotExistShouldFail_Ref() {
@Test
public void uriThatPointsToNodeThatDoesNotExistShouldFail_Uri() {
- assertThrows(JsonSchemaException.class, () -> factory.getSchema(new URI("resource:schema/issue619.json#/definitions/node-that-does-not-exist")));
+ assertThrows(JsonSchemaException.class, () -> factory.getSchema(SchemaLocation.of("resource:schema/issue619.json#/definitions/node-that-does-not-exist")));
}
}
diff --git a/src/test/java/com/networknt/schema/Issue665Test.java b/src/test/java/com/networknt/schema/Issue665Test.java
index 1b9f8bd04..2e4f2d680 100644
--- a/src/test/java/com/networknt/schema/Issue665Test.java
+++ b/src/test/java/com/networknt/schema/Issue665Test.java
@@ -3,10 +3,11 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import com.networknt.schema.uri.MapAbsoluteIriMapper;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
-import java.net.URI;
import java.util.Collections;
import java.util.Set;
@@ -26,11 +27,10 @@ void testUrnUriAsLocalRef() throws IOException {
void testUrnUriAsLocalRef_ExternalURN() {
JsonSchemaFactory factory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
- .uriFetcher(uri -> uri.equals(URI.create("urn:data"))
- ? Thread.currentThread().getContextClassLoader()
- .getResourceAsStream("draft7/urn/issue665_external_urn_subschema.json")
- : null,
- "urn")
+ .absoluteIriMappers(absoluteIriMappers -> {
+ absoluteIriMappers.add(new MapAbsoluteIriMapper(Collections.singletonMap("urn:data",
+ "classpath:draft7/urn/issue665_external_urn_subschema.json")));
+ })
.build();
try (InputStream is = Thread.currentThread().getContextClassLoader()
diff --git a/src/test/java/com/networknt/schema/Issue824Test.java b/src/test/java/com/networknt/schema/Issue824Test.java
index 164420b2d..eb483565b 100644
--- a/src/test/java/com/networknt/schema/Issue824Test.java
+++ b/src/test/java/com/networknt/schema/Issue824Test.java
@@ -2,7 +2,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
-import java.net.URI;
import java.util.Set;
import org.junit.jupiter.api.Test;
@@ -10,15 +9,17 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.URITranslator;
+import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
public class Issue824Test {
@Test
void validate() throws JsonProcessingException {
- SchemaValidatorsConfig config = new SchemaValidatorsConfig();
- config.addUriTranslator(URITranslator.prefix("https://json-schema.org", "resource:"));
- final JsonSchema v201909SpecSchema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)
- .getSchema(URI.create(JsonMetaSchema.getV201909().getUri()), config);
+ final JsonSchema v201909SpecSchema = JsonSchemaFactory
+ .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
+ .absoluteIriMappers(absoluteIriMappers -> {
+ absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://json-schema.org", "resource:"));
+ }).build()
+ .getSchema(SchemaLocation.of(JsonMetaSchema.getV201909().getUri()));
v201909SpecSchema.preloadJsonSchema();
final JsonNode invalidSchema = new ObjectMapper().readTree(
"{"+
diff --git a/src/test/java/com/networknt/schema/Issue928Test.java b/src/test/java/com/networknt/schema/Issue928Test.java
index 04e2d6651..778c2af95 100644
--- a/src/test/java/com/networknt/schema/Issue928Test.java
+++ b/src/test/java/com/networknt/schema/Issue928Test.java
@@ -1,12 +1,10 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.URITranslator;
+import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-import java.net.URI;
-
public class Issue928Test {
private final ObjectMapper mapper = new ObjectMapper();
@@ -14,9 +12,7 @@ private JsonSchemaFactory factoryFor(SpecVersion.VersionFlag version) {
return JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(version))
.objectMapper(mapper)
- .addUriTranslator(
- URITranslator.prefix("https://example.org", "resource:")
- )
+ .absoluteIriMappers(mappers -> mappers.add(new PrefixAbsoluteIriMapper("https://example.org", "classpath:")))
.build();
}
@@ -45,13 +41,13 @@ public void test_spec(SpecVersion.VersionFlag specVersion) {
System.out.println("baseUrl: " + baseUrl);
JsonSchema byPointer = schemaFactory.getSchema(
- URI.create(baseUrl + "#/definitions/example"));
+ SchemaLocation.of(baseUrl + "#/definitions/example"));
Assertions.assertEquals(byPointer.validate(mapper.valueToTree("A")).size(), 0);
Assertions.assertEquals(byPointer.validate(mapper.valueToTree("Z")).size(), 1);
JsonSchema byAnchor = schemaFactory.getSchema(
- URI.create(baseUrl + "#example"));
+ SchemaLocation.of(baseUrl + "#example"));
Assertions.assertEquals(
byPointer.getSchemaNode(),
diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
index d3e382c3e..83aac6da4 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
@@ -2,15 +2,12 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.ClasspathURLFactory;
-import com.networknt.schema.uri.URIFetcher;
-import com.networknt.schema.uri.URLFactory;
+import com.networknt.schema.uri.InputStreamSource;
+import com.networknt.schema.uri.SchemaLoader;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
-import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -20,7 +17,6 @@
public class JsonSchemaFactoryUriCacheTest {
private final ObjectMapper objectMapper = new ObjectMapper();
- private final ClasspathURLFactory classpathURLFactory = new ClasspathURLFactory();
@Test
public void cacheEnabled() throws JsonProcessingException {
@@ -35,7 +31,7 @@ public void cacheDisabled() throws JsonProcessingException {
private void runCacheTest(boolean enableCache) throws JsonProcessingException {
CustomURIFetcher fetcher = new CustomURIFetcher();
JsonSchemaFactory factory = buildJsonSchemaFactory(fetcher, enableCache);
- URI schemaUri = classpathURLFactory.create("cache:uri_mapping/schema1.json");
+ SchemaLocation schemaUri = SchemaLocation.of("cache:uri_mapping/schema1.json");
String schema = "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\", \"title\": \"json-object-with-schema\", \"type\": \"string\" }";
fetcher.addResource(schemaUri, schema);
assertEquals(objectMapper.readTree(schema), factory.getSchema(schemaUri, new SchemaValidatorsConfig()).schemaNode);
@@ -49,27 +45,26 @@ private void runCacheTest(boolean enableCache) throws JsonProcessingException {
private JsonSchemaFactory buildJsonSchemaFactory(CustomURIFetcher uriFetcher, boolean enableUriSchemaCache) {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
.enableUriSchemaCache(enableUriSchemaCache)
- .uriFetcher(uriFetcher, "cache")
- .uriFactory(new URLFactory(), "cache")
+ .schemaLoaders(schemaLoaders -> schemaLoaders.add(0, uriFetcher))
.addMetaSchema(JsonMetaSchema.getV202012())
.build();
}
- private class CustomURIFetcher implements URIFetcher {
+ private class CustomURIFetcher implements SchemaLoader {
- private Map uriToResource = new HashMap<>();
+ private Map uriToResource = new HashMap<>();
- void addResource(URI uri, String schema) {
+ void addResource(SchemaLocation uri, String schema) {
addResource(uri, new ByteArrayInputStream(schema.getBytes(StandardCharsets.UTF_8)));
}
- void addResource(URI uri, InputStream is) {
+ void addResource(SchemaLocation uri, InputStream is) {
uriToResource.put(uri, is);
}
@Override
- public InputStream fetch(URI uri) throws IOException {
- return uriToResource.get(uri);
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ return () -> uriToResource.get(schemaLocation);
}
}
}
diff --git a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
index 8c8006b18..3c7fae236 100644
--- a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
@@ -1,7 +1,6 @@
package com.networknt.schema;
import java.io.InputStream;
-import java.net.URI;
import java.util.ArrayList;
import java.util.List;
@@ -22,7 +21,7 @@ public OpenAPI30JsonSchemaTest() {
}
private void runTestFile(String testCaseFile) throws Exception {
- final URI testCaseFileUri = URI.create("classpath:" + testCaseFile);
+ final SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + testCaseFile);
InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(testCaseFile);
ArrayNode testCases = mapper.readValue(in, ArrayNode.class);
diff --git a/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java b/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
index 819d122dd..1159a57ab 100644
--- a/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
+++ b/src/test/java/com/networknt/schema/RecursiveReferenceValidatorExceptionTest.java
@@ -22,7 +22,8 @@ void testInvalidRecursiveReference() {
JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
JsonSchema jsonSchema = jsonSchemaFactory.getSchema(invalidSchemaJson);
JsonNode schemaNode = jsonSchema.getSchemaNode();
- ValidationContext validationContext = new ValidationContext(jsonSchemaFactory.getUriFactory(), null, jsonSchema.getValidationContext().getMetaSchema(), jsonSchemaFactory, null);
+ ValidationContext validationContext = new ValidationContext(jsonSchema.getValidationContext().getMetaSchema(),
+ jsonSchemaFactory, null);
// Act and Assert
assertThrows(JsonSchemaException.class, () -> {
diff --git a/src/test/java/com/networknt/schema/SharedConfigTest.java b/src/test/java/com/networknt/schema/SharedConfigTest.java
index 63e63ceb8..283d99985 100644
--- a/src/test/java/com/networknt/schema/SharedConfigTest.java
+++ b/src/test/java/com/networknt/schema/SharedConfigTest.java
@@ -37,7 +37,7 @@ public void shouldCallAllKeywordListenerOnWalkStart() throws Exception {
AllKeywordListener allKeywordListener = new AllKeywordListener();
schemaValidatorsConfig.addKeywordWalkListener(allKeywordListener);
- URI draft07Schema = new URI("resource:/draft-07/schema#");
+ SchemaLocation draft07Schema = SchemaLocation.of("resource:/draft-07/schema#");
// depending on this line the test either passes or fails:
// - if this line is executed, then it passes
diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
index a4f41786f..4eb37c049 100644
--- a/src/test/java/com/networknt/schema/UriMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -18,15 +18,13 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchemaFactory.Builder;
-import com.networknt.schema.uri.ClasspathURLFactory;
-import com.networknt.schema.uri.URITranslator;
-import com.networknt.schema.uri.URLFactory;
+import com.networknt.schema.uri.AbsoluteIriMapper;
+import com.networknt.schema.uri.MapAbsoluteIriMapper;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
+import java.io.UncheckedIOException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
@@ -37,26 +35,6 @@
public class UriMappingTest {
private final ObjectMapper mapper = new ObjectMapper();
- private final ClasspathURLFactory classpathURLFactory = new ClasspathURLFactory();
- private final URLFactory urlFactory = new URLFactory();
-
- /**
- * Validate URI Create API
- */
- @Test
- public void testUrlFactoryCreate() {
- try {
- this.urlFactory.create("://example.com/invalid/schema/url");
- fail("Invalid URI, should throw error.");
- }
- catch(IllegalArgumentException e){
-
- }
- catch(Exception e){
- fail("Unknown Exception occured ");
- }
-
- }
/**
* Validate that a JSON URI Mapping file containing the URI Mapping schema is
@@ -66,15 +44,14 @@ public void testUrlFactoryCreate() {
*/
@Test
public void testBuilderUriMappingUri() throws IOException {
- URL mappings = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/uri-mapping.json"));
+ URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json");
JsonMetaSchema draftV4 = JsonMetaSchema.getV4();
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .addUriTranslator(getUriMappingsFromUrl(mappings));
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings)));
JsonSchemaFactory instance = builder.build();
- JsonSchema schema = instance.getSchema(this.urlFactory.create(
+ JsonSchema schema = instance.getSchema(SchemaLocation.of(
"https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
}
@@ -90,7 +67,7 @@ public void testBuilderUriMappingUri() throws IOException {
@Test
public void testBuilderExampleMappings() throws IOException {
JsonSchemaFactory instance = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
- URI example = this.urlFactory.create("http://example.com/invalid/schema/url");
+ SchemaLocation example = SchemaLocation.of("http://example.com/invalid/schema/url");
// first test that attempting to use example URL throws an error
try {
JsonSchema schema = instance.getSchema(example);
@@ -105,13 +82,12 @@ public void testBuilderExampleMappings() throws IOException {
} catch (Exception ex) {
fail("Unexpected exception thrown", ex);
}
- URL mappings = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/invalid-schema-uri.json"));
+ URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json");
JsonMetaSchema draftV4 = JsonMetaSchema.getV4();
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .addUriTranslator(getUriMappingsFromUrl(mappings));
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings)));
instance = builder.build();
JsonSchema schema = instance.getSchema(example);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
@@ -125,14 +101,11 @@ public void testBuilderExampleMappings() throws IOException {
*/
@Test
public void testValidatorConfigUriMappingUri() throws IOException {
- JsonSchemaFactory instance = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
- URL mappings = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/uri-mapping.json"));
- SchemaValidatorsConfig config = new SchemaValidatorsConfig();
- config.addUriTranslator(getUriMappingsFromUrl(mappings));
- JsonSchema schema = instance.getSchema(this.urlFactory.create(
- "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"),
- config);
+ URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json");
+ JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
+ JsonSchema schema = instance.getSchema(SchemaLocation.of(
+ "https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
}
@@ -146,9 +119,11 @@ public void testValidatorConfigUriMappingUri() throws IOException {
*/
@Test
public void testValidatorConfigExampleMappings() throws IOException {
- JsonSchemaFactory instance = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
+ URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json");
+ JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
- URI example = this.urlFactory.create("http://example.com/invalid/schema/url");
+ SchemaLocation example = SchemaLocation.of("http://example.com/invalid/schema/url");
// first test that attempting to use example URL throws an error
try {
JsonSchema schema = instance.getSchema(example, config);
@@ -163,31 +138,31 @@ public void testValidatorConfigExampleMappings() throws IOException {
} catch (Exception ex) {
fail("Unexpected exception thrown");
}
- URL mappings = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/invalid-schema-uri.json"));
- config.addUriTranslator(getUriMappingsFromUrl(mappings));
JsonSchema schema = instance.getSchema(example, config);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
}
@Test
public void testMappingsForRef() throws IOException {
- JsonSchemaFactory instance = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
- URL mappings = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/schema-with-ref-mapping.json"));
+ URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/schema-with-ref-mapping.json");
+ JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
- config.addUriTranslator(getUriMappingsFromUrl(mappings));
- JsonSchema schema = instance.getSchema(this.classpathURLFactory.create("resource:draft4/extra/uri_mapping/schema-with-ref.json"),
+ JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:draft4/extra/uri_mapping/schema-with-ref.json"),
config);
assertEquals(0, schema.validate(mapper.readTree("[]")).size());
}
- private URITranslator getUriMappingsFromUrl(URL url) throws MalformedURLException, IOException {
+ private AbsoluteIriMapper getUriMappingsFromUrl(URL url) {
HashMap map = new HashMap();
- for (JsonNode mapping : mapper.readTree(url)) {
- map.put(mapping.get("publicURL").asText(),
- mapping.get("localURL").asText());
+ try {
+ for (JsonNode mapping : mapper.readTree(url)) {
+ map.put(mapping.get("publicURL").asText(),
+ mapping.get("localURL").asText());
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
- return URITranslator.map(map);
+ return new MapAbsoluteIriMapper(map);
}
}
diff --git a/src/test/java/com/networknt/schema/UrnTest.java b/src/test/java/com/networknt/schema/UrnTest.java
index d28ec56e4..dc27aea52 100644
--- a/src/test/java/com/networknt/schema/UrnTest.java
+++ b/src/test/java/com/networknt/schema/UrnTest.java
@@ -1,14 +1,10 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.ClasspathURLFactory;
-import com.networknt.schema.uri.URLFactory;
-import com.networknt.schema.urn.URNFactory;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
-import java.net.URI;
import java.net.URL;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -16,8 +12,6 @@
public class UrnTest
{
private final ObjectMapper mapper = new ObjectMapper();
- private final ClasspathURLFactory classpathURLFactory = new ClasspathURLFactory();
- private final URLFactory urlFactory = new URLFactory();
/**
* Validate that a JSON URI Mapping file containing the URI Mapping schema is
@@ -27,22 +21,7 @@ public class UrnTest
*/
@Test
public void testURNToURI() throws Exception {
- URL urlTestData = ClasspathURLFactory.convert(
- this.classpathURLFactory.create("resource:draft7/urn/test.json"));
-
- URNFactory urnFactory = new URNFactory()
- {
- @Override public URI create(String urn)
- {
- try {
- URL absoluteURL = ClasspathURLFactory.convert(new ClasspathURLFactory().create(String.format("resource:draft7/urn/%s.schema.json", urn)));
- return absoluteURL.toURI();
- } catch (Exception ex) {
- return null;
- }
- }
- };
-
+ InputStream urlTestData = UrnTest.class.getResourceAsStream("/draft7/urn/test.json");
InputStream is = null;
try {
is = new URL("https://raw.githubusercontent.com/francesc79/json-schema-validator/feature/urn-management/src/test/resources/draft7/urn/urn.schema.json").openStream();
@@ -50,7 +29,8 @@ public void testURNToURI() throws Exception {
JsonSchemaFactory.Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV7.getUri())
.addMetaSchema(draftV7)
- .addUrnFactory(urnFactory);
+ .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(value -> AbsoluteIri.of(String.format("resource:draft7/urn/%s.schema.json", value.toString())))
+ );
JsonSchemaFactory instance = builder.build();
JsonSchema schema = instance.getSchema(is);
assertEquals(0, schema.validate(mapper.readTree(urlTestData)).size());
From e78bf1abf1976c4fd7df380f2808a4571dcda239 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Wed, 10 Jan 2024 14:30:00 +0800
Subject: [PATCH 03/65] Refactor
---
.../java/com/networknt/schema/JsonSchema.java | 3 +-
.../networknt/schema/JsonSchemaFactory.java | 2 +-
.../com/networknt/schema/RefValidator.java | 48 +++++++++----------
.../schema/uri/DefaultSchemaLoader.java | 9 ++--
4 files changed, 33 insertions(+), 29 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index fa05efcf6..9138c66fe 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -229,7 +229,8 @@ public boolean isSchemaResourceRoot() {
return true;
}
// The schema should not cross
- if (!getSchemaLocation().getAbsoluteIri().equals(getParentSchema().getSchemaLocation().getAbsoluteIri())) {
+ if (!Objects.equals(getSchemaLocation().getAbsoluteIri(),
+ getParentSchema().getSchemaLocation().getAbsoluteIri())) {
return true;
}
return false;
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 67b795c22..734081041 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -376,7 +376,7 @@ private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNo
}
private boolean isYaml(final SchemaLocation schemaUri) {
- final String schemeSpecificPart = schemaUri.toString();
+ final String schemeSpecificPart = schemaUri.getAbsoluteIri().toString();
final int idx = schemeSpecificPart.lastIndexOf('.');
if (idx == -1) {
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 7ee733511..80aada7d1 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -21,9 +21,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Set;
+import java.util.*;
public class RefValidator extends BaseJsonValidator {
private static final Logger logger = LoggerFactory.getLogger(RefValidator.class);
@@ -55,8 +53,8 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
// This will determine the correct absolute uri for the refUri. This decision will take into
// account the current uri of the parent schema.
- SchemaLocation schemaLocation = parentSchema.getSchemaLocation().resolve(refUri);
- String schemaUriFinal = schemaLocation.toString();
+ String schemaUriFinal = resolve(parentSchema, refUri);
+ SchemaLocation schemaLocation = SchemaLocation.of(schemaUriFinal);
// This should retrieve schemas regardless of the protocol that is in the uri.
return new JsonSchemaRef(new CachedSupplier<>(() -> {
JsonSchema schemaResource = validationContext.getSchemaResources().get(schemaUriFinal.toString());
@@ -90,25 +88,18 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
}));
} else if (SchemaLocation.Fragment.isAnchorFragment(refValue)) {
- // $ref prevents a sibling $id from changing the base uri
- JsonSchema base = parentSchema;
- if (parentSchema.getId() != null && parentSchema.parentSchema != null) {
- base = parentSchema.parentSchema;
- }
- if (base.getSchemaLocation() != null) {
- String absoluteIri = SchemaLocation.resolve(base.getSchemaLocation(), refValue);
- // Schema resource needs to update the parent and evaluation path
- return new JsonSchemaRef(new CachedSupplier<>(() -> {
- JsonSchema schemaResource = validationContext.getSchemaResources().get(absoluteIri);
- if (schemaResource == null) {
- schemaResource = getJsonSchema(parentSchema, validationContext, refValue, refValueOriginal, evaluationPath);
- }
- if (schemaResource == null) {
- return null;
- }
- return schemaResource.fromRef(parentSchema, evaluationPath);
- }));
- }
+ String absoluteIri = resolve(parentSchema, refValue);
+ // Schema resource needs to update the parent and evaluation path
+ return new JsonSchemaRef(new CachedSupplier<>(() -> {
+ JsonSchema schemaResource = validationContext.getSchemaResources().get(absoluteIri);
+ if (schemaResource == null) {
+ schemaResource = getJsonSchema(parentSchema, validationContext, refValue, refValueOriginal, evaluationPath);
+ }
+ if (schemaResource == null) {
+ return null;
+ }
+ return schemaResource.fromRef(parentSchema, evaluationPath);
+ }));
}
if (refValue.equals(REF_CURRENT)) {
return new JsonSchemaRef(new CachedSupplier<>(
@@ -117,6 +108,15 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
return new JsonSchemaRef(new CachedSupplier<>(
() -> getJsonSchema(parentSchema, validationContext, refValue, refValueOriginal, evaluationPath)));
}
+
+ private static String resolve(JsonSchema parentSchema, String refValue) {
+ // $ref prevents a sibling $id from changing the base uri
+ JsonSchema base = parentSchema;
+ if (parentSchema.getId() != null && parentSchema.parentSchema != null) {
+ base = parentSchema.parentSchema;
+ }
+ return SchemaLocation.resolve(base.getSchemaLocation(), refValue);
+ }
private static JsonSchema getJsonSchema(JsonSchema parent,
ValidationContext validationContext,
diff --git a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
index 408fec9e3..4707a9bab 100644
--- a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
@@ -35,14 +35,17 @@ public DefaultSchemaLoader(List schemaLoaders, List
Date: Wed, 10 Jan 2024 15:04:59 +0800
Subject: [PATCH 04/65] Refactor
---
.../networknt/schema/JsonSchemaFactory.java | 38 +++-----
.../schema/uri/SchemaLoaderBuilder.java | 87 +++++++++++++++++++
.../schema/AbstractJsonSchemaTestSuite.java | 7 +-
.../com/networknt/schema/CustomUriTest.java | 2 +-
.../com/networknt/schema/Issue285Test.java | 9 +-
.../com/networknt/schema/Issue665Test.java | 8 +-
.../com/networknt/schema/Issue824Test.java | 5 +-
.../schema/JsonSchemaFactoryUriCacheTest.java | 2 +-
.../com/networknt/schema/UriMappingTest.java | 10 +--
.../java/com/networknt/schema/UrnTest.java | 2 +-
10 files changed, 117 insertions(+), 53 deletions(-)
create mode 100644 src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 734081041..f8349965a 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -27,9 +27,7 @@
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -45,15 +43,9 @@ public static class Builder {
private YAMLMapper yamlMapper = null;
private String defaultMetaSchemaURI;
private final ConcurrentMap jsonMetaSchemas = new ConcurrentHashMap();
- private List schemaLoaders = new ArrayList<>();
- private List absoluteIriMappers = new ArrayList<>();
+ private SchemaLoaderBuilder schemaLoaderBuilder = new SchemaLoaderBuilder();
private boolean enableUriSchemaCache = true;
- public Builder() {
- this.schemaLoaders.add(new ClasspathSchemaLoader());
- this.schemaLoaders.add(new UriSchemaLoader());
- }
-
public Builder objectMapper(final ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
return this;
@@ -86,13 +78,8 @@ public Builder enableUriSchemaCache(boolean enableUriSchemaCache) {
return this;
}
- public Builder schemaLoaders(Consumer> schemaLoaderCustomizer) {
- schemaLoaderCustomizer.accept(this.schemaLoaders);
- return this;
- }
-
- public Builder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
- absoluteIriCustomizer.accept(this.absoluteIriMappers);
+ public Builder schemaLoaderBuilder(Consumer schemaLoaderBuilderCustomizer) {
+ schemaLoaderBuilderCustomizer.accept(this.schemaLoaderBuilder);
return this;
}
@@ -102,8 +89,7 @@ public JsonSchemaFactory build() {
objectMapper == null ? new ObjectMapper() : objectMapper,
yamlMapper == null ? new YAMLMapper(): yamlMapper,
defaultMetaSchemaURI,
- schemaLoaders,
- absoluteIriMappers,
+ schemaLoaderBuilder,
jsonMetaSchemas,
enableUriSchemaCache
);
@@ -113,8 +99,7 @@ public JsonSchemaFactory build() {
private final ObjectMapper jsonMapper;
private final YAMLMapper yamlMapper;
private final String defaultMetaSchemaURI;
- private final List schemaLoaders;
- private final List absoluteIriMappers;
+ private final SchemaLoaderBuilder schemaLoaderBuilder;
private final SchemaLoader schemaLoader;
private final Map jsonMetaSchemas;
private final ConcurrentMap uriSchemaCache = new ConcurrentHashMap<>();
@@ -125,8 +110,7 @@ private JsonSchemaFactory(
final ObjectMapper jsonMapper,
final YAMLMapper yamlMapper,
final String defaultMetaSchemaURI,
- List schemaLoaders,
- final List absoluteIriMappers,
+ SchemaLoaderBuilder schemaLoaderBuilder,
final Map jsonMetaSchemas,
final boolean enableUriSchemaCache) {
if (jsonMapper == null) {
@@ -135,7 +119,7 @@ private JsonSchemaFactory(
throw new IllegalArgumentException("YAMLMapper must not be null");
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
throw new IllegalArgumentException("defaultMetaSchemaURI must not be null or empty");
- } else if (schemaLoaders == null) {
+ } else if (schemaLoaderBuilder == null) {
throw new IllegalArgumentException("SchemaLoaders must not be null");
} else if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
@@ -145,9 +129,8 @@ private JsonSchemaFactory(
this.jsonMapper = jsonMapper;
this.yamlMapper = yamlMapper;
this.defaultMetaSchemaURI = defaultMetaSchemaURI;
- this.schemaLoaders = schemaLoaders;
- this.absoluteIriMappers = absoluteIriMappers;
- this.schemaLoader = new DefaultSchemaLoader(schemaLoaders, absoluteIriMappers);
+ this.schemaLoaderBuilder = schemaLoaderBuilder;
+ this.schemaLoader = schemaLoaderBuilder.build();
this.jsonMetaSchemas = jsonMetaSchemas;
this.enableUriSchemaCache = enableUriSchemaCache;
}
@@ -208,8 +191,7 @@ public static Builder builder(final JsonSchemaFactory blueprint) {
.defaultMetaSchemaURI(blueprint.defaultMetaSchemaURI)
.objectMapper(blueprint.jsonMapper)
.yamlMapper(blueprint.yamlMapper);
- builder.schemaLoaders = blueprint.schemaLoaders;
- builder.absoluteIriMappers = blueprint.absoluteIriMappers;
+ builder.schemaLoaderBuilder = blueprint.schemaLoaderBuilder;
return builder;
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java b/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
new file mode 100644
index 000000000..973e972c4
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2023 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.uri;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+/**
+ * Builder for {@link SchemaLoader}.
+ */
+public class SchemaLoaderBuilder {
+ private BiFunction, List, SchemaLoader> schemaLoaderFactory = DefaultSchemaLoader::new;
+ private List schemaLoaders = new ArrayList<>();
+ private List absoluteIriMappers = new ArrayList<>();
+
+ public SchemaLoaderBuilder() {
+ this.schemaLoaders.add(new ClasspathSchemaLoader());
+ this.schemaLoaders.add(new UriSchemaLoader());
+ }
+
+ public SchemaLoaderBuilder schemaLoaderFactory(
+ BiFunction, List, SchemaLoader> schemaLoaderFactory) {
+ this.schemaLoaderFactory = schemaLoaderFactory;
+ return this;
+ }
+
+ public SchemaLoaderBuilder schemaLoaders(List schemaLoaders) {
+ this.schemaLoaders = schemaLoaders;
+ return this;
+ }
+
+ public SchemaLoaderBuilder absoluteIriMappers(List absoluteIriMappers) {
+ this.absoluteIriMappers = absoluteIriMappers;
+ return this;
+ }
+
+ public SchemaLoaderBuilder schemaLoaders(Consumer> schemaLoaderCustomizer) {
+ schemaLoaderCustomizer.accept(this.schemaLoaders);
+ return this;
+ }
+
+ public SchemaLoaderBuilder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
+ absoluteIriCustomizer.accept(this.absoluteIriMappers);
+ return this;
+ }
+
+ public SchemaLoaderBuilder absoluteIriMapper(AbsoluteIriMapper absoluteIriMapper) {
+ this.absoluteIriMappers.add(absoluteIriMapper);
+ return this;
+ }
+
+ public SchemaLoaderBuilder schemaLoader(SchemaLoader schemaLoader) {
+ this.schemaLoaders.add(0, schemaLoader);
+ return this;
+ }
+
+ public SchemaLoaderBuilder mapPrefix(String source, String replacement) {
+ this.absoluteIriMappers.add(new PrefixAbsoluteIriMapper(source, replacement));
+ return this;
+ }
+
+ public SchemaLoaderBuilder map(Map mappings) {
+ this.absoluteIriMappers.add(new MapAbsoluteIriMapper(mappings));
+ return this;
+ }
+
+ public SchemaLoader build() {
+ return schemaLoaderFactory.apply(this.schemaLoaders, this.absoluteIriMappers);
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index f95be7dfa..3a5325857 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -184,10 +184,9 @@ private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, Test
return JsonSchemaFactory
.builder(base)
.objectMapper(this.mapper)
- .absoluteIriMappers(absoluteIriMappers -> {
- absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://", "http://"));
- absoluteIriMappers.add(new PrefixAbsoluteIriMapper("http://json-schema.org", "resource:"));
- })
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .mapPrefix("https://", "http://")
+ .mapPrefix("http://json-schema.org", "resource:"))
.build();
}
diff --git a/src/test/java/com/networknt/schema/CustomUriTest.java b/src/test/java/com/networknt/schema/CustomUriTest.java
index 3df482c01..b86a9dff4 100644
--- a/src/test/java/com/networknt/schema/CustomUriTest.java
+++ b/src/test/java/com/networknt/schema/CustomUriTest.java
@@ -31,7 +31,7 @@ public void customUri() throws Exception {
private JsonSchemaFactory buildJsonSchemaFactory() {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .schemaLoaders(schemaLoaders -> schemaLoaders.add(0, new CustomUriFetcher())).build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoader(new CustomUriFetcher())).build();
}
private static class CustomUriFetcher implements SchemaLoader {
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index 2c1b6980b..9c7b33e69 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -18,11 +18,10 @@ public class Issue285Test {
private JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
.objectMapper(mapper)
- .absoluteIriMappers(absoluteIriMappers -> {
- absoluteIriMappers.add(new PrefixAbsoluteIriMapper("http://json-schema.org", "resource:"));
- absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://json-schema.org", "resource:"));
- })
- .build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .mapPrefix("http://json-schema.org", "resource:")
+ .mapPrefix("https://json-schema.org", "resource:"))
+ .build();
String schemaStr = "{\n" +
diff --git a/src/test/java/com/networknt/schema/Issue665Test.java b/src/test/java/com/networknt/schema/Issue665Test.java
index 2e4f2d680..1d57e6f7d 100644
--- a/src/test/java/com/networknt/schema/Issue665Test.java
+++ b/src/test/java/com/networknt/schema/Issue665Test.java
@@ -3,8 +3,6 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-import com.networknt.schema.uri.MapAbsoluteIriMapper;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
@@ -27,9 +25,9 @@ void testUrnUriAsLocalRef() throws IOException {
void testUrnUriAsLocalRef_ExternalURN() {
JsonSchemaFactory factory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
- .absoluteIriMappers(absoluteIriMappers -> {
- absoluteIriMappers.add(new MapAbsoluteIriMapper(Collections.singletonMap("urn:data",
- "classpath:draft7/urn/issue665_external_urn_subschema.json")));
+ .schemaLoaderBuilder(schemaLoaderBuilder -> {
+ schemaLoaderBuilder.map(Collections.singletonMap("urn:data",
+ "classpath:draft7/urn/issue665_external_urn_subschema.json"));
})
.build();
diff --git a/src/test/java/com/networknt/schema/Issue824Test.java b/src/test/java/com/networknt/schema/Issue824Test.java
index eb483565b..bcb2229b4 100644
--- a/src/test/java/com/networknt/schema/Issue824Test.java
+++ b/src/test/java/com/networknt/schema/Issue824Test.java
@@ -9,15 +9,14 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
public class Issue824Test {
@Test
void validate() throws JsonProcessingException {
final JsonSchema v201909SpecSchema = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .absoluteIriMappers(absoluteIriMappers -> {
- absoluteIriMappers.add(new PrefixAbsoluteIriMapper("https://json-schema.org", "resource:"));
+ .schemaLoaderBuilder(schemaLoaderBuilder -> {
+ schemaLoaderBuilder.mapPrefix("https://json-schema.org", "resource:");
}).build()
.getSchema(SchemaLocation.of(JsonMetaSchema.getV201909().getUri()));
v201909SpecSchema.preloadJsonSchema();
diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
index 83aac6da4..71233d591 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
@@ -45,7 +45,7 @@ private void runCacheTest(boolean enableCache) throws JsonProcessingException {
private JsonSchemaFactory buildJsonSchemaFactory(CustomURIFetcher uriFetcher, boolean enableUriSchemaCache) {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
.enableUriSchemaCache(enableUriSchemaCache)
- .schemaLoaders(schemaLoaders -> schemaLoaders.add(0, uriFetcher))
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoader(uriFetcher))
.addMetaSchema(JsonMetaSchema.getV202012())
.build();
}
diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
index 4eb37c049..13b843d14 100644
--- a/src/test/java/com/networknt/schema/UriMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -49,7 +49,7 @@ public void testBuilderUriMappingUri() throws IOException {
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings)));
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings)));
JsonSchemaFactory instance = builder.build();
JsonSchema schema = instance.getSchema(SchemaLocation.of(
"https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
@@ -87,7 +87,7 @@ public void testBuilderExampleMappings() throws IOException {
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings)));
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings)));
instance = builder.build();
JsonSchema schema = instance.getSchema(example);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
@@ -103,7 +103,7 @@ public void testBuilderExampleMappings() throws IOException {
public void testValidatorConfigUriMappingUri() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json");
JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
JsonSchema schema = instance.getSchema(SchemaLocation.of(
"https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
@@ -121,7 +121,7 @@ public void testValidatorConfigUriMappingUri() throws IOException {
public void testValidatorConfigExampleMappings() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json");
JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
SchemaLocation example = SchemaLocation.of("http://example.com/invalid/schema/url");
// first test that attempting to use example URL throws an error
@@ -146,7 +146,7 @@ public void testValidatorConfigExampleMappings() throws IOException {
public void testMappingsForRef() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/schema-with-ref-mapping.json");
JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(getUriMappingsFromUrl(mappings))).build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:draft4/extra/uri_mapping/schema-with-ref.json"),
config);
diff --git a/src/test/java/com/networknt/schema/UrnTest.java b/src/test/java/com/networknt/schema/UrnTest.java
index dc27aea52..02c4d03db 100644
--- a/src/test/java/com/networknt/schema/UrnTest.java
+++ b/src/test/java/com/networknt/schema/UrnTest.java
@@ -29,7 +29,7 @@ public void testURNToURI() throws Exception {
JsonSchemaFactory.Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV7.getUri())
.addMetaSchema(draftV7)
- .absoluteIriMappers(absoluteIriMappers -> absoluteIriMappers.add(value -> AbsoluteIri.of(String.format("resource:draft7/urn/%s.schema.json", value.toString())))
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(value -> AbsoluteIri.of(String.format("resource:draft7/urn/%s.schema.json", value.toString())))
);
JsonSchemaFactory instance = builder.build();
JsonSchema schema = instance.getSchema(is);
From db270485970a295ea57ecd6a9ee8ac2975fa0510 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Wed, 10 Jan 2024 16:21:25 +0800
Subject: [PATCH 05/65] Refactor
---
.../networknt/schema/BaseJsonValidator.java | 1 -
.../java/com/networknt/schema/JsonSchema.java | 41 ++++++++++++++++++-
.../networknt/schema/JsonSchemaFactory.java | 11 ++---
.../com/networknt/schema/Issue928Test.java | 3 +-
4 files changed, 44 insertions(+), 12 deletions(-)
diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java
index 724e03d7d..9b48b7936 100644
--- a/src/main/java/com/networknt/schema/BaseJsonValidator.java
+++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java
@@ -22,7 +22,6 @@
import org.slf4j.Logger;
-import java.net.URI;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index 9138c66fe..a3b7574ca 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -24,7 +24,6 @@
import com.networknt.schema.walk.WalkListenerRunner;
import java.io.UnsupportedEncodingException;
-import java.net.URI;
import java.net.URLDecoder;
import java.util.*;
@@ -189,6 +188,46 @@ public JsonNode getRefSchemaNode(String ref) {
return node;
}
+
+ public JsonSchema getSubSchema(JsonNodePath fragment) {
+ JsonSchema document = findSchemaResourceRoot();
+ JsonSchema parent = document;
+ JsonSchema subSchema = null;
+ for (int x = 0; x < fragment.getNameCount(); x++) {
+ Object segment = fragment.getElement(x);
+ JsonNode subSchemaNode = parent.getNode(segment);
+ if (subSchemaNode != null) {
+ SchemaLocation schemaLocation = parent.getSchemaLocation();
+ JsonNodePath evaluationPath = parent.getEvaluationPath();
+ if (segment instanceof Number) {
+ int index = ((Number) segment).intValue();
+ schemaLocation = schemaLocation.append(index);
+ evaluationPath = evaluationPath.append(index);
+ } else {
+ schemaLocation = schemaLocation.append(segment.toString());
+ evaluationPath = evaluationPath.append(segment.toString());
+ }
+ subSchema = parent.getValidationContext().newSchema(schemaLocation, evaluationPath, subSchemaNode,
+ parent);
+ parent = subSchema;
+ } else {
+ throw new JsonSchemaException("Unable to find subschema " + fragment.toString() + " in "
+ + document.getSchemaLocation().toString());
+ }
+ }
+ return subSchema;
+ }
+
+ protected JsonNode getNode(Object propertyOrIndex) {
+ JsonNode node = getSchemaNode();
+ JsonNode value = null;
+ if (propertyOrIndex instanceof Number) {
+ value = node.get(((Number) propertyOrIndex).intValue());
+ } else {
+ value = node.get(propertyOrIndex.toString());
+ }
+ return value;
+ }
public JsonSchema findLexicalRoot() {
JsonSchema ancestor = this;
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index f8349965a..bcd0c7bad 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -290,7 +290,7 @@ public JsonSchema getSchema(final SchemaLocation schemaUri, final SchemaValidato
protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValidatorsConfig config) {
try (InputStream inputStream = this.schemaLoader.getSchema(schemaUri).getInputStream()) {
if (inputStream == null) {
- throw new IOException("Cannot load schema uri");
+ throw new IOException("Cannot load schema at " + schemaUri.toString());
}
final JsonNode schemaNode;
if (isYaml(schemaUri)) {
@@ -312,12 +312,7 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
final ValidationContext validationContext = createValidationContext(schemaNode, config);
SchemaLocation documentLocation = new SchemaLocation(schemaLocation.getAbsoluteIri());
JsonSchema document = doCreate(validationContext, documentLocation, evaluationPath, schemaNode, null, false);
- JsonNode subSchemaNode = document.getRefSchemaNode("#" + schemaLocation.getFragment().toString());
- if (subSchemaNode != null) {
- jsonSchema = doCreate(validationContext, schemaLocation, evaluationPath, subSchemaNode, document, false);
- } else {
- throw new JsonSchemaException("Unable to find subschema");
- }
+ return document.getSubSchema(schemaLocation.getFragment());
}
return jsonSchema;
} catch (IOException e) {
@@ -325,7 +320,7 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
throw new JsonSchemaException(e);
}
}
-
+
public JsonSchema getSchema(final SchemaLocation schemaUri) {
return getSchema(schemaUri, new SchemaValidatorsConfig());
}
diff --git a/src/test/java/com/networknt/schema/Issue928Test.java b/src/test/java/com/networknt/schema/Issue928Test.java
index 778c2af95..6e23d8822 100644
--- a/src/test/java/com/networknt/schema/Issue928Test.java
+++ b/src/test/java/com/networknt/schema/Issue928Test.java
@@ -1,7 +1,6 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -12,7 +11,7 @@ private JsonSchemaFactory factoryFor(SpecVersion.VersionFlag version) {
return JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(version))
.objectMapper(mapper)
- .absoluteIriMappers(mappers -> mappers.add(new PrefixAbsoluteIriMapper("https://example.org", "classpath:")))
+ .schemaLoaderBuilder(builder -> builder.mapPrefix("https://example.org", "classpath:"))
.build();
}
From a206a856ec9aad36557288ec9a757b2f77cb7504 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Fri, 12 Jan 2024 08:32:24 +0800
Subject: [PATCH 06/65] Fix test
---
src/test/java/com/networknt/schema/UriMappingTest.java | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
index 13b843d14..1d916c8d4 100644
--- a/src/test/java/com/networknt/schema/UriMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -120,8 +120,8 @@ public void testValidatorConfigUriMappingUri() throws IOException {
@Test
public void testValidatorConfigExampleMappings() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json");
- JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
+ JsonSchemaFactory instance = JsonSchemaFactory
+ .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
SchemaLocation example = SchemaLocation.of("http://example.com/invalid/schema/url");
// first test that attempting to use example URL throws an error
@@ -138,6 +138,8 @@ public void testValidatorConfigExampleMappings() throws IOException {
} catch (Exception ex) {
fail("Unexpected exception thrown");
}
+ instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
JsonSchema schema = instance.getSchema(example, config);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
}
From 59b0303bfe331700bb9588ba0e5a683cfa3ef786 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 16 Jan 2024 13:44:29 +0800
Subject: [PATCH 07/65] Refactor
---
src/main/java/com/networknt/schema/RefValidator.java | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 80aada7d1..2ddbf3a52 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -57,7 +57,7 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
SchemaLocation schemaLocation = SchemaLocation.of(schemaUriFinal);
// This should retrieve schemas regardless of the protocol that is in the uri.
return new JsonSchemaRef(new CachedSupplier<>(() -> {
- JsonSchema schemaResource = validationContext.getSchemaResources().get(schemaUriFinal.toString());
+ JsonSchema schemaResource = validationContext.getSchemaResources().get(schemaUriFinal);
if (schemaResource == null) {
schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
if (schemaResource != null) {
@@ -139,7 +139,6 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
if (node != null) {
SchemaLocation path = null;
JsonSchema currentParent = parent;
- SchemaLocation currentUri = parent.getSchemaLocation();
if (refValue.startsWith(REF_CURRENT)) {
// relative to document
path = new SchemaLocation(parent.schemaLocation.getAbsoluteIri(),
@@ -153,12 +152,10 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
if (id != null) {
if (id.contains(":")) {
// absolute
- currentUri = currentUri.resolve(id);
path = SchemaLocation.of(id);
} else {
// relative
String absoluteUri = path.getAbsoluteIri().resolve(id).toString();
- currentUri = currentUri.resolve(absoluteUri);
path = SchemaLocation.of(absoluteUri);
}
}
From 73287c903bf0c6bb70e2b312b31e89fb3b5a0f01 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Fri, 19 Jan 2024 17:58:34 +0800
Subject: [PATCH 08/65] Add test
---
.../com/networknt/schema/JsonMetaSchema.java | 5 +-
.../com/networknt/schema/RefValidator.java | 7 +-
.../schema/uri/ClasspathSchemaLoader.java | 8 ++-
.../java/com/networknt/schema/RefTest.java | 64 +++++++++++++++++++
.../schema/ref-main-schema-resource.json | 51 +++++++++++++++
src/test/resources/schema/ref-main.json | 18 ++++++
src/test/resources/schema/ref-ref.json | 24 +++++++
7 files changed, 170 insertions(+), 7 deletions(-)
create mode 100644 src/test/java/com/networknt/schema/RefTest.java
create mode 100644 src/test/resources/schema/ref-main-schema-resource.json
create mode 100644 src/test/resources/schema/ref-main.json
create mode 100644 src/test/resources/schema/ref-ref.json
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index 7c688a1aa..813501fb0 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -298,5 +298,8 @@ public JsonValidator newValidator(ValidationContext validationContext, SchemaLoc
}
}
-
+ @Override
+ public String toString() {
+ return this.uri;
+ }
}
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 2ddbf3a52..8050cf007 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -126,14 +126,13 @@ private static JsonSchema getJsonSchema(JsonSchema parent,
JsonNode node = parent.getRefSchemaNode(refValue);
if (node != null) {
return validationContext.getSchemaReferences().computeIfAbsent(refValueOriginal, key -> {
- return getJsonSchema(node, parent, validationContext, refValue, evaluationPath);
+ return getJsonSchema(node, parent, refValue, evaluationPath);
});
}
return null;
}
private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
- ValidationContext validationContext,
String refValue,
JsonNodePath evaluationPath) {
if (node != null) {
@@ -148,7 +147,7 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
if (refParts.length > 3) {
String[] subschemaParts = Arrays.copyOf(refParts, refParts.length - 2);
JsonNode subschemaNode = parent.getRefSchemaNode(String.join("/", subschemaParts));
- String id = validationContext.resolveSchemaId(subschemaNode);
+ String id = parent.getValidationContext().resolveSchemaId(subschemaNode);
if (id != null) {
if (id.contains(":")) {
// absolute
@@ -176,7 +175,7 @@ private static JsonSchema getJsonSchema(JsonNode node, JsonSchema parent,
path = path.append(parts[x]);
}
}
- return validationContext.newSchema(path, evaluationPath, node, currentParent);
+ return parent.getValidationContext().newSchema(path, evaluationPath, node, currentParent);
}
throw null;
}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
index c1f49cb3b..d357ba29f 100644
--- a/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
@@ -34,10 +34,14 @@ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
}
ClassLoader loader = classLoader;
String name = schemaLocation.getAbsoluteIri().toString().substring(scheme.length() + 1);
+ if (name.startsWith("//")) {
+ name = name.substring(2);
+ }
+ String resource = name;
return () -> {
- InputStream result = loader.getResourceAsStream(name);
+ InputStream result = loader.getResourceAsStream(resource);
if (result == null) {
- result = loader.getResourceAsStream(name.substring(1));
+ result = loader.getResourceAsStream(resource.substring(1));
}
return result;
};
diff --git a/src/test/java/com/networknt/schema/RefTest.java b/src/test/java/com/networknt/schema/RefTest.java
new file mode 100644
index 000000000..1f02d7b17
--- /dev/null
+++ b/src/test/java/com/networknt/schema/RefTest.java
@@ -0,0 +1,64 @@
+package com.networknt.schema;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
+public class RefTest {
+ private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder().build();
+
+ @Test
+ void shouldLoadRelativeClasspathReference() throws JsonMappingException, JsonProcessingException {
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ JsonSchema schema = factory.getSchema(SchemaLocation.of("classpath:///schema/ref-main.json"), config);
+ String input = "{\r\n"
+ + " \"DriverProperties\": {\r\n"
+ + " \"CommonProperties\": {\r\n"
+ + " \"field2\": \"abc-def-xyz\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + "}";
+ assertEquals("https://json-schema.org/draft-04/schema", schema.getValidationContext().getMetaSchema().getUri());
+ Set errors = schema.validate(OBJECT_MAPPER.readTree(input));
+ assertEquals(1, errors.size());
+ ValidationMessage error = errors.iterator().next();
+ assertEquals("classpath:///schema/ref-ref.json#/definitions/DriverProperties/required",
+ error.getSchemaLocation().toString());
+ assertEquals("/properties/DriverProperties/properties/CommonProperties/$ref/required",
+ error.getEvaluationPath().toString());
+ assertEquals("field1", error.getProperty());
+ }
+
+ @Test
+ void shouldLoadSchemaResource() throws JsonMappingException, JsonProcessingException {
+ JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012);
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setPathType(PathType.JSON_POINTER);
+ JsonSchema schema = factory.getSchema(SchemaLocation.of("classpath:///schema/ref-main-schema-resource.json"), config);
+ String input = "{\r\n"
+ + " \"DriverProperties\": {\r\n"
+ + " \"CommonProperties\": {\r\n"
+ + " \"field2\": \"abc-def-xyz\"\r\n"
+ + " }\r\n"
+ + " }\r\n"
+ + "}";
+ assertEquals("https://json-schema.org/draft-04/schema", schema.getValidationContext().getMetaSchema().getUri());
+ Set errors = schema.validate(OBJECT_MAPPER.readTree(input));
+ assertEquals(1, errors.size());
+ ValidationMessage error = errors.iterator().next();
+ assertEquals("https://www.example.org/common#/definitions/DriverProperties/required",
+ error.getSchemaLocation().toString());
+ assertEquals("/properties/DriverProperties/properties/CommonProperties/$ref/required",
+ error.getEvaluationPath().toString());
+ assertEquals("field1", error.getProperty());
+ }
+}
diff --git a/src/test/resources/schema/ref-main-schema-resource.json b/src/test/resources/schema/ref-main-schema-resource.json
new file mode 100644
index 000000000..5794df791
--- /dev/null
+++ b/src/test/resources/schema/ref-main-schema-resource.json
@@ -0,0 +1,51 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "id": "https://www.example.org/driver",
+ "type": "object",
+ "required": [
+ "DriverProperties"
+ ],
+ "properties": {
+ "DriverProperties": {
+ "type": "object",
+ "properties": {
+ "CommonProperties": {
+ "$ref": "common#/definitions/DriverProperties"
+ }
+ },
+ "required": [
+ "CommonProperties"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false,
+ "definitions": {
+ "common": {
+ "id": "https://www.example.org/common",
+ "type": "object",
+ "additionalProperties": false,
+ "definitions": {
+ "DriverProperties": {
+ "type": "object",
+ "properties": {
+ "field1": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512
+ },
+ "field2": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512
+ }
+ },
+ "required": [
+ "field1"
+ ],
+ "additionalProperties": false
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/schema/ref-main.json b/src/test/resources/schema/ref-main.json
new file mode 100644
index 000000000..c219fec5f
--- /dev/null
+++ b/src/test/resources/schema/ref-main.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "type": "object",
+ "required" : ["DriverProperties"],
+ "properties": {
+ "DriverProperties": {
+ "type": "object",
+ "properties": {
+ "CommonProperties": {
+ "$ref": "ref-ref.json#/definitions/DriverProperties"
+ }
+ },
+ "required": ["CommonProperties"],
+ "additionalProperties": false
+ }
+ },
+ "additionalProperties": false
+}
\ No newline at end of file
diff --git a/src/test/resources/schema/ref-ref.json b/src/test/resources/schema/ref-ref.json
new file mode 100644
index 000000000..3b43a5d06
--- /dev/null
+++ b/src/test/resources/schema/ref-ref.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "additionalProperties": false,
+ "definitions": {
+ "DriverProperties": {
+ "type": "object",
+ "properties": {
+ "field1": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512
+ },
+ "field2": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 512
+ }
+ },
+ "required": ["field1"],
+ "additionalProperties": false
+ }
+ }
+}
\ No newline at end of file
From 97bfdce9c849663419613336441a9357fc428dc6 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Fri, 19 Jan 2024 21:31:04 +0800
Subject: [PATCH 09/65] Ensure correct meta schema
---
.../networknt/schema/JsonSchemaFactory.java | 21 ++++++++++++++++++-
.../java/com/networknt/schema/RefTest.java | 5 +++++
.../schema/ref-main-schema-resource.json | 3 ++-
3 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index bcd0c7bad..474695e88 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -206,7 +206,18 @@ public JsonSchema create(ValidationContext validationContext, SchemaLocation sch
}
private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, boolean suppressSubSchemaRetrieval) {
- return JsonSchema.from(validationContext, schemaLocation, evaluationPath, schemaNode, parentSchema, suppressSubSchemaRetrieval);
+ return JsonSchema.from(withMetaSchema(validationContext, schemaNode), schemaLocation, evaluationPath,
+ schemaNode, parentSchema, suppressSubSchemaRetrieval);
+ }
+
+ private ValidationContext withMetaSchema(ValidationContext validationContext, JsonNode schemaNode) {
+ JsonMetaSchema metaSchema = getMetaSchema(schemaNode);
+ if (metaSchema != null && !metaSchema.getUri().equals(validationContext.getMetaSchema().getUri())) {
+ return new ValidationContext(metaSchema, validationContext.getJsonSchemaFactory(),
+ validationContext.getConfig(), validationContext.getSchemaReferences(),
+ validationContext.getSchemaResources());
+ }
+ return validationContext;
}
/**
@@ -230,6 +241,14 @@ protected ValidationContext createValidationContext(final JsonNode schemaNode, S
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
return new ValidationContext(jsonMetaSchema, this, config);
}
+
+ private JsonMetaSchema getMetaSchema(final JsonNode schemaNode) {
+ final JsonNode uriNode = schemaNode.get("$schema");
+ if (uriNode != null && uriNode.isTextual()) {
+ return jsonMetaSchemas.computeIfAbsent(normalizeMetaSchemaUri(uriNode.textValue()), this::fromId);
+ }
+ return null;
+ }
private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
final JsonNode uriNode = schemaNode.get("$schema");
diff --git a/src/test/java/com/networknt/schema/RefTest.java b/src/test/java/com/networknt/schema/RefTest.java
index 1f02d7b17..c628b3597 100644
--- a/src/test/java/com/networknt/schema/RefTest.java
+++ b/src/test/java/com/networknt/schema/RefTest.java
@@ -60,5 +60,10 @@ void shouldLoadSchemaResource() throws JsonMappingException, JsonProcessingExcep
assertEquals("/properties/DriverProperties/properties/CommonProperties/$ref/required",
error.getEvaluationPath().toString());
assertEquals("field1", error.getProperty());
+ JsonSchema driver = schema.getValidationContext().getSchemaResources().get("https://www.example.org/driver#");
+ JsonSchema common = schema.getValidationContext().getSchemaResources().get("https://www.example.org/common#");
+ assertEquals("https://json-schema.org/draft-04/schema", driver.getValidationContext().getMetaSchema().getUri());
+ assertEquals("https://json-schema.org/draft-07/schema", common.getValidationContext().getMetaSchema().getUri());
+
}
}
diff --git a/src/test/resources/schema/ref-main-schema-resource.json b/src/test/resources/schema/ref-main-schema-resource.json
index 5794df791..c42fecde4 100644
--- a/src/test/resources/schema/ref-main-schema-resource.json
+++ b/src/test/resources/schema/ref-main-schema-resource.json
@@ -22,7 +22,8 @@
"additionalProperties": false,
"definitions": {
"common": {
- "id": "https://www.example.org/common",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://www.example.org/common",
"type": "object",
"additionalProperties": false,
"definitions": {
From 52e0e459b3fd1352fcc1668418e953b1dff145aa Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 09:04:10 +0800
Subject: [PATCH 10/65] Support dynamic ref
---
.../networknt/schema/DynamicRefValidator.java | 165 ++++++++++++++++++
.../com/networknt/schema/JsonMetaSchema.java | 8 +
.../java/com/networknt/schema/JsonSchema.java | 40 ++++-
.../networknt/schema/JsonSchemaFactory.java | 4 +-
.../networknt/schema/ValidationContext.java | 15 +-
.../networknt/schema/ValidatorTypeCode.java | 1 +
.../com/networknt/schema/Version202012.java | 1 +
7 files changed, 223 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/networknt/schema/DynamicRefValidator.java
diff --git a/src/main/java/com/networknt/schema/DynamicRefValidator.java b/src/main/java/com/networknt/schema/DynamicRefValidator.java
new file mode 100644
index 000000000..2efbbbf78
--- /dev/null
+++ b/src/main/java/com/networknt/schema/DynamicRefValidator.java
@@ -0,0 +1,165 @@
+/*
+ * 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 com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.CollectorContext.Scope;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * Resolves $dynamicRef.
+ */
+public class DynamicRefValidator extends BaseJsonValidator {
+ private static final Logger logger = LoggerFactory.getLogger(DynamicRefValidator.class);
+
+ protected JsonSchemaRef schema;
+
+ public DynamicRefValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
+ super(schemaLocation, evaluationPath, schemaNode, parentSchema, ValidatorTypeCode.REF, validationContext);
+ String refValue = schemaNode.asText();
+ this.schema = getRefSchema(parentSchema, validationContext, refValue, evaluationPath);
+ }
+
+ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext validationContext, String refValue,
+ JsonNodePath evaluationPath) {
+ String ref = resolve(parentSchema, refValue);
+ return new JsonSchemaRef(new CachedSupplier<>(() -> {
+ JsonSchema refSchema = validationContext.getDynamicAnchors().get(ref);
+ if (refSchema == null) { // This is a $dynamicRef without a matching $dynamicAnchor
+ // A $dynamicRef without a matching $dynamicAnchor in the same schema resource
+ // behaves like a normal $ref to $anchor
+ // A $dynamicRef without anchor in fragment behaves identical to $ref
+ JsonSchemaRef r = RefValidator.getRefSchema(parentSchema, validationContext, ref, evaluationPath);
+ if (r != null) {
+ refSchema = r.getSchema();
+ }
+ } else {
+ // Check parents
+ JsonSchema base = parentSchema;
+ int index = ref.indexOf("#");
+ String anchor = ref.substring(index);
+ String absoluteIri = ref.substring(0, index);
+ while (base.getEvaluationParentSchema() != null) {
+ base = base.getEvaluationParentSchema();
+ if (!base.getSchemaLocation().getAbsoluteIri().toString().equals(absoluteIri)) {
+ absoluteIri = base.getSchemaLocation().getAbsoluteIri().toString();
+ String parentRef = SchemaLocation.resolve(base.getSchemaLocation(), anchor);
+ JsonSchema parentRefSchema = validationContext.getDynamicAnchors().get(parentRef);
+ if (parentRefSchema != null) {
+ refSchema = parentRefSchema;
+ }
+ }
+ }
+ }
+
+ if (refSchema != null) {
+ refSchema = refSchema.fromRef(parentSchema, evaluationPath);
+ }
+ return refSchema;
+ }));
+ }
+
+ private static String resolve(JsonSchema parentSchema, String refValue) {
+ // $ref prevents a sibling $id from changing the base uri
+ JsonSchema base = parentSchema;
+ if (parentSchema.getId() != null && parentSchema.parentSchema != null) {
+ base = parentSchema.parentSchema;
+ }
+ return SchemaLocation.resolve(base.getSchemaLocation(), refValue);
+ }
+
+
+ @Override
+ public Set validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
+ CollectorContext collectorContext = executionContext.getCollectorContext();
+
+ Set errors = Collections.emptySet();
+
+ Scope parentScope = collectorContext.enterDynamicScope();
+ try {
+ debug(logger, node, rootNode, instanceLocation);
+ JsonSchema refSchema = this.schema.getSchema();
+ if (refSchema == null) {
+ ValidationMessage validationMessage = ValidationMessage.builder().type(ValidatorTypeCode.REF.getValue())
+ .code("internal.unresolvedRef").message("{0}: Reference {1} cannot be resolved")
+ .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .arguments(schemaNode.asText()).build();
+ throw new JsonSchemaException(validationMessage);
+ }
+ errors = refSchema.validate(executionContext, node, rootNode, instanceLocation);
+ } finally {
+ Scope scope = collectorContext.exitDynamicScope();
+ if (errors.isEmpty()) {
+ parentScope.mergeWith(scope);
+ }
+ }
+ return errors;
+ }
+
+ @Override
+ public Set walk(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation, boolean shouldValidateSchema) {
+ CollectorContext collectorContext = executionContext.getCollectorContext();
+
+ Set errors = Collections.emptySet();
+
+ Scope parentScope = collectorContext.enterDynamicScope();
+ try {
+ debug(logger, node, rootNode, instanceLocation);
+ // This is important because if we use same JsonSchemaFactory for creating multiple JSONSchema instances,
+ // these schemas will be cached along with config. We have to replace the config for cached $ref references
+ // with the latest config. Reset the config.
+ JsonSchema refSchema = this.schema.getSchema();
+ if (refSchema == null) {
+ ValidationMessage validationMessage = ValidationMessage.builder().type(ValidatorTypeCode.REF.getValue())
+ .code("internal.unresolvedRef").message("{0}: Reference {1} cannot be resolved")
+ .instanceLocation(instanceLocation).evaluationPath(getEvaluationPath())
+ .arguments(schemaNode.asText()).build();
+ throw new JsonSchemaException(validationMessage);
+ }
+ errors = refSchema.walk(executionContext, node, rootNode, instanceLocation, shouldValidateSchema);
+ return errors;
+ } finally {
+ Scope scope = collectorContext.exitDynamicScope();
+ if (shouldValidateSchema) {
+ if (errors.isEmpty()) {
+ parentScope.mergeWith(scope);
+ }
+ }
+ }
+ }
+
+ public JsonSchemaRef getSchemaRef() {
+ return this.schema;
+ }
+
+
+ @Override
+ public void preloadJsonSchema() {
+ JsonSchema jsonSchema = null;
+ try {
+ jsonSchema = this.schema.getSchema();
+ } catch (JsonSchemaException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw new JsonSchemaException(e);
+ }
+ jsonSchema.initializeValidators();
+ }
+}
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index 813501fb0..dfeed1fd7 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -234,6 +234,14 @@ public String readAnchor(JsonNode schemaNode) {
return null;
}
+ public String readDynamicAnchor(JsonNode schemaNode) {
+ boolean supportsDynamicAnchor = this.keywords.containsKey("$dynamicAnchor");
+ if (supportsDynamicAnchor) {
+ return readText(schemaNode, "$dynamicAnchor");
+ }
+ return null;
+ }
+
public JsonNode getNodeByFragmentRef(String ref, JsonNode node) {
boolean supportsAnchor = this.keywords.containsKey("$anchor");
String refName = supportsAnchor ? ref.substring(1) : ref;
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index a3b7574ca..e27441219 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -59,11 +59,10 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc
JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
super(schemaLocation.resolve(validationContext.resolveSchemaId(schemaNode)), evaluationPath, schemaNode, parent,
null, null, validationContext, suppressSubSchemaRetrieval);
- this.validationContext = validationContext;
- this.metaSchema = validationContext.getMetaSchema();
+ this.metaSchema = this.validationContext.getMetaSchema();
initializeConfig();
- this.id = validationContext.resolveSchemaId(this.schemaNode);
- this.anchor = validationContext.getMetaSchema().readAnchor(this.schemaNode);
+ this.id = this.validationContext.resolveSchemaId(this.schemaNode);
+ this.anchor = this.validationContext.getMetaSchema().readAnchor(this.schemaNode);
if (this.id != null) {
this.validationContext.getSchemaResources()
.putIfAbsent(this.schemaLocation != null ? this.schemaLocation.toString() : this.id, this);
@@ -72,6 +71,11 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc
this.validationContext.getSchemaResources()
.putIfAbsent(this.schemaLocation.getAbsoluteIri().toString() + "#" + anchor, this);
}
+ String dynamicAnchor = this.validationContext.getMetaSchema().readDynamicAnchor(schemaNode);
+ if (dynamicAnchor != null) {
+ this.validationContext.getDynamicAnchors()
+ .putIfAbsent(this.schemaLocation.getAbsoluteIri().toString() + "#" + dynamicAnchor, this);
+ }
getValidators();
}
@@ -116,7 +120,9 @@ public JsonSchema fromRef(JsonSchema refEvaluationParentSchema, JsonNodePath ref
copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(),
copy.getValidationContext().getJsonSchemaFactory(),
refEvaluationParentSchema.validationContext.getConfig(),
- copy.getValidationContext().getSchemaReferences(), copy.getValidationContext().getSchemaResources());
+ refEvaluationParentSchema.getValidationContext().getSchemaReferences(),
+ refEvaluationParentSchema.getValidationContext().getSchemaResources(),
+ refEvaluationParentSchema.getValidationContext().getDynamicAnchors());
copy.evaluationPath = refEvaluationPath;
copy.evaluationParentSchema = refEvaluationParentSchema;
// Validator state is reset due to the changes in evaluation path
@@ -134,7 +140,8 @@ public JsonSchema withConfig(SchemaValidatorsConfig config) {
copy.validationContext = new ValidationContext(copy.getValidationContext().getMetaSchema(),
copy.getValidationContext().getJsonSchemaFactory(), config,
copy.getValidationContext().getSchemaReferences(),
- copy.getValidationContext().getSchemaResources());
+ copy.getValidationContext().getSchemaResources(),
+ copy.getValidationContext().getDynamicAnchors());
copy.validatorsLoaded = false;
copy.requiredValidator = null;
copy.typeValidator = null;
@@ -188,7 +195,26 @@ public JsonNode getRefSchemaNode(String ref) {
return node;
}
-
+
+ public JsonSchema getRefSchema(JsonNodePath fragment) {
+ if (PathType.JSON_POINTER.equals(fragment.getPathType())) {
+ // Json Pointer
+ return getSubSchema(fragment);
+ } else {
+ // Anchor
+ String base = this.getSchemaLocation().getAbsoluteIri() != null ? this.schemaLocation.getAbsoluteIri().toString() : "";
+ String anchor = base + "#" + fragment.toString();
+ JsonSchema result = this.validationContext.getSchemaResources().get(anchor);
+ if (result == null) {
+ result = this.validationContext.getDynamicAnchors().get(anchor);
+ }
+ if (result == null) {
+ throw new JsonSchemaException("Unable to find anchor "+anchor);
+ }
+ return result;
+ }
+ }
+
public JsonSchema getSubSchema(JsonNodePath fragment) {
JsonSchema document = findSchemaResourceRoot();
JsonSchema parent = document;
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 474695e88..a7e44b7cf 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -215,7 +215,7 @@ private ValidationContext withMetaSchema(ValidationContext validationContext, Js
if (metaSchema != null && !metaSchema.getUri().equals(validationContext.getMetaSchema().getUri())) {
return new ValidationContext(metaSchema, validationContext.getJsonSchemaFactory(),
validationContext.getConfig(), validationContext.getSchemaReferences(),
- validationContext.getSchemaResources());
+ validationContext.getSchemaResources(), validationContext.getDynamicAnchors());
}
return validationContext;
}
@@ -331,7 +331,7 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
final ValidationContext validationContext = createValidationContext(schemaNode, config);
SchemaLocation documentLocation = new SchemaLocation(schemaLocation.getAbsoluteIri());
JsonSchema document = doCreate(validationContext, documentLocation, evaluationPath, schemaNode, null, false);
- return document.getSubSchema(schemaLocation.getFragment());
+ return document.getRefSchema(schemaLocation.getFragment());
}
return jsonSchema;
} catch (IOException e) {
diff --git a/src/main/java/com/networknt/schema/ValidationContext.java b/src/main/java/com/networknt/schema/ValidationContext.java
index 31284fcb6..f0758e43c 100644
--- a/src/main/java/com/networknt/schema/ValidationContext.java
+++ b/src/main/java/com/networknt/schema/ValidationContext.java
@@ -29,15 +29,16 @@ public class ValidationContext {
private final SchemaValidatorsConfig config;
private final ConcurrentMap schemaReferences;
private final ConcurrentMap schemaResources;
+ private final ConcurrentMap dynamicAnchors;
public ValidationContext(JsonMetaSchema metaSchema,
JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config) {
- this(metaSchema, jsonSchemaFactory, config, new ConcurrentHashMap<>(), new ConcurrentHashMap<>());
+ this(metaSchema, jsonSchemaFactory, config, new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>());
}
public ValidationContext(JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchemaFactory,
SchemaValidatorsConfig config, ConcurrentMap schemaReferences,
- ConcurrentMap schemaResources) {
+ ConcurrentMap schemaResources, ConcurrentMap dynamicAnchors) {
if (metaSchema == null) {
throw new IllegalArgumentException("JsonMetaSchema must not be null");
}
@@ -49,6 +50,7 @@ public ValidationContext(JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchema
this.config = config == null ? new SchemaValidatorsConfig() : config;
this.schemaReferences = schemaReferences;
this.schemaResources = schemaResources;
+ this.dynamicAnchors = dynamicAnchors;
}
public JsonSchema newSchema(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema) {
@@ -90,6 +92,15 @@ public ConcurrentMap getSchemaResources() {
return this.schemaResources;
}
+ /**
+ * Gets the dynamic anchors.
+ *
+ * @return the dynamic anchors
+ */
+ public ConcurrentMap getDynamicAnchors() {
+ return this.dynamicAnchors;
+ }
+
public JsonMetaSchema getMetaSchema() {
return this.metaSchema;
}
diff --git a/src/main/java/com/networknt/schema/ValidatorTypeCode.java b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
index 786b8ed8c..98f60bd4d 100644
--- a/src/main/java/com/networknt/schema/ValidatorTypeCode.java
+++ b/src/main/java/com/networknt/schema/ValidatorTypeCode.java
@@ -65,6 +65,7 @@ public enum ValidatorTypeCode implements Keyword, ErrorMessageType {
DEPENDENCIES("dependencies", "1007", DependenciesValidator::new, VersionCode.AllVersions),
DEPENDENT_REQUIRED("dependentRequired", "1045", DependentRequired::new, VersionCode.MinV201909),
DEPENDENT_SCHEMAS("dependentSchemas", "1046", DependentSchemas::new, VersionCode.MinV201909),
+ DYNAMIC_REF("$dynamicRef", "1051", DynamicRefValidator::new, VersionCode.MinV202012),
EDITS("edits", "1005", null, VersionCode.AllVersions),
ENUM("enum", "1008", EnumValidator::new, VersionCode.AllVersions),
EXCLUSIVE_MAXIMUM("exclusiveMaximum", "1038", ExclusiveMaximumValidator::new, VersionCode.MinV6),
diff --git a/src/main/java/com/networknt/schema/Version202012.java b/src/main/java/com/networknt/schema/Version202012.java
index f3a6da4cb..3d07de784 100644
--- a/src/main/java/com/networknt/schema/Version202012.java
+++ b/src/main/java/com/networknt/schema/Version202012.java
@@ -28,6 +28,7 @@ public JsonMetaSchema getInstance() {
new NonValidationKeyword("$comment"),
new NonValidationKeyword("$defs"),
new NonValidationKeyword("$anchor"),
+ new NonValidationKeyword("$dynamicAnchor"),
new NonValidationKeyword("deprecated"),
new NonValidationKeyword("contentMediaType"),
new NonValidationKeyword("contentEncoding"),
From 1259752c443860c80867058995ddc33decacf338 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 11:03:08 +0800
Subject: [PATCH 11/65] Fix anchor
---
.../com/networknt/schema/RefValidator.java | 34 +++++++++++++++++--
.../schema/JsonSchemaTestSuiteTest.java | 6 ----
2 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index 8050cf007..e7842faea 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -69,6 +69,10 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
validationContext.getSchemaReferences()
.putAll(schemaResource.getValidationContext().getSchemaReferences());
}
+ if (!schemaResource.getValidationContext().getDynamicAnchors().isEmpty()) {
+ validationContext.getDynamicAnchors()
+ .putAll(schemaResource.getValidationContext().getDynamicAnchors());
+ }
}
}
if (index < 0) {
@@ -78,8 +82,31 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
return schemaResource.fromRef(parentSchema, evaluationPath);
} else {
String newRefValue = refValue.substring(index);
- schemaResource = getJsonSchema(schemaResource, validationContext, newRefValue, refValueOriginal,
- evaluationPath);
+ String find = schemaLocation.getAbsoluteIri() + newRefValue;
+ JsonSchema findSchemaResource = validationContext.getSchemaResources().get(find);
+ if (schemaResource == null) {
+ schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
+ if (schemaResource != null) {
+ if (!schemaResource.getValidationContext().getSchemaResources().isEmpty()) {
+ validationContext.getSchemaResources()
+ .putAll(schemaResource.getValidationContext().getSchemaResources());
+ }
+ if (!schemaResource.getValidationContext().getSchemaReferences().isEmpty()) {
+ validationContext.getSchemaReferences()
+ .putAll(schemaResource.getValidationContext().getSchemaReferences());
+ }
+ if (!schemaResource.getValidationContext().getDynamicAnchors().isEmpty()) {
+ validationContext.getDynamicAnchors()
+ .putAll(schemaResource.getValidationContext().getDynamicAnchors());
+ }
+ }
+ }
+ if (findSchemaResource != null) {
+ schemaResource = findSchemaResource;
+ } else {
+ schemaResource = getJsonSchema(schemaResource, validationContext, newRefValue, refValueOriginal,
+ evaluationPath);
+ }
if (schemaResource == null) {
return null;
}
@@ -92,6 +119,9 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
// Schema resource needs to update the parent and evaluation path
return new JsonSchemaRef(new CachedSupplier<>(() -> {
JsonSchema schemaResource = validationContext.getSchemaResources().get(absoluteIri);
+ if (schemaResource == null) {
+ schemaResource = validationContext.getDynamicAnchors().get(absoluteIri);
+ }
if (schemaResource == null) {
schemaResource = getJsonSchema(parentSchema, validationContext, refValue, refValueOriginal, evaluationPath);
}
diff --git a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
index 034cc1a8b..0c09ff168 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
@@ -69,17 +69,11 @@ protected Optional reason(Path path) {
}
private void disableV202012Tests() {
- this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/anchor.json"), "Unsupported behavior");
- this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/defs.json"), "Unsupported behavior");
- this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/dynamicRef.json"), "Unsupported behavior");
- this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/id.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"), "Unsupported behavior");
}
private void disableV201909Tests() {
- this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/anchor.json"), "Unsupported behavior");
- this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/id.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/vocabulary.json"), "Unsupported behavior");
}
From 0956bbd29e2e761ede9a74287f08f1b426276bda Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 13:56:15 +0800
Subject: [PATCH 12/65] Refactor
---
.../networknt/schema/ExecutionCustomizer.java | 2 +-
.../networknt/schema/JsonSchemaFactory.java | 23 +++++++---
.../com/networknt/schema/RefValidator.java | 46 ++++++++-----------
3 files changed, 36 insertions(+), 35 deletions(-)
diff --git a/src/main/java/com/networknt/schema/ExecutionCustomizer.java b/src/main/java/com/networknt/schema/ExecutionCustomizer.java
index 7ae5d1a56..c24038b8d 100644
--- a/src/main/java/com/networknt/schema/ExecutionCustomizer.java
+++ b/src/main/java/com/networknt/schema/ExecutionCustomizer.java
@@ -20,7 +20,7 @@
* Customize the execution context before validation.
*/
@FunctionalInterface
-interface ExecutionCustomizer {
+public interface ExecutionCustomizer {
/**
* Customize the execution context before validation.
*
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index a7e44b7cf..d7a3f6c4b 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -339,7 +339,19 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
throw new JsonSchemaException(e);
}
}
-
+
+ public JsonSchema getSchema(final URI schemaUri) {
+ return getSchema(SchemaLocation.of(schemaUri.toString()), new SchemaValidatorsConfig());
+ }
+
+ public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
+ return newJsonSchema(SchemaLocation.of(schemaUri.toString()), jsonNode, config);
+ }
+
+ public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode) {
+ return newJsonSchema(SchemaLocation.of(schemaUri.toString()), jsonNode, null);
+ }
+
public JsonSchema getSchema(final SchemaLocation schemaUri) {
return getSchema(schemaUri, new SchemaValidatorsConfig());
}
@@ -347,16 +359,15 @@ public JsonSchema getSchema(final SchemaLocation schemaUri) {
public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
return newJsonSchema(schemaUri, jsonNode, config);
}
-
+
+ public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode) {
+ return newJsonSchema(schemaUri, jsonNode, null);
+ }
public JsonSchema getSchema(final JsonNode jsonNode, final SchemaValidatorsConfig config) {
return newJsonSchema(null, jsonNode, config);
}
- public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode) {
- return newJsonSchema(schemaUri, jsonNode, null);
- }
-
public JsonSchema getSchema(final JsonNode jsonNode) {
return newJsonSchema(null, jsonNode, null);
}
diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java
index e7842faea..17ef67b2e 100644
--- a/src/main/java/com/networknt/schema/RefValidator.java
+++ b/src/main/java/com/networknt/schema/RefValidator.java
@@ -61,18 +61,7 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
if (schemaResource == null) {
schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
if (schemaResource != null) {
- if (!schemaResource.getValidationContext().getSchemaResources().isEmpty()) {
- validationContext.getSchemaResources()
- .putAll(schemaResource.getValidationContext().getSchemaResources());
- }
- if (!schemaResource.getValidationContext().getSchemaReferences().isEmpty()) {
- validationContext.getSchemaReferences()
- .putAll(schemaResource.getValidationContext().getSchemaReferences());
- }
- if (!schemaResource.getValidationContext().getDynamicAnchors().isEmpty()) {
- validationContext.getDynamicAnchors()
- .putAll(schemaResource.getValidationContext().getDynamicAnchors());
- }
+ copySchemaResources(validationContext, schemaResource);
}
}
if (index < 0) {
@@ -84,22 +73,8 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
String newRefValue = refValue.substring(index);
String find = schemaLocation.getAbsoluteIri() + newRefValue;
JsonSchema findSchemaResource = validationContext.getSchemaResources().get(find);
- if (schemaResource == null) {
- schemaResource = validationContext.getJsonSchemaFactory().getSchema(schemaLocation, validationContext.getConfig());
- if (schemaResource != null) {
- if (!schemaResource.getValidationContext().getSchemaResources().isEmpty()) {
- validationContext.getSchemaResources()
- .putAll(schemaResource.getValidationContext().getSchemaResources());
- }
- if (!schemaResource.getValidationContext().getSchemaReferences().isEmpty()) {
- validationContext.getSchemaReferences()
- .putAll(schemaResource.getValidationContext().getSchemaReferences());
- }
- if (!schemaResource.getValidationContext().getDynamicAnchors().isEmpty()) {
- validationContext.getDynamicAnchors()
- .putAll(schemaResource.getValidationContext().getDynamicAnchors());
- }
- }
+ if (findSchemaResource == null) {
+ findSchemaResource = validationContext.getDynamicAnchors().get(find);
}
if (findSchemaResource != null) {
schemaResource = findSchemaResource;
@@ -138,6 +113,21 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
return new JsonSchemaRef(new CachedSupplier<>(
() -> getJsonSchema(parentSchema, validationContext, refValue, refValueOriginal, evaluationPath)));
}
+
+ private static void copySchemaResources(ValidationContext validationContext, JsonSchema schemaResource) {
+ if (!schemaResource.getValidationContext().getSchemaResources().isEmpty()) {
+ validationContext.getSchemaResources()
+ .putAll(schemaResource.getValidationContext().getSchemaResources());
+ }
+ if (!schemaResource.getValidationContext().getSchemaReferences().isEmpty()) {
+ validationContext.getSchemaReferences()
+ .putAll(schemaResource.getValidationContext().getSchemaReferences());
+ }
+ if (!schemaResource.getValidationContext().getDynamicAnchors().isEmpty()) {
+ validationContext.getDynamicAnchors()
+ .putAll(schemaResource.getValidationContext().getDynamicAnchors());
+ }
+ }
private static String resolve(JsonSchema parentSchema, String refValue) {
// $ref prevents a sibling $id from changing the base uri
From 0cfa6c59c7472a6616e26665b8aa5a47064c23b3 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 13:56:33 +0800
Subject: [PATCH 13/65] Update docs
---
README.md | 4 +-
doc/compatibility.md | 10 +-
doc/config.md | 8 -
doc/cust-fetcher.md | 61 --------
doc/multiple-language.md | 10 +-
doc/quickstart.md | 2 +-
doc/schema-map.md | 141 ------------------
doc/schema-retrieval.md | 99 ++++++++++++
.../com/networknt/schema/CustomUriTest.java | 10 +-
.../suite/tests/draft2019-09/refRemote.json | 4 -
.../suite/tests/draft2020-12/refRemote.json | 3 -
src/test/suite/tests/draft4/id.json | 8 +-
12 files changed, 122 insertions(+), 238 deletions(-)
delete mode 100644 doc/cust-fetcher.md
delete mode 100644 doc/schema-map.md
create mode 100644 doc/schema-retrieval.md
diff --git a/README.md b/README.md
index 208147f2b..99af87497 100644
--- a/README.md
+++ b/README.md
@@ -110,9 +110,7 @@ For the latest version, please check the [release](https://github.com/networknt/
## [YAML Validation](doc/yaml.md)
-## [Schema Mapping](doc/schema-map.md)
-
-## [Customized URIFetcher](doc/cust-fetcher.md)
+## [Customizing Schema Retrieval](doc/schema-retrieval.md)
## [Customized MetaSchema](doc/cust-meta.md)
diff --git a/doc/compatibility.md b/doc/compatibility.md
index 292d8073f..525a02a01 100644
--- a/doc/compatibility.md
+++ b/doc/compatibility.md
@@ -12,13 +12,13 @@
| Keyword | Draft 4 | Draft 6 | Draft 7 | Draft 2019-09 | Draft 2020-12 |
|:---------------------------|:-------:|:-------:|:-------:|:-------------:|:-------------:|
-| $anchor | 🚫 | 🚫 | 🚫 | 🔴 | 🔴 |
-| $dynamicAnchor | 🚫 | 🚫 | 🚫 | 🚫 | 🔴 |
-| $dynamicRef | 🚫 | 🚫 | 🚫 | 🚫 | 🔴 |
-| $id | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |
+| $anchor | 🚫 | 🚫 | 🚫 | 🟢 | 🟢 |
+| $dynamicAnchor | 🚫 | 🚫 | 🚫 | 🚫 | 🟢 |
+| $dynamicRef | 🚫 | 🚫 | 🚫 | 🚫 | 🟢 |
+| $id | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
| $recursiveAnchor | 🚫 | 🚫 | 🚫 | 🟢 | 🚫 |
| $recursiveRef | 🚫 | 🚫 | 🚫 | 🟢 | 🚫 |
-| $ref | 🟡 | 🟡 | 🟡 | 🟡 | 🟡 |
+| $ref | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
| $vocabulary | 🚫 | 🚫 | 🚫 | 🔴 | 🔴 |
| additionalItems | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
| additionalProperties | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 |
diff --git a/doc/config.md b/doc/config.md
index 023c344c5..fb265d88e 100644
--- a/doc/config.md
+++ b/doc/config.md
@@ -45,14 +45,6 @@ The default value is true in the SchemaValidatorsConfig object.
For more details, please refer to this [issue](https://github.com/networknt/json-schema-validator/issues/183).
-
-* uriMappings
-
-Map of public, typically internet-accessible schema URLs to alternate locations; this allows for offline validation of schemas that refer to public URLs. This is merged with any mappings the sonSchemaFactory
-may have been built.
-
-The type for this variable is `Map`.
-
* javaSemantics
When set to true, use Java-specific semantics rather than native JavaScript semantics.
diff --git a/doc/cust-fetcher.md b/doc/cust-fetcher.md
deleted file mode 100644
index 2b5c8a14e..000000000
--- a/doc/cust-fetcher.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Custom URIFetcher
-
-The default `URIFetcher` implementation uses JDK connection/socket without handling network exceptions. It works in most of the cases; however, if you want to have a customized implementation, you can do so. One user has his implementation with urirest to handle the timeout. A detailed discussion can be found in this [issue](https://github.com/networknt/json-schema-validator/issues/240)
-
-## Example implementation
-
-The default URIFetcher can be overwritten in order to customize its behaviour in regards of authorization or error handling.
-Therefore the _URIFetcher_ interface must implemented and the method _fetch_ must be overwritten.
-
-```
-public class CustomUriFetcher implements URIFetcher {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(CustomUriFetcher.class);
-
- private final String authorizationToken;
-
- private final HttpClient client;
-
- public CustomUriFetcher(String authorizationToken) {
- this.authorizationToken = authorizationToken;
- this.client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
- }
-
- @Override
- public InputStream fetch(URI uri) throws IOException {
- HttpRequest request = HttpRequest.newBuilder().uri(uri).header("Authorization", authorizationToken).build();
- try {
- HttpResponse response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
- if ((200 > response.statusCode()) || (response.statusCode() > 299)) {
- String errorMessage = String.format("Could not get data from schema endpoint. The following status %d was returned.", response.statusCode());
- LOGGER.error(errorMessage);
- }
-
- return new ByteArrayInputStream(response.body().getBytes(StandardCharsets.UTF_8));
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
-```
-
-Within the _JsonSchemaFactory_ the custom URIFetcher can be referenced.
-This also works for schema references ($ref) inside the schema.
-
-```
-...
-CustomUriFetcher uriFetcher = new CustomUriFetcher(authorizationToken);
-
-JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
- .uriFetcher(uriFetcher, "http")
- .addMetaSchema(JsonMetaSchema.getV7())
- .defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
- .build();
-JsonSchema jsonSchema = schemaFactory.getSchema(schemaUri);
-for (ValidationMessage validationMessage : jsonSchema.validate(jsonNodeRecord)) {
- // handle the validation messages
-}
-```
-
-**_NOTE:_**
-Within `.uriFetcher(uriFetcher, "http")` your URI must be mapped to the related protocol like http, ftp, ...
\ No newline at end of file
diff --git a/doc/multiple-language.md b/doc/multiple-language.md
index 73cb84079..92881501d 100644
--- a/doc/multiple-language.md
+++ b/doc/multiple-language.md
@@ -2,7 +2,7 @@ The error messages have been translated to several languages by contributors, de
bundle under https://github.com/networknt/json-schema-validator/tree/master/src/main/resources. To use one of the
available translations the simplest approach is to set your default locale before running the validation:
-```
+```java
// Set the default locale to German (needs only to be set once before using the validator)
Locale.setDefault(Locale.GERMAN);
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
@@ -14,7 +14,7 @@ Note that the above approach changes the locale for the entire JVM which is prob
using this in an application expected to support multiple languages (for example a localised web application). In this
case you should use the `SchemaValidatorsConfig` class before loading your schema:
-```
+```java
// Set the configuration with a specific locale (you can create this before each validation)
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
config.setLocale(myLocale);
@@ -26,7 +26,7 @@ JsonSchema schema = factory.getSchema(source, config);
Besides setting the locale and using the default resource bundle, you may also specify your own to cover any languages you
choose without adapting the library's source, or to override default messages. In doing so you however you should ensure that your resource bundle covers all the keys defined by the default bundle.
-```
+```java
// Set the configuration with a custom message source
MessageSource messageSource = new ResourceBundleMessageSource("my-messages");
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
@@ -38,7 +38,7 @@ JsonSchema schema = factory.getSchema(source, config);
It is possible to override specific keys from the default resource bundle. Note however that you will need to supply all the languages for that specific key as it will not fallback on the default resource bundle. For instance the jsv-messages-override resource bundle will take precedence when resolving the message key.
-```
+```java
// Set the configuration with a custom message source
MessageSource messageSource = new ResourceBundleMessageSource("jsv-messages-override", DefaultMessageSource.BUNDLE_BASE_NAME);
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
@@ -50,7 +50,7 @@ JsonSchema schema = factory.getSchema(source, config);
The following approach can be used to determine the locale to use on a per user basis using a language tag priority list.
-```
+```java
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
JsonSchema schema = factory.getSchema(source, config);
diff --git a/doc/quickstart.md b/doc/quickstart.md
index bb4ab07a9..65467b817 100644
--- a/doc/quickstart.md
+++ b/doc/quickstart.md
@@ -38,7 +38,7 @@ public class BaseJsonSchemaValidatorTest {
protected JsonSchema getJsonSchemaFromUrl(String uri) throws URISyntaxException {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
- return factory.getSchema(new URI(uri));
+ return factory.getSchema(SchemaLocation.of(uri));
}
protected JsonSchema getJsonSchemaFromJsonNode(JsonNode jsonNode) {
diff --git a/doc/schema-map.md b/doc/schema-map.md
deleted file mode 100644
index beb4559c8..000000000
--- a/doc/schema-map.md
+++ /dev/null
@@ -1,141 +0,0 @@
-While working with JSON schema validation, we have to use external references sometimes. However, there are two issues to have references to schemas on the Internet.
-
-* Some applications are running inside a corporate network without Internet access.
-* Some of the Internet resources are not reliable.
-* A test environment may serve unpublished schemas, which are not yet available at the location identified by the payload's `$schema` property.
-
-One solution is to change all the external reference to internal in JSON schemas, but this is error-prone and hard to maintain in a long run.
-
-A smart solution is to map the external references to internal ones in a configuration file. This allows us to use the resources as they are without any modification. In the JSON schema specification, it is not allowed to use local filesystem resource directly. With the mapping, we can use the local resources without worrying about breaking the specification as the references are still in URL format in schemas. In addition, the mapped URL can be a different external URL, or embbeded within a JAR file with a lot more flexibility.
-
-Note that when using a mapping, the local copy is always used, and the external reference is not queried.
-
-### URI Translation
-
-Both `SchemaValidatorsConfig` and `JsonSchemaFactory` accept one or more `URITranslator` instances. A `URITranslator` is responsible for providing a new URI when the given URI matches certain criteria.
-
-#### Examples
-
-Automatically map HTTP to HTTPS
-
-```java
-SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-config.addUriTranlator(uri -> {
- if ("http".equalsIgnoreCase(uri.getScheme()) {
- try {
- return new URI(
- "https",
- uri.getUserInfo(),
- uri.getHost(),
- uri.getPort(),
- uri.getPath(),
- uri.getQuery(),
- uri.getFragment()
- );
- } catch (URISyntaxException x) {
- throw new IllegalArgumentException(x.getMessage(), x);
- }
- }
- return uri;
-});
-```
-
-Map a public schema to a test environment
-
-```java
-SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-config.addUriTranlator(uri -> {
- if (true
- && "https".equalsIgnoreCase(uri.getScheme()
- && "schemas.acme.org".equalsIgnoreCase(uri.getHost())
- && (-1 == uri.getPort() || 443 == uri.getPort())
- ) {
- try {
- return new URI(
- "http",
- uri.getUserInfo(),
- "test-schemas.acme.org",
- 8080,
- uri.getPath(),
- uri.getQuery(),
- uri.getFragment()
- );
- } catch (URISyntaxException x) {
- throw new IllegalArgumentException(x.getMessage(), x);
- }
- }
- return uri;
-});
-```
-
-Replace a URI with another
-
-**Note:**
-This also works for mapping URNs to resources.
-
-```java
-SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-config.addUriTranlator(URITranslator.map("https://schemas.acme.org/Foo", "classpath://Foo");
-```
-
-### Precedence
-
-Both `SchemaValidatorsConfig` and `JsonSchemaFactory` accept multiple `URITranslator`s and in general, they are evaluated in the order of addition. This means that each `URITranslator` receives the output of the previous translator. For example, assuming the following configuration:
-
-```
-SchemaValidatorsConfig config = new SchemaValidatorsConfig();
-config.addUriTranlator(uri -> {
- if ("http".equalsIgnoreCase(uri.getScheme()) {
- try {
- return new URI(
- "https",
- uri.getUserInfo(),
- uri.getHost(),
- uri.getPort(),
- uri.getPath(),
- uri.getQuery(),
- uri.getFragment()
- );
- } catch (URISyntaxException x) {
- throw new IllegalArgumentException(x.getMessage(), x);
- }
- }
- return uri;
-});
-config.addUriTranlator(uri -> {
- if (true
- && "https".equalsIgnoreCase(uri.getScheme()
- && "schemas.acme.org".equalsIgnoreCase(uri.getHost())
- && (-1 == uri.getPort() || 443 == uri.getPort())
- ) {
- try {
- return new URI(
- "http",
- uri.getUserInfo(),
- "test-schemas.acme.org",
- 8080,
- uri.getPath(),
- uri.getQuery(),
- uri.getFragment()
- );
- } catch (URISyntaxException x) {
- throw new IllegalArgumentException(x.getMessage(), x);
- }
- }
- return uri;
-});
-config.addUriTranlator(URITranslator.map("http://test-schemas.acme.org:8080/Foo", "classpath://Foo");
-```
-
-Given a starting URI of `https://schemas.acme.org/Foo`, the configuration above produces the following translations (in order):
-
-1. The translation from HTTP to HTTPS does not occur since the original URI already specifies HTTPS.
-2. The second rule receives the original URI since nothing happened in the first rule. The second rule translates the URI from `https://schemas.acme.org/Foo` to `http://test-schemas.acme.org:8080/Foo` since the scheme, host and port match this rule.
-3. The third rule receives the URI produced by the second rule and performs a simple mapping to a local resource.
-
-Since all `JsonSchemaFactory`s are created from an optional `SchemaValidatorsConfig`, any `URITranslator`s added to the factory are evaluated after those provided by `SchemaValidatorsConfig`.
-
-### Deprecated
-
-Previously, this library supported simple mappings from one URI to another through `SchemaValidatorsConfig.setUriMappings()` and `JsonSchemaFactory.addUriMappings()`. Usage of these methods are still supported but are now discouraged. `URITranslator` provides a more powerful mechanism of dealing with URI mapping than what was provided before.
-
diff --git a/doc/schema-retrieval.md b/doc/schema-retrieval.md
new file mode 100644
index 000000000..08e37f480
--- /dev/null
+++ b/doc/schema-retrieval.md
@@ -0,0 +1,99 @@
+# Customizing Schema Retrieval
+
+A schema can be identified by its schema identifier which is indicated using the `$id` keyword or `id` keyword in earlier drafts. This is an absolute IRI that uniquely identifies the schema and is not necessarily a network locator. A schema need not be downloadable from it's absolute IRI.
+
+In the event a schema references a schema identifier that is not a subschema resource, for instance defined in the `$defs` keyword or `definitions` keyword. The library will need to be able to retrieve the schema given its schema identifier.
+
+In the event that the schema does not define a schema identifier using the `$id` keyword, the retrieval IRI will be used as it's schema identifier.
+
+## Mapping Schema Identifier to Retrieval IRI
+
+The schema identifier can be mapped to the retrieval IRI by implementing the `AbsoluteIriMapper` interface.
+
+### Configuring AbsoluteIriMapper
+
+```java
+JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .absoluteIriMapper(new CustomAbsoluteIriMapper())
+ .addMetaSchema(JsonMetaSchema.getV7())
+ .defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
+ .build();
+```
+
+### Configuring Prefix Mappings
+
+```java
+JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .mapPrefix("https://", "http://")
+ .mapPrefix("http://json-schema.org", "classpath:"))
+ .addMetaSchema(JsonMetaSchema.getV7())
+ .defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
+ .build();
+```
+
+## Customizing Network Schema Retrieval
+
+The default `UriSchemaLoader` implementation uses JDK connection/socket without handling network exceptions. It works in most of the cases; however, if you want to have a customized implementation, you can do so. One user has his implementation with urirest to handle the timeout. A detailed discussion can be found in this [issue](https://github.com/networknt/json-schema-validator/issues/240)
+
+### Configuring Custom URI Schema Loader
+
+The default `UriSchemaLoader` can be overwritten in order to customize its behaviour in regards of authorization or error handling.
+
+The `SchemaLoader` interface must implemented and the implementation configured on the `JsonSchemaFactory`.
+
+```java
+public class CustomUriSchemaLoader implements SchemaLoader {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CustomUriSchemaLoader.class);
+
+ private final String authorizationToken;
+
+ private final HttpClient client;
+
+ public CustomUriSchemaLoader(String authorizationToken) {
+ this.authorizationToken = authorizationToken;
+ this.client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
+ }
+
+ @Override
+ public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ URI uri = URI.create(schemaLocation.getAbsoluteIri().toString());
+ return () -> {
+ HttpRequest request = HttpRequest.newBuilder().uri(uri).header("Authorization", authorizationToken).build();
+ try {
+ HttpResponse response = this.client.send(request, HttpResponse.BodyHandlers.ofString());
+ if ((200 > response.statusCode()) || (response.statusCode() > 299)) {
+ String errorMessage = String.format("Could not get data from schema endpoint. The following status %d was returned.", response.statusCode());
+ LOGGER.error(errorMessage);
+ }
+ return new ByteArrayInputStream(response.body().getBytes(StandardCharsets.UTF_8));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
+```
+
+Within the `JsonSchemaFactory` the custom `SchemaLoader` must be configured.
+
+```java
+CustomUriSchemaLoader uriSchemaLoader = new CustomUriSchemaLoader(authorizationToken);
+
+JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoaders(schemaLoaders -> {
+ for (int x = 0; x < schemaLoaders.size(); x++) {
+ if (schemaLoaders.get(x) instanceof UriSchemaLoader) {
+ schemaLoaders.set(x, uriSchemaLoader);
+ }
+ }
+ .addMetaSchema(JsonMetaSchema.getV7())
+ .defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
+ .build();
+JsonSchema jsonSchema = schemaFactory.getSchema(schemaUri);
+for (ValidationMessage validationMessage : jsonSchema.validate(jsonNodeRecord)) {
+ // handle the validation messages
+}
+```
diff --git a/src/test/java/com/networknt/schema/CustomUriTest.java b/src/test/java/com/networknt/schema/CustomUriTest.java
index b86a9dff4..9b35ef2fe 100644
--- a/src/test/java/com/networknt/schema/CustomUriTest.java
+++ b/src/test/java/com/networknt/schema/CustomUriTest.java
@@ -3,6 +3,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.uri.InputStreamSource;
import com.networknt.schema.uri.SchemaLoader;
+import com.networknt.schema.uri.UriSchemaLoader;
+
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
@@ -31,7 +33,13 @@ public void customUri() throws Exception {
private JsonSchemaFactory buildJsonSchemaFactory() {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoader(new CustomUriFetcher())).build();
+ .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoaders(schemaLoaders -> {
+ for (int x = 0; x < schemaLoaders.size(); x++) {
+ if (schemaLoaders.get(x) instanceof UriSchemaLoader) {
+ schemaLoaders.set(x, new CustomUriFetcher());
+ }
+ }
+ })).build();
}
private static class CustomUriFetcher implements SchemaLoader {
diff --git a/src/test/suite/tests/draft2019-09/refRemote.json b/src/test/suite/tests/draft2019-09/refRemote.json
index b84dad69a..00bf60b5b 100644
--- a/src/test/suite/tests/draft2019-09/refRemote.json
+++ b/src/test/suite/tests/draft2019-09/refRemote.json
@@ -147,8 +147,6 @@
}
}
},
- "disabled": true,
- "reason": "URI resolution does not account for identifiers that are not at the root schema",
"tests": [
{
"description": "number is valid",
@@ -302,8 +300,6 @@
{
"description": "remote HTTP ref with nested absolute ref",
"schema": {"$ref": "http://localhost:1234/nested-absolute-ref-to-string.json"},
- "disabled": true,
- "reason": "URI resolution does not account for identifiers that are not at the root schema",
"tests": [
{
"description": "number is invalid",
diff --git a/src/test/suite/tests/draft2020-12/refRemote.json b/src/test/suite/tests/draft2020-12/refRemote.json
index 5508e357f..17c36a29a 100644
--- a/src/test/suite/tests/draft2020-12/refRemote.json
+++ b/src/test/suite/tests/draft2020-12/refRemote.json
@@ -147,7 +147,6 @@
}
}
},
- "disabled": true,
"reason": "URI resolution does not account for identifiers that are not at the root schema",
"tests": [
{
@@ -302,8 +301,6 @@
{
"description": "remote HTTP ref with nested absolute ref",
"schema": {"$ref": "http://localhost:1234/nested-absolute-ref-to-string.json"},
- "disabled": true,
- "reason": "URI resolution does not account for identifiers that are not at the root schema",
"tests": [
{
"description": "number is invalid",
diff --git a/src/test/suite/tests/draft4/id.json b/src/test/suite/tests/draft4/id.json
index d49133f74..1c91d33ee 100644
--- a/src/test/suite/tests/draft4/id.json
+++ b/src/test/suite/tests/draft4/id.json
@@ -40,16 +40,12 @@
{
"description": "match $ref to id",
"data": "a string to match #/definitions/id_in_enum",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Dereferencing an id is conditional on the contxt"
+ "valid": true
},
{
"description": "no match on enum or $ref to id",
"data": 1,
- "valid": false,
- "disabled": true,
- "reason": "TODO: Dereferencing an id is conditional on the contxt"
+ "valid": false
}
]
}
From fee73b733dc506d47b746af715dba94c9c998e34 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 14:01:05 +0800
Subject: [PATCH 14/65] Refactor
---
.../java/com/networknt/schema/SchemaValidatorsConfig.java | 5 -----
.../com/networknt/schema/AbstractJsonSchemaTestSuite.java | 1 -
src/test/java/com/networknt/schema/CustomMetaSchemaTest.java | 1 -
src/test/java/com/networknt/schema/Issue285Test.java | 2 --
src/test/java/com/networknt/schema/Issue451Test.java | 1 +
src/test/java/com/networknt/schema/Issue619Test.java | 2 --
src/test/java/com/networknt/schema/Issue650Test.java | 1 -
src/test/java/com/networknt/schema/Issue898Test.java | 4 ----
.../java/com/networknt/schema/JsonWalkApplyDefaultsTest.java | 1 -
src/test/java/com/networknt/schema/OutputFormatTest.java | 1 -
src/test/java/com/networknt/schema/SharedConfigTest.java | 1 -
11 files changed, 1 insertion(+), 19 deletions(-)
diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
index 9c7655542..d2790f96b 100644
--- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
+++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
@@ -19,11 +19,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.networknt.schema.i18n.DefaultMessageSource;
import com.networknt.schema.i18n.MessageSource;
-import com.networknt.schema.uri.AbsoluteIriMapper;
-import com.networknt.schema.uri.ClasspathSchemaLoader;
-import com.networknt.schema.uri.DefaultSchemaLoader;
-import com.networknt.schema.uri.SchemaLoader;
-import com.networknt.schema.uri.UriSchemaLoader;
import com.networknt.schema.walk.JsonSchemaWalkListener;
import java.util.ArrayList;
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 3a5325857..524c8b12f 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -21,7 +21,6 @@
import com.networknt.schema.suite.TestCase;
import com.networknt.schema.suite.TestSource;
import com.networknt.schema.suite.TestSpec;
-import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
import org.junit.jupiter.api.AssertionFailureBuilder;
import org.junit.jupiter.api.DynamicNode;
diff --git a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
index 6ef1f0ab6..7e427c2ef 100644
--- a/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/CustomMetaSchemaTest.java
@@ -22,7 +22,6 @@
import org.junit.jupiter.api.Test;
import java.io.IOException;
-import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index 9c7b33e69..e64f8b673 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -1,9 +1,7 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.networknt.schema.uri.PrefixAbsoluteIriMapper;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.IOException;
diff --git a/src/test/java/com/networknt/schema/Issue451Test.java b/src/test/java/com/networknt/schema/Issue451Test.java
index 8edc17842..e01e430d9 100644
--- a/src/test/java/com/networknt/schema/Issue451Test.java
+++ b/src/test/java/com/networknt/schema/Issue451Test.java
@@ -81,6 +81,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess
}
private Map collector(ExecutionContext executionContext) {
+ @SuppressWarnings("unchecked")
Map collector = (Map) executionContext.getCollectorContext().get(COLLECTOR_ID);
if(collector == null) {
collector = new HashMap<>();
diff --git a/src/test/java/com/networknt/schema/Issue619Test.java b/src/test/java/com/networknt/schema/Issue619Test.java
index 6c075d943..0ea63a2c6 100644
--- a/src/test/java/com/networknt/schema/Issue619Test.java
+++ b/src/test/java/com/networknt/schema/Issue619Test.java
@@ -20,8 +20,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import java.net.URI;
-
import static com.networknt.schema.BaseJsonSchemaValidatorTest.getJsonNodeFromStringContent;
import static org.junit.jupiter.api.Assertions.assertFalse;
diff --git a/src/test/java/com/networknt/schema/Issue650Test.java b/src/test/java/com/networknt/schema/Issue650Test.java
index 37a29f73e..02bffbf25 100644
--- a/src/test/java/com/networknt/schema/Issue650Test.java
+++ b/src/test/java/com/networknt/schema/Issue650Test.java
@@ -1,6 +1,5 @@
package com.networknt.schema;
-import static org.junit.jupiter.api.Assertions.*;
import java.io.InputStream;
import java.util.Set;
import org.junit.jupiter.api.Assertions;
diff --git a/src/test/java/com/networknt/schema/Issue898Test.java b/src/test/java/com/networknt/schema/Issue898Test.java
index 6255fc24a..24b653c8e 100644
--- a/src/test/java/com/networknt/schema/Issue898Test.java
+++ b/src/test/java/com/networknt/schema/Issue898Test.java
@@ -2,14 +2,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import java.io.InputStream;
import java.util.List;
import java.util.Locale;
-import java.util.ResourceBundle;
import static java.util.stream.Collectors.toList;
diff --git a/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java
index 0c95579f2..abb0b06a6 100644
--- a/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java
+++ b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java
@@ -10,7 +10,6 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.hamcrest.Matchers;
-import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
diff --git a/src/test/java/com/networknt/schema/OutputFormatTest.java b/src/test/java/com/networknt/schema/OutputFormatTest.java
index 48c46975a..dc6ef6c07 100644
--- a/src/test/java/com/networknt/schema/OutputFormatTest.java
+++ b/src/test/java/com/networknt/schema/OutputFormatTest.java
@@ -3,7 +3,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import java.io.InputStream;
-import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
diff --git a/src/test/java/com/networknt/schema/SharedConfigTest.java b/src/test/java/com/networknt/schema/SharedConfigTest.java
index 283d99985..6173872ef 100644
--- a/src/test/java/com/networknt/schema/SharedConfigTest.java
+++ b/src/test/java/com/networknt/schema/SharedConfigTest.java
@@ -1,6 +1,5 @@
package com.networknt.schema;
-import java.net.URI;
import java.util.Set;
import org.junit.jupiter.api.Assertions;
From 64d7a68fa7b75ea64093e5b54d12aac025d39778 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 14:31:18 +0800
Subject: [PATCH 15/65] Fix
---
.../networknt/schema/DynamicRefValidator.java | 2 +-
.../draft2020-12/detached-dynamicref.json | 13 ++
.../remotes/draft2020-12/detached-ref.json | 13 ++
.../suite/tests/draft2020-12/dynamicRef.json | 143 ++++++++++++++----
4 files changed, 142 insertions(+), 29 deletions(-)
create mode 100644 src/test/suite/remotes/draft2020-12/detached-dynamicref.json
create mode 100644 src/test/suite/remotes/draft2020-12/detached-ref.json
diff --git a/src/main/java/com/networknt/schema/DynamicRefValidator.java b/src/main/java/com/networknt/schema/DynamicRefValidator.java
index 2efbbbf78..91eb1dd47 100644
--- a/src/main/java/com/networknt/schema/DynamicRefValidator.java
+++ b/src/main/java/com/networknt/schema/DynamicRefValidator.java
@@ -46,7 +46,7 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
// A $dynamicRef without a matching $dynamicAnchor in the same schema resource
// behaves like a normal $ref to $anchor
// A $dynamicRef without anchor in fragment behaves identical to $ref
- JsonSchemaRef r = RefValidator.getRefSchema(parentSchema, validationContext, ref, evaluationPath);
+ JsonSchemaRef r = RefValidator.getRefSchema(parentSchema, validationContext, refValue, evaluationPath);
if (r != null) {
refSchema = r.getSchema();
}
diff --git a/src/test/suite/remotes/draft2020-12/detached-dynamicref.json b/src/test/suite/remotes/draft2020-12/detached-dynamicref.json
new file mode 100644
index 000000000..07cce1dac
--- /dev/null
+++ b/src/test/suite/remotes/draft2020-12/detached-dynamicref.json
@@ -0,0 +1,13 @@
+{
+ "$id": "http://localhost:1234/draft2020-12/detached-dynamicref.json",
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "foo": {
+ "$dynamicRef": "#detached"
+ },
+ "detached": {
+ "$dynamicAnchor": "detached",
+ "type": "integer"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/suite/remotes/draft2020-12/detached-ref.json b/src/test/suite/remotes/draft2020-12/detached-ref.json
new file mode 100644
index 000000000..9c2dca93c
--- /dev/null
+++ b/src/test/suite/remotes/draft2020-12/detached-ref.json
@@ -0,0 +1,13 @@
+{
+ "$id": "http://localhost:1234/draft2020-12/detached-ref.json",
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "foo": {
+ "$ref": "#detached"
+ },
+ "detached": {
+ "$anchor": "detached",
+ "type": "integer"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/suite/tests/draft2020-12/dynamicRef.json b/src/test/suite/tests/draft2020-12/dynamicRef.json
index 0f6ed4804..bff26ad61 100644
--- a/src/test/suite/tests/draft2020-12/dynamicRef.json
+++ b/src/test/suite/tests/draft2020-12/dynamicRef.json
@@ -392,45 +392,84 @@
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://test.json-schema.org/dynamic-ref-with-multiple-paths/main",
+ "if": {
+ "properties": {
+ "kindOfList": { "const": "numbers" }
+ },
+ "required": ["kindOfList"]
+ },
+ "then": { "$ref": "numberList" },
+ "else": { "$ref": "stringList" },
+
"$defs": {
- "inner": {
- "$id": "inner",
- "$dynamicAnchor": "foo",
- "title": "inner",
- "additionalProperties": {
- "$dynamicRef": "#foo"
+ "genericList": {
+ "$id": "genericList",
+ "properties": {
+ "list": {
+ "items": { "$dynamicRef": "#itemType" }
+ }
+ },
+ "$defs": {
+ "defaultItemType": {
+ "$comment": "Only needed to satisfy bookending requirement",
+ "$dynamicAnchor": "itemType"
+ }
}
+ },
+ "numberList": {
+ "$id": "numberList",
+ "$defs": {
+ "itemType": {
+ "$dynamicAnchor": "itemType",
+ "type": "number"
+ }
+ },
+ "$ref": "genericList"
+ },
+ "stringList": {
+ "$id": "stringList",
+ "$defs": {
+ "itemType": {
+ "$dynamicAnchor": "itemType",
+ "type": "string"
+ }
+ },
+ "$ref": "genericList"
}
- },
- "if": {
- "propertyNames": {
- "pattern": "^[a-m]"
- }
- },
- "then": {
- "title": "any type of node",
- "$id": "anyLeafNode",
- "$dynamicAnchor": "foo",
- "$ref": "inner"
- },
- "else": {
- "title": "integer node",
- "$id": "integerNode",
- "$dynamicAnchor": "foo",
- "type": [ "object", "integer" ],
- "$ref": "inner"
}
},
"tests": [
{
- "description": "recurse to anyLeafNode - floats are allowed",
- "data": { "alpha": 1.1 },
+ "description": "number list with number values",
+ "data": {
+ "kindOfList": "numbers",
+ "list": [1.1]
+ },
"valid": true
},
{
- "description": "recurse to integerNode - floats are not allowed",
- "data": { "november": 1.1 },
+ "description": "number list with string values",
+ "data": {
+ "kindOfList": "numbers",
+ "list": ["foo"]
+ },
+ "valid": false
+ },
+ {
+ "description": "string list with number values",
+ "data": {
+ "kindOfList": "strings",
+ "list": [1.1]
+ },
"valid": false
+ },
+ {
+ "description": "string list with string values",
+ "data": {
+ "kindOfList": "strings",
+ "list": ["foo"]
+ },
+ "valid": true
}
]
},
@@ -669,5 +708,53 @@
"valid": true
}
]
+ },
+ {
+ "description": "$ref to $dynamicRef finds detached $dynamicAnchor",
+ "schema": {
+ "$ref": "http://localhost:1234/draft2020-12/detached-dynamicref.json#/$defs/foo"
+ },
+ "tests": [
+ {
+ "description": "number is valid",
+ "data": 1,
+ "valid": true
+ },
+ {
+ "description": "non-number is invalid",
+ "data": "a",
+ "valid": false
+ }
+ ]
+ },
+ {
+ "description": "$dynamicRef points to a boolean schema",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "true": true,
+ "false": false
+ },
+ "properties": {
+ "true": {
+ "$dynamicRef": "#/$defs/true"
+ },
+ "false": {
+ "$dynamicRef": "#/$defs/false"
+ }
+ }
+ },
+ "tests": [
+ {
+ "description": "follow $dynamicRef to a true schema",
+ "data": { "true": 1 },
+ "valid": true
+ },
+ {
+ "description": "follow $dynamicRef to a false schema",
+ "data": { "false": 1 },
+ "valid": false
+ }
+ ]
}
]
From 364365df3e5914b6aa575bed56212f2430f713ec Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 15:37:47 +0800
Subject: [PATCH 16/65] Refactor
---
.../com/networknt/schema/JsonMetaSchema.java | 21 -------------------
.../java/com/networknt/schema/JsonSchema.java | 6 ------
2 files changed, 27 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index dfeed1fd7..037b7551c 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -242,27 +242,6 @@ public String readDynamicAnchor(JsonNode schemaNode) {
return null;
}
- public JsonNode getNodeByFragmentRef(String ref, JsonNode node) {
- boolean supportsAnchor = this.keywords.containsKey("$anchor");
- String refName = supportsAnchor ? ref.substring(1) : ref;
- String fieldToRead = supportsAnchor ? "$anchor" : this.idKeyword;
-
- boolean nodeContainsRef = refName.equals(readText(node, fieldToRead));
- if (nodeContainsRef) {
- return node;
- }
-
- Iterator children = node.elements();
- while (children.hasNext()) {
- JsonNode refNode = getNodeByFragmentRef(ref, children.next());
- if (refNode != null) {
- return refNode;
- }
- }
-
- return null;
- }
-
private static String readText(JsonNode node, String field) {
JsonNode idNode = node.get(field);
if (idNode == null || !idNode.isTextual()) {
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index e27441219..d19295479 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -186,13 +186,7 @@ public JsonNode getRefSchemaNode(String ref) {
if (node.isMissingNode()) {
node = handleNullNode(ref, schema);
}
- } else if ((ref.startsWith("#") && ref.length() > 1) || (ref.startsWith("urn:") && ref.length() > 4)) {
- node = this.metaSchema.getNodeByFragmentRef(ref, node);
- if (node == null) {
- node = handleNullNode(ref, schema);
- }
}
-
return node;
}
From 35c43e37709d6c40ac5269eb417c93be269f1b9e Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 15:38:01 +0800
Subject: [PATCH 17/65] Update test suite
---
src/test/suite/tests/draft2019-09/anchor.json | 90 -------------------
.../tests/draft2019-09/optional/anchor.json | 60 +++++++++++++
.../suite/tests/draft2019-09/optional/id.json | 53 +++++++++++
.../optional/refOfUnknownKeyword.json | 23 +++++
.../draft2019-09/optional/unknownKeyword.json | 57 ++++++++++++
src/test/suite/tests/draft2020-12/anchor.json | 90 -------------------
.../tests/draft2020-12/optional/anchor.json | 60 +++++++++++++
.../suite/tests/draft2020-12/optional/id.json | 53 +++++++++++
.../optional/refOfUnknownKeyword.json | 23 +++++
.../draft2020-12/optional/unknownKeyword.json | 57 ++++++++++++
10 files changed, 386 insertions(+), 180 deletions(-)
create mode 100644 src/test/suite/tests/draft2019-09/optional/anchor.json
create mode 100644 src/test/suite/tests/draft2019-09/optional/id.json
create mode 100644 src/test/suite/tests/draft2019-09/optional/unknownKeyword.json
create mode 100644 src/test/suite/tests/draft2020-12/optional/anchor.json
create mode 100644 src/test/suite/tests/draft2020-12/optional/id.json
create mode 100644 src/test/suite/tests/draft2020-12/optional/unknownKeyword.json
diff --git a/src/test/suite/tests/draft2019-09/anchor.json b/src/test/suite/tests/draft2019-09/anchor.json
index 5d8c86f11..eb0a969a8 100644
--- a/src/test/suite/tests/draft2019-09/anchor.json
+++ b/src/test/suite/tests/draft2019-09/anchor.json
@@ -81,64 +81,6 @@
}
]
},
- {
- "description": "$anchor inside an enum is not a real identifier",
- "comment": "the implementation must not be confused by an $anchor buried in the enum",
- "schema": {
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$defs": {
- "anchor_in_enum": {
- "enum": [
- {
- "$anchor": "my_anchor",
- "type": "null"
- }
- ]
- },
- "real_identifier_in_schema": {
- "$anchor": "my_anchor",
- "type": "string"
- },
- "zzz_anchor_in_const": {
- "const": {
- "$anchor": "my_anchor",
- "type": "null"
- }
- }
- },
- "anyOf": [
- { "$ref": "#/$defs/anchor_in_enum" },
- { "$ref": "#my_anchor" }
- ]
- },
- "tests": [
- {
- "description": "exact match to enum, and type matches",
- "data": {
- "$anchor": "my_anchor",
- "type": "null"
- },
- "valid": true
- },
- {
- "description": "in implementations that strip $anchor, this may match either $def",
- "data": {
- "type": "null"
- },
- "valid": false
- },
- {
- "description": "match $ref to $anchor",
- "data": "a string to match #/$defs/anchor_in_enum",
- "valid": true
- },
- {
- "description": "no match on enum or $ref to $anchor",
- "data": 1,
- "valid": false
- }
- ]
- },
{
"description": "same $anchor with different base uri",
"schema": {
@@ -175,38 +117,6 @@
}
]
},
- {
- "description": "non-schema object containing an $anchor property",
- "schema": {
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "$defs": {
- "const_not_anchor": {
- "const": {
- "$anchor": "not_a_real_anchor"
- }
- }
- },
- "if": {
- "const": "skip not_a_real_anchor"
- },
- "then": true,
- "else" : {
- "$ref": "#/$defs/const_not_anchor"
- }
- },
- "tests": [
- {
- "description": "skip traversing definition for a valid result",
- "data": "skip not_a_real_anchor",
- "valid": true
- },
- {
- "description": "const at const_not_anchor does not match",
- "data": 1,
- "valid": false
- }
- ]
- },
{
"description": "invalid anchors",
"comment": "Section 8.2.3",
diff --git a/src/test/suite/tests/draft2019-09/optional/anchor.json b/src/test/suite/tests/draft2019-09/optional/anchor.json
new file mode 100644
index 000000000..45951d0a3
--- /dev/null
+++ b/src/test/suite/tests/draft2019-09/optional/anchor.json
@@ -0,0 +1,60 @@
+[
+ {
+ "description": "$anchor inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $anchor buried in the enum",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$defs": {
+ "anchor_in_enum": {
+ "enum": [
+ {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ ]
+ },
+ "real_identifier_in_schema": {
+ "$anchor": "my_anchor",
+ "type": "string"
+ },
+ "zzz_anchor_in_const": {
+ "const": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/anchor_in_enum" },
+ { "$ref": "#my_anchor" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "in implementations that strip $anchor, this may match either $def",
+ "data": {
+ "type": "null"
+ },
+ "valid": false
+ },
+ {
+ "description": "match $ref to $anchor",
+ "data": "a string to match #/$defs/anchor_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $anchor",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/src/test/suite/tests/draft2019-09/optional/id.json b/src/test/suite/tests/draft2019-09/optional/id.json
new file mode 100644
index 000000000..4daa8f51f
--- /dev/null
+++ b/src/test/suite/tests/draft2019-09/optional/id.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "$id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $id buried in the enum",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$defs": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/draft2019-09/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/draft2019-09/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/draft2019-09/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_enum" },
+ { "$ref": "https://localhost:1234/draft2019-09/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/draft2019-09/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to $id",
+ "data": "a string to match #/$defs/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/src/test/suite/tests/draft2019-09/optional/refOfUnknownKeyword.json b/src/test/suite/tests/draft2019-09/optional/refOfUnknownKeyword.json
index eee1c33ed..e9a75dd1e 100644
--- a/src/test/suite/tests/draft2019-09/optional/refOfUnknownKeyword.json
+++ b/src/test/suite/tests/draft2019-09/optional/refOfUnknownKeyword.json
@@ -42,5 +42,28 @@
"valid": false
}
]
+ },
+ {
+ "description": "reference internals of known non-applicator",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$id": "/base",
+ "examples": [
+ { "type": "string" }
+ ],
+ "$ref": "#/examples/0"
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 42,
+ "valid": false
+ }
+ ]
}
]
diff --git a/src/test/suite/tests/draft2019-09/optional/unknownKeyword.json b/src/test/suite/tests/draft2019-09/optional/unknownKeyword.json
new file mode 100644
index 000000000..f98e87c54
--- /dev/null
+++ b/src/test/suite/tests/draft2019-09/optional/unknownKeyword.json
@@ -0,0 +1,57 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2019-09/schema",
+ "$defs": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/draft2019-09/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/draft2019-09/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/draft2019-09/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_unknown0" },
+ { "$ref": "#/$defs/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/draft2019-09/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/src/test/suite/tests/draft2020-12/anchor.json b/src/test/suite/tests/draft2020-12/anchor.json
index 423835dac..83a7166d7 100644
--- a/src/test/suite/tests/draft2020-12/anchor.json
+++ b/src/test/suite/tests/draft2020-12/anchor.json
@@ -81,64 +81,6 @@
}
]
},
- {
- "description": "$anchor inside an enum is not a real identifier",
- "comment": "the implementation must not be confused by an $anchor buried in the enum",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$defs": {
- "anchor_in_enum": {
- "enum": [
- {
- "$anchor": "my_anchor",
- "type": "null"
- }
- ]
- },
- "real_identifier_in_schema": {
- "$anchor": "my_anchor",
- "type": "string"
- },
- "zzz_anchor_in_const": {
- "const": {
- "$anchor": "my_anchor",
- "type": "null"
- }
- }
- },
- "anyOf": [
- { "$ref": "#/$defs/anchor_in_enum" },
- { "$ref": "#my_anchor" }
- ]
- },
- "tests": [
- {
- "description": "exact match to enum, and type matches",
- "data": {
- "$anchor": "my_anchor",
- "type": "null"
- },
- "valid": true
- },
- {
- "description": "in implementations that strip $anchor, this may match either $def",
- "data": {
- "type": "null"
- },
- "valid": false
- },
- {
- "description": "match $ref to $anchor",
- "data": "a string to match #/$defs/anchor_in_enum",
- "valid": true
- },
- {
- "description": "no match on enum or $ref to $anchor",
- "data": 1,
- "valid": false
- }
- ]
- },
{
"description": "same $anchor with different base uri",
"schema": {
@@ -175,38 +117,6 @@
}
]
},
- {
- "description": "non-schema object containing an $anchor property",
- "schema": {
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$defs": {
- "const_not_anchor": {
- "const": {
- "$anchor": "not_a_real_anchor"
- }
- }
- },
- "if": {
- "const": "skip not_a_real_anchor"
- },
- "then": true,
- "else" : {
- "$ref": "#/$defs/const_not_anchor"
- }
- },
- "tests": [
- {
- "description": "skip traversing definition for a valid result",
- "data": "skip not_a_real_anchor",
- "valid": true
- },
- {
- "description": "const at const_not_anchor does not match",
- "data": 1,
- "valid": false
- }
- ]
- },
{
"description": "invalid anchors",
"comment": "Section 8.2.2",
diff --git a/src/test/suite/tests/draft2020-12/optional/anchor.json b/src/test/suite/tests/draft2020-12/optional/anchor.json
new file mode 100644
index 000000000..6d6713be5
--- /dev/null
+++ b/src/test/suite/tests/draft2020-12/optional/anchor.json
@@ -0,0 +1,60 @@
+[
+ {
+ "description": "$anchor inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $anchor buried in the enum",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "anchor_in_enum": {
+ "enum": [
+ {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ ]
+ },
+ "real_identifier_in_schema": {
+ "$anchor": "my_anchor",
+ "type": "string"
+ },
+ "zzz_anchor_in_const": {
+ "const": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/anchor_in_enum" },
+ { "$ref": "#my_anchor" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$anchor": "my_anchor",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "in implementations that strip $anchor, this may match either $def",
+ "data": {
+ "type": "null"
+ },
+ "valid": false
+ },
+ {
+ "description": "match $ref to $anchor",
+ "data": "a string to match #/$defs/anchor_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $anchor",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/src/test/suite/tests/draft2020-12/optional/id.json b/src/test/suite/tests/draft2020-12/optional/id.json
new file mode 100644
index 000000000..0b7df4e80
--- /dev/null
+++ b/src/test/suite/tests/draft2020-12/optional/id.json
@@ -0,0 +1,53 @@
+[
+ {
+ "description": "$id inside an enum is not a real identifier",
+ "comment": "the implementation must not be confused by an $id buried in the enum",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "id_in_enum": {
+ "enum": [
+ {
+ "$id": "https://localhost:1234/draft2020-12/id/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/draft2020-12/id/my_identifier.json",
+ "type": "string"
+ },
+ "zzz_id_in_const": {
+ "const": {
+ "$id": "https://localhost:1234/draft2020-12/id/my_identifier.json",
+ "type": "null"
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_enum" },
+ { "$ref": "https://localhost:1234/draft2020-12/id/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "exact match to enum, and type matches",
+ "data": {
+ "$id": "https://localhost:1234/draft2020-12/id/my_identifier.json",
+ "type": "null"
+ },
+ "valid": true
+ },
+ {
+ "description": "match $ref to $id",
+ "data": "a string to match #/$defs/id_in_enum",
+ "valid": true
+ },
+ {
+ "description": "no match on enum or $ref to $id",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
diff --git a/src/test/suite/tests/draft2020-12/optional/refOfUnknownKeyword.json b/src/test/suite/tests/draft2020-12/optional/refOfUnknownKeyword.json
index f91c18884..c2b080a1e 100644
--- a/src/test/suite/tests/draft2020-12/optional/refOfUnknownKeyword.json
+++ b/src/test/suite/tests/draft2020-12/optional/refOfUnknownKeyword.json
@@ -42,5 +42,28 @@
"valid": false
}
]
+ },
+ {
+ "description": "reference internals of known non-applicator",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "/base",
+ "examples": [
+ { "type": "string" }
+ ],
+ "$ref": "#/examples/0"
+ },
+ "tests": [
+ {
+ "description": "match",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "mismatch",
+ "data": 42,
+ "valid": false
+ }
+ ]
}
]
diff --git a/src/test/suite/tests/draft2020-12/optional/unknownKeyword.json b/src/test/suite/tests/draft2020-12/optional/unknownKeyword.json
new file mode 100644
index 000000000..28b0c4ce9
--- /dev/null
+++ b/src/test/suite/tests/draft2020-12/optional/unknownKeyword.json
@@ -0,0 +1,57 @@
+[
+ {
+ "description": "$id inside an unknown keyword is not a real identifier",
+ "comment": "the implementation must not be confused by an $id in locations we do not know how to parse",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$defs": {
+ "id_in_unknown0": {
+ "not": {
+ "array_of_schemas": [
+ {
+ "$id": "https://localhost:1234/draft2020-12/unknownKeyword/my_identifier.json",
+ "type": "null"
+ }
+ ]
+ }
+ },
+ "real_id_in_schema": {
+ "$id": "https://localhost:1234/draft2020-12/unknownKeyword/my_identifier.json",
+ "type": "string"
+ },
+ "id_in_unknown1": {
+ "not": {
+ "object_of_schemas": {
+ "foo": {
+ "$id": "https://localhost:1234/draft2020-12/unknownKeyword/my_identifier.json",
+ "type": "integer"
+ }
+ }
+ }
+ }
+ },
+ "anyOf": [
+ { "$ref": "#/$defs/id_in_unknown0" },
+ { "$ref": "#/$defs/id_in_unknown1" },
+ { "$ref": "https://localhost:1234/draft2020-12/unknownKeyword/my_identifier.json" }
+ ]
+ },
+ "tests": [
+ {
+ "description": "type matches second anyOf, which has a real schema in it",
+ "data": "a string",
+ "valid": true
+ },
+ {
+ "description": "type matches non-schema in first anyOf",
+ "data": null,
+ "valid": false
+ },
+ {
+ "description": "type matches non-schema in third anyOf",
+ "data": 1,
+ "valid": false
+ }
+ ]
+ }
+]
From 113c1047a29d30e00a59aa074b01bff3be415b9c Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 16:38:14 +0800
Subject: [PATCH 18/65] Refactor
---
.../networknt/schema/CollectorContext.java | 4 +--
.../networknt/schema/DynamicRefValidator.java | 5 ++--
.../java/com/networknt/schema/JsonSchema.java | 26 +++++++++++--------
3 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/src/main/java/com/networknt/schema/CollectorContext.java b/src/main/java/com/networknt/schema/CollectorContext.java
index 36647ef41..07b0c6800 100644
--- a/src/main/java/com/networknt/schema/CollectorContext.java
+++ b/src/main/java/com/networknt/schema/CollectorContext.java
@@ -100,12 +100,12 @@ public JsonSchema getOutermostSchema() {
}
JsonSchema lexicalRoot = context.findLexicalRoot();
- if (lexicalRoot.isDynamicAnchor()) {
+ if (lexicalRoot.isRecursiveAnchor()) {
Iterator it = this.dynamicScopes.descendingIterator();
while (it.hasNext()) {
Scope scope = it.next();
JsonSchema containingSchema = scope.getContainingSchema();
- if (null != containingSchema && containingSchema.isDynamicAnchor()) {
+ if (null != containingSchema && containingSchema.isRecursiveAnchor()) {
return containingSchema;
}
}
diff --git a/src/main/java/com/networknt/schema/DynamicRefValidator.java b/src/main/java/com/networknt/schema/DynamicRefValidator.java
index 91eb1dd47..f1f0fd742 100644
--- a/src/main/java/com/networknt/schema/DynamicRefValidator.java
+++ b/src/main/java/com/networknt/schema/DynamicRefValidator.java
@@ -58,8 +58,9 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
String absoluteIri = ref.substring(0, index);
while (base.getEvaluationParentSchema() != null) {
base = base.getEvaluationParentSchema();
- if (!base.getSchemaLocation().getAbsoluteIri().toString().equals(absoluteIri)) {
- absoluteIri = base.getSchemaLocation().getAbsoluteIri().toString();
+ String baseAbsoluteIri = base.getSchemaLocation().getAbsoluteIri() != null ? base.getSchemaLocation().getAbsoluteIri().toString() : "";
+ if (!baseAbsoluteIri.equals(absoluteIri)) {
+ absoluteIri = baseAbsoluteIri;
String parentRef = SchemaLocation.resolve(base.getSchemaLocation(), anchor);
JsonSchema parentRefSchema = validationContext.getDynamicAnchors().get(parentRef);
if (parentRefSchema != null) {
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index d19295479..94a92848f 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -41,7 +41,7 @@ public class JsonSchema extends BaseJsonValidator {
private List validators;
private final JsonMetaSchema metaSchema;
private boolean validatorsLoaded = false;
- private boolean dynamicAnchor = false;
+ private boolean recursiveAnchor = false;
private JsonValidator requiredValidator = null;
private TypeValidator typeValidator;
@@ -49,7 +49,6 @@ public class JsonSchema extends BaseJsonValidator {
WalkListenerRunner keywordWalkListenerRunner = null;
private final String id;
- private final String anchor;
static JsonSchema from(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
return new JsonSchema(validationContext, schemaLocation, evaluationPath, schemaNode, parent, suppressSubSchemaRetrieval);
@@ -62,19 +61,25 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc
this.metaSchema = this.validationContext.getMetaSchema();
initializeConfig();
this.id = this.validationContext.resolveSchemaId(this.schemaNode);
- this.anchor = this.validationContext.getMetaSchema().readAnchor(this.schemaNode);
if (this.id != null) {
this.validationContext.getSchemaResources()
.putIfAbsent(this.schemaLocation != null ? this.schemaLocation.toString() : this.id, this);
}
- if (this.anchor != null) {
+ String anchor = this.validationContext.getMetaSchema().readAnchor(this.schemaNode);
+ if (anchor != null) {
+ String absoluteIri = this.schemaLocation.getAbsoluteIri() != null
+ ? this.schemaLocation.getAbsoluteIri().toString()
+ : "";
this.validationContext.getSchemaResources()
- .putIfAbsent(this.schemaLocation.getAbsoluteIri().toString() + "#" + anchor, this);
+ .putIfAbsent(absoluteIri + "#" + anchor, this);
}
String dynamicAnchor = this.validationContext.getMetaSchema().readDynamicAnchor(schemaNode);
if (dynamicAnchor != null) {
+ String absoluteIri = this.schemaLocation.getAbsoluteIri() != null
+ ? this.schemaLocation.getAbsoluteIri().toString()
+ : "";
this.validationContext.getDynamicAnchors()
- .putIfAbsent(this.schemaLocation.getAbsoluteIri().toString() + "#" + dynamicAnchor, this);
+ .putIfAbsent(absoluteIri + "#" + dynamicAnchor, this);
}
getValidators();
}
@@ -96,12 +101,11 @@ protected JsonSchema(JsonSchema copy) {
this.validators = copy.validators;
this.metaSchema = copy.metaSchema;
this.validatorsLoaded = copy.validatorsLoaded;
- this.dynamicAnchor = copy.dynamicAnchor;
+ this.recursiveAnchor = copy.recursiveAnchor;
this.requiredValidator = copy.requiredValidator;
this.typeValidator = copy.typeValidator;
this.keywordWalkListenerRunner = copy.keywordWalkListenerRunner;
this.id = copy.id;
- this.anchor = copy.anchor;
}
/**
@@ -356,7 +360,7 @@ private List read(JsonNode schemaNode) {
.build();
throw new JsonSchemaException(validationMessage);
}
- this.dynamicAnchor = nodeToUse.booleanValue();
+ this.recursiveAnchor = nodeToUse.booleanValue();
}
JsonValidator validator = this.validationContext.newValidator(schemaPath, path,
@@ -717,8 +721,8 @@ public void initializeValidators() {
}
}
- public boolean isDynamicAnchor() {
- return this.dynamicAnchor;
+ public boolean isRecursiveAnchor() {
+ return this.recursiveAnchor;
}
/**
From d5a48983b6fbaa13c9f16f919cc4f5b492374854 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 17:29:36 +0800
Subject: [PATCH 19/65] Fix
---
.../java/com/networknt/schema/uri/DefaultSchemaLoader.java | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
index 4707a9bab..5af7961ce 100644
--- a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
@@ -35,15 +35,16 @@ public DefaultSchemaLoader(List schemaLoaders, List
Date: Mon, 22 Jan 2024 17:30:02 +0800
Subject: [PATCH 20/65] Support custom meta schema
---
.../networknt/schema/JsonSchemaFactory.java | 22 +++++++++++--------
.../networknt/schema/SpecVersionDetector.java | 16 +++++++++-----
.../schema/AbstractJsonSchemaTest.java | 2 +-
.../schema/AbstractJsonSchemaTestSuite.java | 4 ++--
.../schema/JsonSchemaTestSuiteTest.java | 2 +-
.../schema/SpecVersionDetectorTest.java | 2 +-
6 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index d7a3f6c4b..79cb192d5 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -211,7 +211,7 @@ private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation
}
private ValidationContext withMetaSchema(ValidationContext validationContext, JsonNode schemaNode) {
- JsonMetaSchema metaSchema = getMetaSchema(schemaNode);
+ JsonMetaSchema metaSchema = getMetaSchema(schemaNode, validationContext.getConfig());
if (metaSchema != null && !metaSchema.getUri().equals(validationContext.getMetaSchema().getUri())) {
return new ValidationContext(metaSchema, validationContext.getJsonSchemaFactory(),
validationContext.getConfig(), validationContext.getSchemaReferences(),
@@ -238,34 +238,38 @@ protected SchemaLocation getSchemaLocation(SchemaLocation schemaRetrievalUri, Js
}
protected ValidationContext createValidationContext(final JsonNode schemaNode, SchemaValidatorsConfig config) {
- final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
+ final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode, config);
return new ValidationContext(jsonMetaSchema, this, config);
}
- private JsonMetaSchema getMetaSchema(final JsonNode schemaNode) {
+ private JsonMetaSchema getMetaSchema(final JsonNode schemaNode, SchemaValidatorsConfig config) {
final JsonNode uriNode = schemaNode.get("$schema");
if (uriNode != null && uriNode.isTextual()) {
- return jsonMetaSchemas.computeIfAbsent(normalizeMetaSchemaUri(uriNode.textValue()), this::fromId);
+ return jsonMetaSchemas.computeIfAbsent(normalizeMetaSchemaUri(uriNode.textValue()), id -> fromId(id, config));
}
return null;
}
- private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
+ private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode, SchemaValidatorsConfig config) {
final JsonNode uriNode = schemaNode.get("$schema");
if (uriNode != null && !uriNode.isNull() && !uriNode.isTextual()) {
throw new JsonSchemaException("Unknown MetaSchema: " + uriNode.toString());
}
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue());
- final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.computeIfAbsent(uri, this::fromId);
+ final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.computeIfAbsent(uri, id -> fromId(id, config));
return jsonMetaSchema;
}
- private JsonMetaSchema fromId(String id) {
+ private JsonMetaSchema fromId(String id, SchemaValidatorsConfig config) {
// Is it a well-known dialect?
return SpecVersionDetector.detectOptionalVersion(id)
.map(JsonSchemaFactory::checkVersion)
.map(JsonSchemaVersion::getInstance)
- .orElseThrow(() -> new JsonSchemaException("Unknown MetaSchema: " + id));
+ .orElseGet(() -> {
+ // Custom meta schema
+ JsonSchema schema = getSchema(SchemaLocation.of(id), config);
+ return schema.getValidationContext().getMetaSchema();
+ });
}
public JsonSchema getSchema(final String schema, final SchemaValidatorsConfig config) {
@@ -318,7 +322,7 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
schemaNode = jsonMapper.readTree(inputStream);
}
- final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
+ final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode, config);
JsonNodePath evaluationPath = new JsonNodePath(config.getPathType());
JsonSchema jsonSchema;
SchemaLocation schemaLocation = SchemaLocation.of(schemaUri.toString());
diff --git a/src/main/java/com/networknt/schema/SpecVersionDetector.java b/src/main/java/com/networknt/schema/SpecVersionDetector.java
index 43e80897a..118fe0669 100644
--- a/src/main/java/com/networknt/schema/SpecVersionDetector.java
+++ b/src/main/java/com/networknt/schema/SpecVersionDetector.java
@@ -58,7 +58,7 @@ private SpecVersionDetector() {
* @return Spec version if present, otherwise throws an exception
*/
public static VersionFlag detect(JsonNode jsonNode) {
- return detectOptionalVersion(jsonNode).orElseThrow(
+ return detectOptionalVersion(jsonNode, true).orElseThrow(
() -> new JsonSchemaException("'" + SCHEMA_TAG + "' tag is not present")
);
}
@@ -70,23 +70,27 @@ public static VersionFlag detect(JsonNode jsonNode) {
* @param jsonNode JSON Node to read from
* @return Spec version if present, otherwise empty
*/
- public static Optional detectOptionalVersion(JsonNode jsonNode) {
+ public static Optional detectOptionalVersion(JsonNode jsonNode, boolean throwIfUnsupported) {
return Optional.ofNullable(jsonNode.get(SCHEMA_TAG)).map(schemaTag -> {
String schemaTagValue = schemaTag.asText();
String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(schemaTagValue);
- return VersionFlag.fromId(schemaUri)
- .orElseThrow(() -> new JsonSchemaException("'" + schemaTagValue + "' is unrecognizable schema"));
+ if (throwIfUnsupported) {
+ return VersionFlag.fromId(schemaUri)
+ .orElseThrow(() -> new JsonSchemaException("'" + schemaTagValue + "' is unrecognizable schema"));
+ } else {
+ return VersionFlag.fromId(schemaUri).orElse(null);
+ }
});
}
// For 2019-09 and later published drafts, implementations that are able to
// detect the draft of each schema via $schema SHOULD be configured to do so
- public static VersionFlag detectVersion(JsonNode jsonNode, Path specification, VersionFlag defaultVersion) {
+ public static VersionFlag detectVersion(JsonNode jsonNode, Path specification, VersionFlag defaultVersion, boolean throwIfUnsupported) {
return Stream.of(
- detectOptionalVersion(jsonNode),
+ detectOptionalVersion(jsonNode, throwIfUnsupported),
detectVersionFromPath(specification)
)
.filter(Optional::isPresent)
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
index 44d5bd1ee..fc718acf9 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
@@ -62,7 +62,7 @@ private JsonNode getJsonNodeFromPath(String dataPath) {
private JsonSchema getJsonSchema(JsonNode schemaNode) {
return JsonSchemaFactory
- .getInstance(SpecVersionDetector.detectOptionalVersion(schemaNode).orElse(DEFAULT_VERSION_FLAG))
+ .getInstance(SpecVersionDetector.detectOptionalVersion(schemaNode, false).orElse(DEFAULT_VERSION_FLAG))
.getSchema(schemaNode);
}
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 524c8b12f..5a33479fa 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -178,7 +178,7 @@ private DynamicNode buildContainer(VersionFlag defaultVersion, TestCase testCase
private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, TestCase testCase) {
if (testCase.isDisabled()) return null;
- VersionFlag specVersion = detectVersion(testCase.getSchema(), testCase.getSpecification(), defaultVersion);
+ VersionFlag specVersion = detectVersion(testCase.getSchema(), testCase.getSpecification(), defaultVersion, false);
JsonSchemaFactory base = JsonSchemaFactory.getInstance(specVersion);
return JsonSchemaFactory
.builder(base)
@@ -216,7 +216,7 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS
}
SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));
- JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testSpec.getTestCase().getSchema(), config);
+ JsonSchema schema = validatorFactory.getSchema(/*testCaseFileUri, */testSpec.getTestCase().getSchema(), config);
return dynamicTest(testSpec.getDescription(), () -> executeAndReset(schema, testSpec));
}
diff --git a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
index 0c09ff168..a1a398711 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java
@@ -69,7 +69,7 @@ protected Optional reason(Path path) {
}
private void disableV202012Tests() {
- this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"), "Unsupported behavior");
+ //this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"), "Unsupported behavior");
}
diff --git a/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java b/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java
index 13a689f1e..2c7804242 100644
--- a/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java
+++ b/src/test/java/com/networknt/schema/SpecVersionDetectorTest.java
@@ -50,7 +50,7 @@ void detectOptionalSpecVersion() throws IOException {
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(
"data/schemaTagMissing.json");
JsonNode node = mapper.readTree(in);
- Optional flag = SpecVersionDetector.detectOptionalVersion(node);
+ Optional flag = SpecVersionDetector.detectOptionalVersion(node, true);
assertEquals(Optional.empty(), flag);
}
}
From 339e096a2d703a503b819564efcc544b759ee612 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 19:05:00 +0800
Subject: [PATCH 21/65] Refactor
---
.../com/networknt/schema/JsonMetaSchema.java | 41 +++++++++++++++++--
.../networknt/schema/JsonSchemaFactory.java | 18 +++++++-
.../com/networknt/schema/Version201909.java | 7 ++++
.../com/networknt/schema/Version202012.java | 8 ++++
.../java/com/networknt/schema/Version4.java | 1 +
.../java/com/networknt/schema/Version6.java | 1 +
.../java/com/networknt/schema/Version7.java | 1 +
.../schema/AbstractJsonSchemaTestSuite.java | 2 +-
8 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index 037b7551c..0f2d8905a 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -17,6 +17,7 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.format.DateFormat;
import com.networknt.schema.format.EmailFormat;
import com.networknt.schema.format.IdnEmailFormat;
@@ -81,8 +82,10 @@ static PatternFormat pattern(String name, String regex) {
}
public static class Builder {
+ private VersionFlag specification = null;
private Map keywords = new HashMap<>();
private Map formats = new HashMap<>();
+ private Map vocabularies = new HashMap<>();
private String uri;
private String idKeyword = "id";
@@ -133,6 +136,24 @@ public Builder addFormats(Collection extends Format> formats) {
return this;
}
+ public Builder vocabulary(String vocabulary) {
+ return vocabulary(vocabulary, true);
+ }
+
+ public Builder vocabulary(String vocabulary, boolean enabled) {
+ this.vocabularies.put(vocabulary, enabled);
+ return this;
+ }
+
+ public Builder vocabularies(Map vocabularies) {
+ this.vocabularies = vocabularies;
+ return this;
+ }
+
+ public Builder specification(VersionFlag specification) {
+ this.specification = specification;
+ return this;
+ }
public Builder idKeyword(String idKeyword) {
this.idKeyword = idKeyword;
@@ -142,15 +163,17 @@ public Builder idKeyword(String idKeyword) {
public JsonMetaSchema build() {
// create builtin keywords with (custom) formats.
Map kwords = createKeywordsMap(this.keywords, this.formats);
- return new JsonMetaSchema(this.uri, this.idKeyword, kwords);
+ return new JsonMetaSchema(this.uri, this.idKeyword, kwords, this.vocabularies, this.specification);
}
}
private final String uri;
private final String idKeyword;
private Map keywords;
+ private Map vocabularies;
+ private final VersionFlag specification;
- JsonMetaSchema(String uri, String idKeyword, Map keywords) {
+ JsonMetaSchema(String uri, String idKeyword, Map keywords, Map vocabularies, VersionFlag specification) {
if (StringUtils.isBlank(uri)) {
throw new IllegalArgumentException("uri must not be null or blank");
}
@@ -164,6 +187,7 @@ public JsonMetaSchema build() {
this.uri = uri;
this.idKeyword = idKeyword;
this.keywords = keywords;
+ this.specification = specification;
}
public static JsonMetaSchema getV4() {
@@ -215,7 +239,10 @@ public static Builder builder(String uri, JsonMetaSchema blueprint) {
return builder(uri)
.idKeyword(blueprint.idKeyword)
.addKeywords(blueprint.keywords.values())
- .addFormats(formatKeyword.getFormats());
+ .addFormats(formatKeyword.getFormats())
+ .specification(blueprint.getSpecification())
+ .vocabularies(blueprint.getVocabularies())
+ ;
}
public String getIdKeyword() {
@@ -258,6 +285,14 @@ public Map getKeywords() {
return this.keywords;
}
+ public Map getVocabularies() {
+ return this.vocabularies;
+ }
+
+ public VersionFlag getSpecification() {
+ return this.specification;
+ }
+
public JsonValidator newValidator(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, String keyword /* keyword */, JsonNode schemaNode,
JsonSchema parentSchema) {
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 79cb192d5..b443a5413 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.uri.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,6 +30,7 @@
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
@@ -268,7 +270,21 @@ private JsonMetaSchema fromId(String id, SchemaValidatorsConfig config) {
.orElseGet(() -> {
// Custom meta schema
JsonSchema schema = getSchema(SchemaLocation.of(id), config);
- return schema.getValidationContext().getMetaSchema();
+ JsonMetaSchema.Builder builder = JsonMetaSchema.builder(id, schema.getValidationContext().getMetaSchema());
+ VersionFlag specification = schema.getValidationContext().getMetaSchema().getSpecification();
+ if (specification != null) {
+ if (specification.getVersionFlagValue() >= VersionFlag.V201909.getVersionFlagValue()) {
+ // Process vocabularies
+ JsonNode vocabulary = schema.getSchemaNode().get("$vocabulary");
+ if (vocabulary != null) {
+ for(Entry vocabs : vocabulary.properties()) {
+ builder.vocabulary(vocabs.getKey(), vocabs.getValue().booleanValue());
+ }
+ }
+
+ }
+ }
+ return builder.build();
});
}
diff --git a/src/main/java/com/networknt/schema/Version201909.java b/src/main/java/com/networknt/schema/Version201909.java
index 6410bd994..75b22ef06 100644
--- a/src/main/java/com/networknt/schema/Version201909.java
+++ b/src/main/java/com/networknt/schema/Version201909.java
@@ -13,6 +13,7 @@ public class Version201909 extends JsonSchemaVersion{
@Override
public JsonMetaSchema getInstance() {
return new JsonMetaSchema.Builder(URI)
+ .specification(SpecVersion.VersionFlag.V201909)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V201909))
@@ -37,6 +38,12 @@ public JsonMetaSchema getInstance() {
new NonValidationKeyword("then"),
new NonValidationKeyword("else")
))
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/core")
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/applicator")
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/validation")
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/meta-data")
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/format", false)
+ .vocabulary("https://json-schema.org/draft/2019-09/vocab/content")
.build();
}
}
diff --git a/src/main/java/com/networknt/schema/Version202012.java b/src/main/java/com/networknt/schema/Version202012.java
index 3d07de784..8cba2a48f 100644
--- a/src/main/java/com/networknt/schema/Version202012.java
+++ b/src/main/java/com/networknt/schema/Version202012.java
@@ -14,6 +14,7 @@ public class Version202012 extends JsonSchemaVersion {
@Override
public JsonMetaSchema getInstance() {
return new JsonMetaSchema.Builder(URI)
+ .specification(SpecVersion.VersionFlag.V202012)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V202012))
@@ -37,6 +38,13 @@ public JsonMetaSchema getInstance() {
new NonValidationKeyword("else"),
new NonValidationKeyword("additionalItems")
))
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/core")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/applicator")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/unevaluated")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/validation")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/meta-data")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/format-annotation")
+ .vocabulary("https://json-schema.org/draft/2020-12/vocab/content")
.build();
}
}
diff --git a/src/main/java/com/networknt/schema/Version4.java b/src/main/java/com/networknt/schema/Version4.java
index 5763118d9..248dad904 100644
--- a/src/main/java/com/networknt/schema/Version4.java
+++ b/src/main/java/com/networknt/schema/Version4.java
@@ -13,6 +13,7 @@ public class Version4 extends JsonSchemaVersion{
public JsonMetaSchema getInstance() {
return new JsonMetaSchema.Builder(URI)
+ .specification(SpecVersion.VersionFlag.V4)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V4))
diff --git a/src/main/java/com/networknt/schema/Version6.java b/src/main/java/com/networknt/schema/Version6.java
index c9243bac1..4459bb889 100644
--- a/src/main/java/com/networknt/schema/Version6.java
+++ b/src/main/java/com/networknt/schema/Version6.java
@@ -14,6 +14,7 @@ public class Version6 extends JsonSchemaVersion{
public JsonMetaSchema getInstance() {
return new JsonMetaSchema.Builder(URI)
+ .specification(SpecVersion.VersionFlag.V6)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V6))
diff --git a/src/main/java/com/networknt/schema/Version7.java b/src/main/java/com/networknt/schema/Version7.java
index 51318fc69..e8b24efc8 100644
--- a/src/main/java/com/networknt/schema/Version7.java
+++ b/src/main/java/com/networknt/schema/Version7.java
@@ -13,6 +13,7 @@ public class Version7 extends JsonSchemaVersion{
@Override
public JsonMetaSchema getInstance() {
return new JsonMetaSchema.Builder(URI)
+ .specification(SpecVersion.VersionFlag.V7)
.idKeyword(ID)
.addFormats(BUILTIN_FORMATS)
.addKeywords(ValidatorTypeCode.getNonFormatKeywords(SpecVersion.VersionFlag.V7))
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 5a33479fa..37bbd9527 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -216,7 +216,7 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS
}
SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification()));
- JsonSchema schema = validatorFactory.getSchema(/*testCaseFileUri, */testSpec.getTestCase().getSchema(), config);
+ JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testSpec.getTestCase().getSchema(), config);
return dynamicTest(testSpec.getDescription(), () -> executeAndReset(schema, testSpec));
}
From b2482bc281ca77169106fb7060b8e8b2922b10e9 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 20:33:30 +0800
Subject: [PATCH 22/65] Support format assertion configuration
---
.../com/networknt/schema/ExecutionConfig.java | 25 ++++++
.../com/networknt/schema/FormatValidator.java | 30 +++++---
.../com/networknt/schema/JsonMetaSchema.java | 4 +-
.../format/BaseFormatJsonValidator.java | 48 ++++++++++++
.../schema/format/DateTimeValidator.java | 13 ++--
.../schema/AbstractJsonSchemaTestSuite.java | 7 +-
.../schema/DurationFormatValidatorTest.java | 4 +-
.../com/networknt/schema/Issue575Test.java | 4 +-
.../schema/OverrideValidatorTest.java | 4 +-
src/test/suite/tests/draft2020-12/format.json | 76 +++++--------------
10 files changed, 136 insertions(+), 79 deletions(-)
create mode 100644 src/main/java/com/networknt/schema/format/BaseFormatJsonValidator.java
diff --git a/src/main/java/com/networknt/schema/ExecutionConfig.java b/src/main/java/com/networknt/schema/ExecutionConfig.java
index 81ac03d0e..a7de8ed48 100644
--- a/src/main/java/com/networknt/schema/ExecutionConfig.java
+++ b/src/main/java/com/networknt/schema/ExecutionConfig.java
@@ -26,6 +26,7 @@
public class ExecutionConfig {
private Locale locale = Locale.ROOT;
private Predicate annotationAllowedPredicate = (keyword) -> true;
+ private Boolean formatAssertionsEnabled = null;
public Locale getLocale() {
return locale;
@@ -84,4 +85,28 @@ public void setAnnotationAllowedPredicate(Predicate annotationAllowedPre
this.annotationAllowedPredicate = Objects.requireNonNull(annotationAllowedPredicate,
"annotationAllowedPredicate must not be null");
}
+
+ /**
+ * Gets the format assertion enabled flag.
+ *
+ * This defaults to null meaning that it will follow the defaults of the
+ * specification.
+ *
+ * Since draft 2019-09 this will default to false unless enabled by using the
+ * $vocabulary keyword.
+ *
+ * @return the format assertions enabled flag
+ */
+ public Boolean getFormatAssertionsEnabled() {
+ return formatAssertionsEnabled;
+ }
+
+ /**
+ * Sets the format assertion enabled flag.
+ *
+ * @param formatAssertionsEnabled the format assertions enabled flag
+ */
+ public void setFormatAssertionsEnabled(Boolean formatAssertionsEnabled) {
+ this.formatAssertionsEnabled = formatAssertionsEnabled;
+ }
}
diff --git a/src/main/java/com/networknt/schema/FormatValidator.java b/src/main/java/com/networknt/schema/FormatValidator.java
index 14d005780..cb3cb1f58 100644
--- a/src/main/java/com/networknt/schema/FormatValidator.java
+++ b/src/main/java/com/networknt/schema/FormatValidator.java
@@ -17,6 +17,8 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.format.BaseFormatJsonValidator;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -25,7 +27,7 @@
import java.util.Set;
import java.util.regex.PatternSyntaxException;
-public class FormatValidator extends BaseJsonValidator implements JsonValidator {
+public class FormatValidator extends BaseFormatJsonValidator implements JsonValidator {
private static final Logger logger = LoggerFactory.getLogger(FormatValidator.class);
private final Format format;
@@ -33,7 +35,6 @@ public class FormatValidator extends BaseJsonValidator implements JsonValidator
public FormatValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, Format format, ValidatorTypeCode type) {
super(schemaLocation, evaluationPath, schemaNode, parentSchema, type, validationContext);
this.format = format;
- this.validationContext = validationContext;
}
public Set validate(ExecutionContext executionContext, JsonNode node, JsonNode rootNode, JsonNodePath instanceLocation) {
@@ -44,26 +45,33 @@ public Set validate(ExecutionContext executionContext, JsonNo
return Collections.emptySet();
}
+ boolean assertionsEnabled = isAssertionsEnabled(executionContext);
Set errors = new LinkedHashSet<>();
if (format != null) {
if(format.getName().equals("ipv6")) {
if(!node.textValue().trim().equals(node.textValue())) {
- // leading and trailing spaces
- errors.add(message().instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
- .arguments(format.getName(), format.getErrorMessageDescription()).build());
+ if (assertionsEnabled) {
+ // leading and trailing spaces
+ errors.add(message().instanceLocation(instanceLocation)
+ .locale(executionContext.getExecutionConfig().getLocale())
+ .arguments(format.getName(), format.getErrorMessageDescription()).build());
+ }
} else if(node.textValue().contains("%")) {
- // zone id is not part of the ipv6
- errors.add(message().instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale())
- .arguments(format.getName(), format.getErrorMessageDescription()).build());
+ if (assertionsEnabled) {
+ // zone id is not part of the ipv6
+ errors.add(message().instanceLocation(instanceLocation)
+ .locale(executionContext.getExecutionConfig().getLocale())
+ .arguments(format.getName(), format.getErrorMessageDescription()).build());
+ }
}
}
try {
if (!format.matches(executionContext, node.textValue())) {
- errors.add(message().instanceLocation(instanceLocation)
+ if (assertionsEnabled) {
+ errors.add(message().instanceLocation(instanceLocation)
.locale(executionContext.getExecutionConfig().getLocale())
.arguments(format.getName(), format.getErrorMessageDescription()).build());
+ }
}
} catch (PatternSyntaxException pse) {
// String is considered valid if pattern is invalid
diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java
index 0f2d8905a..c31706512 100644
--- a/src/main/java/com/networknt/schema/JsonMetaSchema.java
+++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java
@@ -188,6 +188,7 @@ public JsonMetaSchema build() {
this.idKeyword = idKeyword;
this.keywords = keywords;
this.specification = specification;
+ this.vocabularies = vocabularies;
}
public static JsonMetaSchema getV4() {
@@ -236,12 +237,13 @@ public static Builder builder(String uri, JsonMetaSchema blueprint) {
if (formatKeyword == null) {
throw new IllegalArgumentException("The formatKeyword did not exist - blueprint is invalid.");
}
+ Map vocabularies = new HashMap<>(blueprint.getVocabularies());
return builder(uri)
.idKeyword(blueprint.idKeyword)
.addKeywords(blueprint.keywords.values())
.addFormats(formatKeyword.getFormats())
.specification(blueprint.getSpecification())
- .vocabularies(blueprint.getVocabularies())
+ .vocabularies(vocabularies)
;
}
diff --git a/src/main/java/com/networknt/schema/format/BaseFormatJsonValidator.java b/src/main/java/com/networknt/schema/format/BaseFormatJsonValidator.java
new file mode 100644
index 000000000..948729502
--- /dev/null
+++ b/src/main/java/com/networknt/schema/format/BaseFormatJsonValidator.java
@@ -0,0 +1,48 @@
+package com.networknt.schema.format;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.BaseJsonValidator;
+import com.networknt.schema.ExecutionContext;
+import com.networknt.schema.JsonNodePath;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.ValidationContext;
+import com.networknt.schema.ValidatorTypeCode;
+import com.networknt.schema.SpecVersion.VersionFlag;
+
+public abstract class BaseFormatJsonValidator extends BaseJsonValidator {
+ protected final boolean assertionsEnabled;
+
+ public BaseFormatJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode,
+ JsonSchema parentSchema, ValidatorTypeCode validatorType, ValidationContext validationContext) {
+ super(schemaLocation, evaluationPath, schemaNode, parentSchema, validatorType,validationContext);
+ VersionFlag specification = this.validationContext.getMetaSchema().getSpecification();
+ if (specification == null || specification.getVersionFlagValue() < VersionFlag.V201909.getVersionFlagValue()) {
+ assertionsEnabled = true;
+ } else {
+ // Check vocabulary
+ assertionsEnabled = isFormatAssertionVocabularyEnabled(specification,
+ this.validationContext.getMetaSchema().getVocabularies());
+ }
+ }
+
+ protected boolean isFormatAssertionVocabularyEnabled(VersionFlag specification, Map vocabularies) {
+ if (VersionFlag.V202012.equals(specification)) {
+ String vocabulary = "https://json-schema.org/draft/2020-12/vocab/format-assertion";
+ return vocabularies.containsKey(vocabulary); // doesn't matter if it is true or false
+ } else if (VersionFlag.V201909.equals(specification)) {
+ String vocabulary = "https://json-schema.org/draft/2019-09/vocab/format";
+ return vocabularies.getOrDefault(vocabulary, false);
+ }
+ return false;
+ }
+
+ protected boolean isAssertionsEnabled(ExecutionContext executionContext) {
+ if (Boolean.TRUE.equals(executionContext.getExecutionConfig().getFormatAssertionsEnabled())) {
+ return true;
+ }
+ return this.assertionsEnabled;
+ }
+}
diff --git a/src/main/java/com/networknt/schema/format/DateTimeValidator.java b/src/main/java/com/networknt/schema/format/DateTimeValidator.java
index 5ac8352c9..d3daea685 100644
--- a/src/main/java/com/networknt/schema/format/DateTimeValidator.java
+++ b/src/main/java/com/networknt/schema/format/DateTimeValidator.java
@@ -19,7 +19,6 @@
import com.ethlo.time.ITU;
import com.ethlo.time.LeapSecondException;
import com.fasterxml.jackson.databind.JsonNode;
-import com.networknt.schema.BaseJsonValidator;
import com.networknt.schema.ExecutionContext;
import com.networknt.schema.JsonNodePath;
import com.networknt.schema.JsonSchema;
@@ -36,7 +35,7 @@
import java.util.Collections;
import java.util.Set;
-public class DateTimeValidator extends BaseJsonValidator {
+public class DateTimeValidator extends BaseFormatJsonValidator {
private static final Logger logger = LoggerFactory.getLogger(DateTimeValidator.class);
private static final String DATETIME = "date-time";
@@ -53,10 +52,14 @@ public Set validate(ExecutionContext executionContext, JsonNo
if (nodeType != JsonType.STRING) {
return Collections.emptySet();
}
+ boolean assertionsEnabled = isAssertionsEnabled(executionContext);
+
if (!isLegalDateTime(node.textValue())) {
- return Collections.singleton(message().instanceLocation(instanceLocation)
- .locale(executionContext.getExecutionConfig().getLocale()).arguments(node.textValue(), DATETIME)
- .build());
+ if (assertionsEnabled) {
+ return Collections.singleton(message().instanceLocation(instanceLocation)
+ .locale(executionContext.getExecutionConfig().getLocale()).arguments(node.textValue(), DATETIME)
+ .build());
+ }
}
return Collections.emptySet();
}
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 37bbd9527..66c471260 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -53,8 +53,11 @@ private static String toForwardSlashPath(Path file) {
}
private static void executeTest(JsonSchema schema, TestSpec testSpec) {
-
- Set errors = schema.validate(testSpec.getData());
+ Set errors = schema.validate(testSpec.getData(), OutputFormat.DEFAULT, (executionContext, validationContext) -> {
+ if (testSpec.getTestCase().getSource().getPath().getParent().toString().endsWith("format")) {
+ executionContext.getExecutionConfig().setFormatAssertionsEnabled(true);
+ }
+ });
if (testSpec.isValid()) {
if (!errors.isEmpty()) {
diff --git a/src/test/java/com/networknt/schema/DurationFormatValidatorTest.java b/src/test/java/com/networknt/schema/DurationFormatValidatorTest.java
index df85b3454..bcf80d342 100644
--- a/src/test/java/com/networknt/schema/DurationFormatValidatorTest.java
+++ b/src/test/java/com/networknt/schema/DurationFormatValidatorTest.java
@@ -41,7 +41,9 @@ public void durationFormatValidatorTest() throws JsonProcessingException, IOExce
Set messages = validatorSchema.validate(validTargetNode);
assertEquals(0, messages.size());
- messages = validatorSchema.validate(invalidTargetNode);
+ messages = validatorSchema.validate(invalidTargetNode, OutputFormat.DEFAULT, (executionContext, validationContext) -> {
+ executionContext.getExecutionConfig().setFormatAssertionsEnabled(true);
+ });
assertEquals(1, messages.size());
}
diff --git a/src/test/java/com/networknt/schema/Issue575Test.java b/src/test/java/com/networknt/schema/Issue575Test.java
index ff3c221a4..dd22d379c 100644
--- a/src/test/java/com/networknt/schema/Issue575Test.java
+++ b/src/test/java/com/networknt/schema/Issue575Test.java
@@ -121,7 +121,9 @@ public static Stream invalidTimeRepresentations() {
@ParameterizedTest
@MethodSource("invalidTimeRepresentations")
void testInvalidTimeRepresentations(String jsonObject) throws JsonProcessingException {
- Set errors = schema.validate(new ObjectMapper().readTree(jsonObject));
+ Set errors = schema.validate(new ObjectMapper().readTree(jsonObject), OutputFormat.DEFAULT, (executionContext, validationContext) -> {
+ executionContext.getExecutionConfig().setFormatAssertionsEnabled(true);
+ });
Assertions.assertFalse(errors.isEmpty());
}
}
diff --git a/src/test/java/com/networknt/schema/OverrideValidatorTest.java b/src/test/java/com/networknt/schema/OverrideValidatorTest.java
index 3c57f77c9..ccedb0ab9 100644
--- a/src/test/java/com/networknt/schema/OverrideValidatorTest.java
+++ b/src/test/java/com/networknt/schema/OverrideValidatorTest.java
@@ -48,7 +48,9 @@ public void overrideDefaultValidator() throws JsonProcessingException, IOExcepti
final JsonSchemaFactory validatorFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).addMetaSchema(validatorMetaSchema).build();
final JsonSchema validatorSchema = validatorFactory.getSchema(schema);
- Set messages = validatorSchema.validate(targetNode);
+ Set messages = validatorSchema.validate(targetNode, OutputFormat.DEFAULT, (executionContext, validationContext) -> {
+ executionContext.getExecutionConfig().setFormatAssertionsEnabled(true);
+ });
assertEquals(1, messages.size());
// Override EmailValidator
diff --git a/src/test/suite/tests/draft2020-12/format.json b/src/test/suite/tests/draft2020-12/format.json
index 18dba2755..01adcbda3 100644
--- a/src/test/suite/tests/draft2020-12/format.json
+++ b/src/test/suite/tests/draft2020-12/format.json
@@ -39,9 +39,7 @@
{
"description": "invalid email string is only an annotation by default",
"data": "2962",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -85,9 +83,7 @@
{
"description": "invalid idn-email string is only an annotation by default",
"data": "2962",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -131,9 +127,7 @@
{
"description": "invalid regex string is only an annotation by default",
"data": "^(abc]",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -177,9 +171,7 @@
{
"description": "invalid ipv4 string is only an annotation by default",
"data": "127.0.0.0.1",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -223,9 +215,7 @@
{
"description": "invalid ipv6 string is only an annotation by default",
"data": "12345::",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -269,9 +259,7 @@
{
"description": "invalid idn-hostname string is only an annotation by default",
"data": "〮실례.테스트",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -315,9 +303,7 @@
{
"description": "invalid hostname string is only an annotation by default",
"data": "-a-host-name-that-starts-with--",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -361,9 +347,7 @@
{
"description": "invalid date string is only an annotation by default",
"data": "06/19/1963",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -407,9 +391,7 @@
{
"description": "invalid date-time string is only an annotation by default",
"data": "1990-02-31T15:59:60.123-08:00",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -453,9 +435,7 @@
{
"description": "invalid time string is only an annotation by default",
"data": "08:30:06 PST",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -499,9 +479,7 @@
{
"description": "invalid json-pointer string is only an annotation by default",
"data": "/foo/bar~",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -545,9 +523,7 @@
{
"description": "invalid relative-json-pointer string is only an annotation by default",
"data": "/foo/bar",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -591,9 +567,7 @@
{
"description": "invalid iri string is only an annotation by default",
"data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -637,9 +611,7 @@
{
"description": "invalid iri-reference string is only an annotation by default",
"data": "\\\\WINDOWS\\filëßåré",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -683,9 +655,7 @@
{
"description": "invalid uri string is only an annotation by default",
"data": "//foo.bar/?baz=qux#quux",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -729,9 +699,7 @@
{
"description": "invalid uri-reference string is only an annotation by default",
"data": "\\\\WINDOWS\\fileshare",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -775,9 +743,7 @@
{
"description": "invalid uri-template string is only an annotation by default",
"data": "http://example.com/dictionary/{term:1}/{term",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -821,9 +787,7 @@
{
"description": "invalid uuid string is only an annotation by default",
"data": "2eb8aa08-aa98-11ea-b4aa-73b441d1638",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
},
@@ -867,9 +831,7 @@
{
"description": "invalid duration string is only an annotation by default",
"data": "PT1D",
- "valid": true,
- "disabled": true,
- "reason": "TODO: Only the Format-Assertion vocabulary is currently supported"
+ "valid": true
}
]
}
From 4f8112b6ad5320975366745e62386d99373e2ba6 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Mon, 22 Jan 2024 21:14:14 +0800
Subject: [PATCH 23/65] Refactor
---
.../java/com/networknt/schema/JsonSchema.java | 43 +++++++++++++++++--
1 file changed, 40 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index 94a92848f..a2599daa2 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -26,6 +26,7 @@
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;
+import java.util.function.Consumer;
/**
* This is the core of json constraint implementation. It parses json constraint
@@ -501,7 +502,7 @@ public Set validate(ExecutionContext executionContext, JsonNo
/**
* Validate the given root JsonNode, starting at the root of the data path.
- * @param rootNode JsonNode
+ * @param rootNode the root node
*
* @return A list of ValidationMessage if there is any validation error, or an empty
* list if there is no error.
@@ -510,6 +511,26 @@ public Set validate(JsonNode rootNode) {
return validate(rootNode, OutputFormat.DEFAULT);
}
+ /**
+ * Validate the given root JsonNode, starting at the root of the data path.
+ * @param rootNode the root node
+ * @param executionCustomizer the execution customizer
+ * @return
+ */
+ public Set validate(JsonNode rootNode, ExecutionCustomizer executionCustomizer) {
+ return validate(rootNode, OutputFormat.DEFAULT, executionCustomizer);
+ }
+
+ /**
+ * Validate the given root JsonNode, starting at the root of the data path.
+ * @param rootNode the root node
+ * @param executionCustomizer the execution customizer
+ * @return
+ */
+ public Set validate(JsonNode rootNode, Consumer executionCustomizer) {
+ return validate(rootNode, OutputFormat.DEFAULT, executionCustomizer);
+ }
+
/**
* Validates the given root JsonNode, starting at the root of the data path. The
* output will be formatted using the formatter specified.
@@ -520,7 +541,7 @@ public Set validate(JsonNode rootNode) {
* @return the result
*/
public T validate(JsonNode rootNode, OutputFormat format) {
- return validate(rootNode, format, null);
+ return validate(rootNode, format, (ExecutionCustomizer) null);
}
/**
@@ -528,7 +549,7 @@ public T validate(JsonNode rootNode, OutputFormat format) {
* output will be formatted using the formatter specified.
*
* @param the result type
- * @param rootNode the root note
+ * @param rootNode the root node
* @param format the formatter
* @param executionCustomizer the execution customizer
* @return the result
@@ -537,6 +558,22 @@ public T validate(JsonNode rootNode, OutputFormat format, ExecutionCustom
return validate(createExecutionContext(), rootNode, format, executionCustomizer);
}
+ /**
+ * Validates the given root JsonNode, starting at the root of the data path. The
+ * output will be formatted using the formatter specified.
+ *
+ * @param the result type
+ * @param rootNode the root node
+ * @param format the formatter
+ * @param executionCustomizer the execution customizer
+ * @return the result
+ */
+ public T validate(JsonNode rootNode, OutputFormat format, Consumer executionCustomizer) {
+ return validate(createExecutionContext(), rootNode, format, (executionContext, validationContext) -> {
+ executionCustomizer.accept(executionContext);
+ });
+ }
+
public ValidationResult validateAndCollect(ExecutionContext executionContext, JsonNode node) {
return validateAndCollect(executionContext, node, node, atRoot());
}
From 31faa6bd5f4094aa5679f3f370a9d82dac14395c Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 23 Jan 2024 01:01:23 +0800
Subject: [PATCH 24/65] Refactor
---
.../networknt/schema/JsonSchemaFactory.java | 2 +-
.../schema/uri/ClasspathSchemaLoader.java | 8 +--
.../schema/uri/DefaultSchemaLoader.java | 25 ++++-----
.../schema/uri/MapAbsoluteIriMapper.java | 4 +-
.../schema/uri/PrefixAbsoluteIriMapper.java | 4 +-
.../networknt/schema/uri/SchemaLoader.java | 4 +-
.../schema/uri/SchemaLoaderBuilder.java | 12 ++---
.../networknt/schema/uri/SchemaLoaders.java | 50 ++++++++++++++++++
...soluteIriMapper.java => SchemaMapper.java} | 2 +-
.../networknt/schema/uri/SchemaMappers.java | 51 +++++++++++++++++++
.../networknt/schema/uri/UriSchemaLoader.java | 6 +--
.../com/networknt/schema/CustomUriTest.java | 2 +-
.../schema/JsonSchemaFactoryUriCacheTest.java | 14 ++---
.../com/networknt/schema/UriMappingTest.java | 4 +-
14 files changed, 141 insertions(+), 47 deletions(-)
create mode 100644 src/main/java/com/networknt/schema/uri/SchemaLoaders.java
rename src/main/java/com/networknt/schema/uri/{AbsoluteIriMapper.java => SchemaMapper.java} (92%)
create mode 100644 src/main/java/com/networknt/schema/uri/SchemaMappers.java
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index b443a5413..5f5e989c3 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -327,7 +327,7 @@ public JsonSchema getSchema(final SchemaLocation schemaUri, final SchemaValidato
}
protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValidatorsConfig config) {
- try (InputStream inputStream = this.schemaLoader.getSchema(schemaUri).getInputStream()) {
+ try (InputStream inputStream = this.schemaLoader.getSchema(schemaUri.getAbsoluteIri()).getInputStream()) {
if (inputStream == null) {
throw new IOException("Cannot load schema at " + schemaUri.toString());
}
diff --git a/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
index d357ba29f..8c7349bf9 100644
--- a/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/ClasspathSchemaLoader.java
@@ -17,7 +17,7 @@
import java.io.InputStream;
-import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.AbsoluteIri;
/**
* Loads from classpath.
@@ -25,15 +25,15 @@
public class ClasspathSchemaLoader implements SchemaLoader {
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
- String scheme = schemaLocation.getAbsoluteIri().getScheme();
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ String scheme = absoluteIri.getScheme();
if (scheme.startsWith("classpath") || scheme.startsWith("resource")) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = SchemaLoader.class.getClassLoader();
}
ClassLoader loader = classLoader;
- String name = schemaLocation.getAbsoluteIri().toString().substring(scheme.length() + 1);
+ String name = absoluteIri.toString().substring(scheme.length() + 1);
if (name.startsWith("//")) {
name = name.substring(2);
}
diff --git a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
index 5af7961ce..3ff132524 100644
--- a/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/DefaultSchemaLoader.java
@@ -18,37 +18,30 @@
import java.util.List;
import com.networknt.schema.AbsoluteIri;
-import com.networknt.schema.SchemaLocation;
/**
* Default {@link SchemaLoader}.
*/
public class DefaultSchemaLoader implements SchemaLoader {
private final List schemaLoaders;
- private final List absoluteIriMappers;
+ private final List schemaMappers;
- public DefaultSchemaLoader(List schemaLoaders, List absoluteIriMappers) {
+ public DefaultSchemaLoader(List schemaLoaders, List schemaMappers) {
this.schemaLoaders = schemaLoaders;
- this.absoluteIriMappers = absoluteIriMappers;
+ this.schemaMappers = schemaMappers;
}
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
- AbsoluteIri absoluteIri = schemaLocation.getAbsoluteIri();
- boolean modified = false;
- SchemaLocation mappedSchemaLocation = schemaLocation;
- for (AbsoluteIriMapper mapper : absoluteIriMappers) {
- AbsoluteIri mapped = mapper.map(absoluteIri);
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ AbsoluteIri mappedResult = absoluteIri;
+ for (SchemaMapper mapper : schemaMappers) {
+ AbsoluteIri mapped = mapper.map(mappedResult);
if (mapped != null) {
- absoluteIri = mapped;
- modified = true;
+ mappedResult = mapped;
}
}
- if (modified) {
- mappedSchemaLocation = new SchemaLocation(absoluteIri, schemaLocation.getFragment());
- }
for (SchemaLoader loader : schemaLoaders) {
- InputStreamSource result = loader.getSchema(mappedSchemaLocation);
+ InputStreamSource result = loader.getSchema(mappedResult);
if (result != null) {
return result;
}
diff --git a/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
index 3eaba98f5..53d443a92 100644
--- a/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
+++ b/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
@@ -5,9 +5,9 @@
import com.networknt.schema.AbsoluteIri;
/**
- * Map implementation of {@link AbsoluteIriMapper}.
+ * Map implementation of {@link SchemaMapper}.
*/
-public class MapAbsoluteIriMapper implements AbsoluteIriMapper {
+public class MapAbsoluteIriMapper implements SchemaMapper {
private final Map mappings;
public MapAbsoluteIriMapper(Map mappings) {
diff --git a/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
index e5cdc1708..7014a0d98 100644
--- a/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
+++ b/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
@@ -3,9 +3,9 @@
import com.networknt.schema.AbsoluteIri;
/**
- * Prefix implementation of {@link AbsoluteIriMapper}.
+ * Prefix implementation of {@link SchemaMapper}.
*/
-public class PrefixAbsoluteIriMapper implements AbsoluteIriMapper {
+public class PrefixAbsoluteIriMapper implements SchemaMapper {
private final String source;
private final String replacement;
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoader.java b/src/main/java/com/networknt/schema/uri/SchemaLoader.java
index f9e27d5a5..8f1cc7fa5 100644
--- a/src/main/java/com/networknt/schema/uri/SchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoader.java
@@ -15,12 +15,12 @@
*/
package com.networknt.schema.uri;
-import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.AbsoluteIri;
/**
* Loader for schema.
*/
@FunctionalInterface
public interface SchemaLoader {
- InputStreamSource getSchema(SchemaLocation schemaLocation);
+ InputStreamSource getSchema(AbsoluteIri absoluteIri);
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java b/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
index 973e972c4..cd6d5a1a2 100644
--- a/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
@@ -25,9 +25,9 @@
* Builder for {@link SchemaLoader}.
*/
public class SchemaLoaderBuilder {
- private BiFunction, List, SchemaLoader> schemaLoaderFactory = DefaultSchemaLoader::new;
+ private BiFunction, List, SchemaLoader> schemaLoaderFactory = DefaultSchemaLoader::new;
private List schemaLoaders = new ArrayList<>();
- private List absoluteIriMappers = new ArrayList<>();
+ private List absoluteIriMappers = new ArrayList<>();
public SchemaLoaderBuilder() {
this.schemaLoaders.add(new ClasspathSchemaLoader());
@@ -35,7 +35,7 @@ public SchemaLoaderBuilder() {
}
public SchemaLoaderBuilder schemaLoaderFactory(
- BiFunction, List, SchemaLoader> schemaLoaderFactory) {
+ BiFunction, List, SchemaLoader> schemaLoaderFactory) {
this.schemaLoaderFactory = schemaLoaderFactory;
return this;
}
@@ -45,7 +45,7 @@ public SchemaLoaderBuilder schemaLoaders(List schemaLoaders) {
return this;
}
- public SchemaLoaderBuilder absoluteIriMappers(List absoluteIriMappers) {
+ public SchemaLoaderBuilder absoluteIriMappers(List absoluteIriMappers) {
this.absoluteIriMappers = absoluteIriMappers;
return this;
}
@@ -55,12 +55,12 @@ public SchemaLoaderBuilder schemaLoaders(Consumer> schemaLoad
return this;
}
- public SchemaLoaderBuilder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
+ public SchemaLoaderBuilder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
absoluteIriCustomizer.accept(this.absoluteIriMappers);
return this;
}
- public SchemaLoaderBuilder absoluteIriMapper(AbsoluteIriMapper absoluteIriMapper) {
+ public SchemaLoaderBuilder absoluteIriMapper(SchemaMapper absoluteIriMapper) {
this.absoluteIriMappers.add(absoluteIriMapper);
return this;
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoaders.java b/src/main/java/com/networknt/schema/uri/SchemaLoaders.java
new file mode 100644
index 000000000..1160923f1
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoaders.java
@@ -0,0 +1,50 @@
+/*
+ * 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.uri;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Schema Loaders.
+ */
+public class SchemaLoaders extends ArrayList {
+ private static final long serialVersionUID = 1L;
+
+ public SchemaLoaders() {
+ super();
+ }
+
+ public SchemaLoaders(Collection extends SchemaLoader> c) {
+ super(c);
+ }
+
+ public SchemaLoaders(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private SchemaLoaders values = new SchemaLoaders();
+
+ public SchemaLoaders build() {
+ return values;
+ }
+ }
+}
diff --git a/src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/SchemaMapper.java
similarity index 92%
rename from src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java
rename to src/main/java/com/networknt/schema/uri/SchemaMapper.java
index bed7d0dd1..914a61018 100644
--- a/src/main/java/com/networknt/schema/uri/AbsoluteIriMapper.java
+++ b/src/main/java/com/networknt/schema/uri/SchemaMapper.java
@@ -21,6 +21,6 @@
* Maps absolute IRI.
*/
@FunctionalInterface
-public interface AbsoluteIriMapper {
+public interface SchemaMapper {
AbsoluteIri map(AbsoluteIri absoluteIRI);
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaMappers.java b/src/main/java/com/networknt/schema/uri/SchemaMappers.java
new file mode 100644
index 000000000..c4afb6eaf
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/SchemaMappers.java
@@ -0,0 +1,51 @@
+/*
+ * 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.uri;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Schema Mappers.
+ */
+public class SchemaMappers extends ArrayList {
+ private static final long serialVersionUID = 1L;
+
+ public SchemaMappers() {
+ super();
+ }
+
+ public SchemaMappers(Collection extends SchemaMapper> c) {
+ super(c);
+ }
+
+ public SchemaMappers(int initialCapacity) {
+ super(initialCapacity);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private SchemaMappers values = new SchemaMappers();
+
+ public SchemaMappers build() {
+ return values;
+ }
+ }
+
+}
diff --git a/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java b/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
index bf69037df..863e7bc4c 100644
--- a/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/uri/UriSchemaLoader.java
@@ -17,15 +17,15 @@
import java.net.URI;
-import com.networknt.schema.SchemaLocation;
+import com.networknt.schema.AbsoluteIri;
/**
* Loads from uri.
*/
public class UriSchemaLoader implements SchemaLoader {
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
- URI uri = URI.create(schemaLocation.getAbsoluteIri().toString());
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ URI uri = URI.create(absoluteIri.toString());
return () -> uri.toURL().openStream();
}
}
diff --git a/src/test/java/com/networknt/schema/CustomUriTest.java b/src/test/java/com/networknt/schema/CustomUriTest.java
index 9b35ef2fe..4f02a8db7 100644
--- a/src/test/java/com/networknt/schema/CustomUriTest.java
+++ b/src/test/java/com/networknt/schema/CustomUriTest.java
@@ -46,7 +46,7 @@ private static class CustomUriFetcher implements SchemaLoader {
private static final String SCHEMA = "{\"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\"$id\":\"custom:date\",\"type\":\"string\",\"format\":\"date\"}";
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
return () -> new ByteArrayInputStream(SCHEMA.getBytes(StandardCharsets.UTF_8));
}
}
diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
index 71233d591..a2a5f5ed6 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
@@ -33,11 +33,11 @@ private void runCacheTest(boolean enableCache) throws JsonProcessingException {
JsonSchemaFactory factory = buildJsonSchemaFactory(fetcher, enableCache);
SchemaLocation schemaUri = SchemaLocation.of("cache:uri_mapping/schema1.json");
String schema = "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\", \"title\": \"json-object-with-schema\", \"type\": \"string\" }";
- fetcher.addResource(schemaUri, schema);
+ fetcher.addResource(schemaUri.getAbsoluteIri(), schema);
assertEquals(objectMapper.readTree(schema), factory.getSchema(schemaUri, new SchemaValidatorsConfig()).schemaNode);
String modifiedSchema = "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\", \"title\": \"json-object-with-schema\", \"type\": \"object\" }";
- fetcher.addResource(schemaUri, modifiedSchema);
+ fetcher.addResource(schemaUri.getAbsoluteIri(), modifiedSchema);
assertEquals(objectMapper.readTree(enableCache ? schema : modifiedSchema), factory.getSchema(schemaUri, new SchemaValidatorsConfig()).schemaNode);
}
@@ -52,19 +52,19 @@ private JsonSchemaFactory buildJsonSchemaFactory(CustomURIFetcher uriFetcher, bo
private class CustomURIFetcher implements SchemaLoader {
- private Map uriToResource = new HashMap<>();
+ private Map uriToResource = new HashMap<>();
- void addResource(SchemaLocation uri, String schema) {
+ void addResource(AbsoluteIri uri, String schema) {
addResource(uri, new ByteArrayInputStream(schema.getBytes(StandardCharsets.UTF_8)));
}
- void addResource(SchemaLocation uri, InputStream is) {
+ void addResource(AbsoluteIri uri, InputStream is) {
uriToResource.put(uri, is);
}
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
- return () -> uriToResource.get(schemaLocation);
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ return () -> uriToResource.get(absoluteIri);
}
}
}
diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
index 1d916c8d4..be3b3b29b 100644
--- a/src/test/java/com/networknt/schema/UriMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -18,7 +18,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchemaFactory.Builder;
-import com.networknt.schema.uri.AbsoluteIriMapper;
+import com.networknt.schema.uri.SchemaMapper;
import com.networknt.schema.uri.MapAbsoluteIriMapper;
import org.junit.jupiter.api.Test;
@@ -155,7 +155,7 @@ public void testMappingsForRef() throws IOException {
assertEquals(0, schema.validate(mapper.readTree("[]")).size());
}
- private AbsoluteIriMapper getUriMappingsFromUrl(URL url) {
+ private SchemaMapper getUriMappingsFromUrl(URL url) {
HashMap map = new HashMap();
try {
for (JsonNode mapping : mapper.readTree(url)) {
From 20312c37a820a7b5c3ce0d842d862fd44de120a9 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 23 Jan 2024 05:29:16 +0800
Subject: [PATCH 25/65] Refactor
---
.../networknt/schema/JsonSchemaFactory.java | 33 ++++---
.../schema/uri/MapAbsoluteIriMapper.java | 26 ------
.../networknt/schema/uri/MapSchemaLoader.java | 36 ++++++++
.../networknt/schema/uri/MapSchemaMapper.java | 31 +++++++
...IriMapper.java => PrefixSchemaMapper.java} | 4 +-
.../schema/uri/SchemaLoaderBuilder.java | 87 -------------------
.../networknt/schema/uri/SchemaLoaders.java | 62 ++++++++++++-
.../networknt/schema/uri/SchemaMappers.java | 43 +++++++++
.../schema/AbstractJsonSchemaTestSuite.java | 2 +-
.../com/networknt/schema/CustomUriTest.java | 9 +-
.../com/networknt/schema/Issue285Test.java | 2 +-
.../com/networknt/schema/Issue665Test.java | 4 +-
.../com/networknt/schema/Issue824Test.java | 4 +-
.../schema/JsonSchemaFactoryUriCacheTest.java | 2 +-
.../com/networknt/schema/UriMappingTest.java | 14 +--
.../java/com/networknt/schema/UrnTest.java | 2 +-
16 files changed, 210 insertions(+), 151 deletions(-)
delete mode 100644 src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
create mode 100644 src/main/java/com/networknt/schema/uri/MapSchemaLoader.java
create mode 100644 src/main/java/com/networknt/schema/uri/MapSchemaMapper.java
rename src/main/java/com/networknt/schema/uri/{PrefixAbsoluteIriMapper.java => PrefixSchemaMapper.java} (80%)
delete mode 100644 src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 5f5e989c3..6895357cd 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -45,7 +45,8 @@ public static class Builder {
private YAMLMapper yamlMapper = null;
private String defaultMetaSchemaURI;
private final ConcurrentMap jsonMetaSchemas = new ConcurrentHashMap();
- private SchemaLoaderBuilder schemaLoaderBuilder = new SchemaLoaderBuilder();
+ private SchemaLoaders.Builder schemaLoadersBuilder = SchemaLoaders.builder();
+ private SchemaMappers.Builder schemaMappersBuilder = SchemaMappers.builder();
private boolean enableUriSchemaCache = true;
public Builder objectMapper(final ObjectMapper objectMapper) {
@@ -80,8 +81,13 @@ public Builder enableUriSchemaCache(boolean enableUriSchemaCache) {
return this;
}
- public Builder schemaLoaderBuilder(Consumer schemaLoaderBuilderCustomizer) {
- schemaLoaderBuilderCustomizer.accept(this.schemaLoaderBuilder);
+ public Builder schemaLoaders(Consumer schemaLoadersBuilderCustomizer) {
+ schemaLoadersBuilderCustomizer.accept(this.schemaLoadersBuilder);
+ return this;
+ }
+
+ public Builder schemaMappers(Consumer schemaMappersBuilderCustomizer) {
+ schemaMappersBuilderCustomizer.accept(this.schemaMappersBuilder);
return this;
}
@@ -91,7 +97,8 @@ public JsonSchemaFactory build() {
objectMapper == null ? new ObjectMapper() : objectMapper,
yamlMapper == null ? new YAMLMapper(): yamlMapper,
defaultMetaSchemaURI,
- schemaLoaderBuilder,
+ schemaLoadersBuilder,
+ schemaMappersBuilder,
jsonMetaSchemas,
enableUriSchemaCache
);
@@ -101,7 +108,8 @@ public JsonSchemaFactory build() {
private final ObjectMapper jsonMapper;
private final YAMLMapper yamlMapper;
private final String defaultMetaSchemaURI;
- private final SchemaLoaderBuilder schemaLoaderBuilder;
+ private final SchemaLoaders.Builder schemaLoadersBuilder;
+ private final SchemaMappers.Builder schemaMappersBuilder;
private final SchemaLoader schemaLoader;
private final Map jsonMetaSchemas;
private final ConcurrentMap uriSchemaCache = new ConcurrentHashMap<>();
@@ -112,7 +120,8 @@ private JsonSchemaFactory(
final ObjectMapper jsonMapper,
final YAMLMapper yamlMapper,
final String defaultMetaSchemaURI,
- SchemaLoaderBuilder schemaLoaderBuilder,
+ SchemaLoaders.Builder schemaLoadersBuilder,
+ SchemaMappers.Builder schemaMappersBuilder,
final Map jsonMetaSchemas,
final boolean enableUriSchemaCache) {
if (jsonMapper == null) {
@@ -121,8 +130,10 @@ private JsonSchemaFactory(
throw new IllegalArgumentException("YAMLMapper must not be null");
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
throw new IllegalArgumentException("defaultMetaSchemaURI must not be null or empty");
- } else if (schemaLoaderBuilder == null) {
+ } else if (schemaLoadersBuilder == null) {
throw new IllegalArgumentException("SchemaLoaders must not be null");
+ } else if (schemaMappersBuilder == null) {
+ throw new IllegalArgumentException("SchemaMappers must not be null");
} else if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
} else if (jsonMetaSchemas.get(normalizeMetaSchemaUri(defaultMetaSchemaURI)) == null) {
@@ -131,8 +142,9 @@ private JsonSchemaFactory(
this.jsonMapper = jsonMapper;
this.yamlMapper = yamlMapper;
this.defaultMetaSchemaURI = defaultMetaSchemaURI;
- this.schemaLoaderBuilder = schemaLoaderBuilder;
- this.schemaLoader = schemaLoaderBuilder.build();
+ this.schemaLoadersBuilder = schemaLoadersBuilder;
+ this.schemaMappersBuilder = schemaMappersBuilder;
+ this.schemaLoader = new DefaultSchemaLoader(schemaLoadersBuilder.build(), schemaMappersBuilder.build());
this.jsonMetaSchemas = jsonMetaSchemas;
this.enableUriSchemaCache = enableUriSchemaCache;
}
@@ -193,7 +205,8 @@ public static Builder builder(final JsonSchemaFactory blueprint) {
.defaultMetaSchemaURI(blueprint.defaultMetaSchemaURI)
.objectMapper(blueprint.jsonMapper)
.yamlMapper(blueprint.yamlMapper);
- builder.schemaLoaderBuilder = blueprint.schemaLoaderBuilder;
+ builder.schemaLoadersBuilder.with(blueprint.schemaLoadersBuilder);
+ builder.schemaMappersBuilder.with(blueprint.schemaMappersBuilder);
return builder;
}
diff --git a/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
deleted file mode 100644
index 53d443a92..000000000
--- a/src/main/java/com/networknt/schema/uri/MapAbsoluteIriMapper.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.networknt.schema.uri;
-
-import java.util.Map;
-
-import com.networknt.schema.AbsoluteIri;
-
-/**
- * Map implementation of {@link SchemaMapper}.
- */
-public class MapAbsoluteIriMapper implements SchemaMapper {
- private final Map mappings;
-
- public MapAbsoluteIriMapper(Map mappings) {
- this.mappings = mappings;
- }
-
- @Override
- public AbsoluteIri map(AbsoluteIri absoluteIRI) {
- String mapped = this.mappings.get(absoluteIRI.toString());
- if (mapped != null) {
- return AbsoluteIri.of(mapped);
- }
- return null;
- }
-
-}
diff --git a/src/main/java/com/networknt/schema/uri/MapSchemaLoader.java b/src/main/java/com/networknt/schema/uri/MapSchemaLoader.java
new file mode 100644
index 000000000..d6b036d19
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/MapSchemaLoader.java
@@ -0,0 +1,36 @@
+package com.networknt.schema.uri;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+import java.util.function.Function;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Map implementation of {@link SchemaLoader}.
+ */
+public class MapSchemaLoader implements SchemaLoader {
+ private final Function mappings;
+
+ public MapSchemaLoader(Map mappings) {
+ this(mappings::get);
+ }
+
+ public MapSchemaLoader(Function mappings) {
+ this.mappings = mappings;
+ }
+
+ @Override
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ try {
+ String result = mappings.apply(absoluteIri.toString());
+ if (result != null) {
+ return () -> new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8));
+ }
+ } catch (Exception e) {
+ // Do nothing
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/networknt/schema/uri/MapSchemaMapper.java b/src/main/java/com/networknt/schema/uri/MapSchemaMapper.java
new file mode 100644
index 000000000..25916374b
--- /dev/null
+++ b/src/main/java/com/networknt/schema/uri/MapSchemaMapper.java
@@ -0,0 +1,31 @@
+package com.networknt.schema.uri;
+
+import java.util.Map;
+import java.util.function.Function;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Map implementation of {@link SchemaMapper}.
+ */
+public class MapSchemaMapper implements SchemaMapper {
+ private final Function mappings;
+
+ public MapSchemaMapper(Map mappings) {
+ this(mappings::get);
+ }
+
+ public MapSchemaMapper(Function mappings) {
+ this.mappings = mappings;
+ }
+
+ @Override
+ public AbsoluteIri map(AbsoluteIri absoluteIRI) {
+ String mapped = this.mappings.apply(absoluteIRI.toString());
+ if (mapped != null) {
+ return AbsoluteIri.of(mapped);
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java b/src/main/java/com/networknt/schema/uri/PrefixSchemaMapper.java
similarity index 80%
rename from src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
rename to src/main/java/com/networknt/schema/uri/PrefixSchemaMapper.java
index 7014a0d98..aa7eee080 100644
--- a/src/main/java/com/networknt/schema/uri/PrefixAbsoluteIriMapper.java
+++ b/src/main/java/com/networknt/schema/uri/PrefixSchemaMapper.java
@@ -5,11 +5,11 @@
/**
* Prefix implementation of {@link SchemaMapper}.
*/
-public class PrefixAbsoluteIriMapper implements SchemaMapper {
+public class PrefixSchemaMapper implements SchemaMapper {
private final String source;
private final String replacement;
- public PrefixAbsoluteIriMapper(String source, String replacement) {
+ public PrefixSchemaMapper(String source, String replacement) {
this.source = source;
this.replacement = replacement;
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java b/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
deleted file mode 100644
index cd6d5a1a2..000000000
--- a/src/main/java/com/networknt/schema/uri/SchemaLoaderBuilder.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2023 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.uri;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-
-/**
- * Builder for {@link SchemaLoader}.
- */
-public class SchemaLoaderBuilder {
- private BiFunction, List, SchemaLoader> schemaLoaderFactory = DefaultSchemaLoader::new;
- private List schemaLoaders = new ArrayList<>();
- private List absoluteIriMappers = new ArrayList<>();
-
- public SchemaLoaderBuilder() {
- this.schemaLoaders.add(new ClasspathSchemaLoader());
- this.schemaLoaders.add(new UriSchemaLoader());
- }
-
- public SchemaLoaderBuilder schemaLoaderFactory(
- BiFunction, List, SchemaLoader> schemaLoaderFactory) {
- this.schemaLoaderFactory = schemaLoaderFactory;
- return this;
- }
-
- public SchemaLoaderBuilder schemaLoaders(List schemaLoaders) {
- this.schemaLoaders = schemaLoaders;
- return this;
- }
-
- public SchemaLoaderBuilder absoluteIriMappers(List absoluteIriMappers) {
- this.absoluteIriMappers = absoluteIriMappers;
- return this;
- }
-
- public SchemaLoaderBuilder schemaLoaders(Consumer> schemaLoaderCustomizer) {
- schemaLoaderCustomizer.accept(this.schemaLoaders);
- return this;
- }
-
- public SchemaLoaderBuilder absoluteIriMappers(Consumer> absoluteIriCustomizer) {
- absoluteIriCustomizer.accept(this.absoluteIriMappers);
- return this;
- }
-
- public SchemaLoaderBuilder absoluteIriMapper(SchemaMapper absoluteIriMapper) {
- this.absoluteIriMappers.add(absoluteIriMapper);
- return this;
- }
-
- public SchemaLoaderBuilder schemaLoader(SchemaLoader schemaLoader) {
- this.schemaLoaders.add(0, schemaLoader);
- return this;
- }
-
- public SchemaLoaderBuilder mapPrefix(String source, String replacement) {
- this.absoluteIriMappers.add(new PrefixAbsoluteIriMapper(source, replacement));
- return this;
- }
-
- public SchemaLoaderBuilder map(Map mappings) {
- this.absoluteIriMappers.add(new MapAbsoluteIriMapper(mappings));
- return this;
- }
-
- public SchemaLoader build() {
- return schemaLoaderFactory.apply(this.schemaLoaders, this.absoluteIriMappers);
- }
-
-}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaLoaders.java b/src/main/java/com/networknt/schema/uri/SchemaLoaders.java
index 1160923f1..a433daac9 100644
--- a/src/main/java/com/networknt/schema/uri/SchemaLoaders.java
+++ b/src/main/java/com/networknt/schema/uri/SchemaLoaders.java
@@ -17,6 +17,10 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Schema Loaders.
@@ -24,6 +28,17 @@
public class SchemaLoaders extends ArrayList {
private static final long serialVersionUID = 1L;
+ private static final ClasspathSchemaLoader CLASSPATH_SCHEMA_LOADER = new ClasspathSchemaLoader();
+ private static final UriSchemaLoader URI_SCHEMA_LOADER = new UriSchemaLoader();
+ private static final SchemaLoaders DEFAULT;
+
+ static {
+ SchemaLoaders schemaLoaders = new SchemaLoaders();
+ schemaLoaders.add(CLASSPATH_SCHEMA_LOADER);
+ schemaLoaders.add(URI_SCHEMA_LOADER);
+ DEFAULT = schemaLoaders;
+ }
+
public SchemaLoaders() {
super();
}
@@ -35,16 +50,57 @@ public SchemaLoaders(Collection extends SchemaLoader> c) {
public SchemaLoaders(int initialCapacity) {
super(initialCapacity);
}
-
+
public static Builder builder() {
return new Builder();
}
public static class Builder {
- private SchemaLoaders values = new SchemaLoaders();
+ SchemaLoaders values = new SchemaLoaders();
+
+ public Builder() {
+ }
+
+ public Builder(Builder copy) {
+ this.values.addAll(copy.values);
+ }
+
+ public Builder with(Builder builder) {
+ if (!builder.values.isEmpty()) {
+ this.values.addAll(builder.values);
+ }
+ return this;
+ }
+
+ public Builder values(Consumer> values) {
+ values.accept(this.values);
+ return this;
+ }
+ public Builder add(SchemaLoader schemaLoader) {
+ this.values.add(schemaLoader);
+ return this;
+ }
+
+ public Builder values(Map mappings) {
+ this.values.add(new MapSchemaLoader(mappings));
+ return this;
+ }
+
+ public Builder values(Function mappings) {
+ this.values.add(new MapSchemaLoader(mappings));
+ return this;
+ }
+
public SchemaLoaders build() {
- return values;
+ if (this.values.isEmpty()) {
+ return DEFAULT;
+ }
+ SchemaLoaders result = new SchemaLoaders();
+ result.add(CLASSPATH_SCHEMA_LOADER);
+ result.addAll(this.values);
+ result.add(URI_SCHEMA_LOADER);
+ return result;
}
}
}
diff --git a/src/main/java/com/networknt/schema/uri/SchemaMappers.java b/src/main/java/com/networknt/schema/uri/SchemaMappers.java
index c4afb6eaf..1a21abdab 100644
--- a/src/main/java/com/networknt/schema/uri/SchemaMappers.java
+++ b/src/main/java/com/networknt/schema/uri/SchemaMappers.java
@@ -17,6 +17,10 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Schema Mappers.
@@ -42,7 +46,46 @@ public static Builder builder() {
public static class Builder {
private SchemaMappers values = new SchemaMappers();
+
+ public Builder() {
+ }
+
+ public Builder(Builder copy) {
+ this.values.addAll(copy.values);
+ }
+
+ public Builder with(Builder builder) {
+ if (!builder.values.isEmpty()) {
+ this.values.addAll(builder.values);
+ }
+ return this;
+ }
+
+ public Builder values(Consumer> values) {
+ values.accept(this.values);
+ return this;
+ }
+
+ public Builder add(SchemaMapper schemaMapper) {
+ this.values.add(schemaMapper);
+ return this;
+ }
+
+ public Builder mapPrefix(String source, String replacement) {
+ this.values.add(new PrefixSchemaMapper(source, replacement));
+ return this;
+ }
+
+ public Builder values(Map mappings) {
+ this.values.add(new MapSchemaMapper(mappings));
+ return this;
+ }
+ public Builder values(Function mappings) {
+ this.values.add(new MapSchemaMapper(mappings));
+ return this;
+ }
+
public SchemaMappers build() {
return values;
}
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index 66c471260..f318f164f 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -186,7 +186,7 @@ private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, Test
return JsonSchemaFactory
.builder(base)
.objectMapper(this.mapper)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .schemaMappers(schemaMappers -> schemaMappers
.mapPrefix("https://", "http://")
.mapPrefix("http://json-schema.org", "resource:"))
.build();
diff --git a/src/test/java/com/networknt/schema/CustomUriTest.java b/src/test/java/com/networknt/schema/CustomUriTest.java
index 4f02a8db7..d796887be 100644
--- a/src/test/java/com/networknt/schema/CustomUriTest.java
+++ b/src/test/java/com/networknt/schema/CustomUriTest.java
@@ -3,7 +3,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.uri.InputStreamSource;
import com.networknt.schema.uri.SchemaLoader;
-import com.networknt.schema.uri.UriSchemaLoader;
import org.junit.jupiter.api.Test;
@@ -33,13 +32,7 @@ public void customUri() throws Exception {
private JsonSchemaFactory buildJsonSchemaFactory() {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoaders(schemaLoaders -> {
- for (int x = 0; x < schemaLoaders.size(); x++) {
- if (schemaLoaders.get(x) instanceof UriSchemaLoader) {
- schemaLoaders.set(x, new CustomUriFetcher());
- }
- }
- })).build();
+ .schemaLoaders(schemaLoaders -> schemaLoaders.add(new CustomUriFetcher())).build();
}
private static class CustomUriFetcher implements SchemaLoader {
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index e64f8b673..ff868ccae 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -16,7 +16,7 @@ public class Issue285Test {
private JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
.objectMapper(mapper)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .schemaMappers(schemaMappers -> schemaMappers
.mapPrefix("http://json-schema.org", "resource:")
.mapPrefix("https://json-schema.org", "resource:"))
.build();
diff --git a/src/test/java/com/networknt/schema/Issue665Test.java b/src/test/java/com/networknt/schema/Issue665Test.java
index 1d57e6f7d..2973cc4f1 100644
--- a/src/test/java/com/networknt/schema/Issue665Test.java
+++ b/src/test/java/com/networknt/schema/Issue665Test.java
@@ -25,8 +25,8 @@ void testUrnUriAsLocalRef() throws IOException {
void testUrnUriAsLocalRef_ExternalURN() {
JsonSchemaFactory factory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
- .schemaLoaderBuilder(schemaLoaderBuilder -> {
- schemaLoaderBuilder.map(Collections.singletonMap("urn:data",
+ .schemaMappers(schemaMappers -> {
+ schemaMappers.values(Collections.singletonMap("urn:data",
"classpath:draft7/urn/issue665_external_urn_subschema.json"));
})
.build();
diff --git a/src/test/java/com/networknt/schema/Issue824Test.java b/src/test/java/com/networknt/schema/Issue824Test.java
index bcb2229b4..dc3279c78 100644
--- a/src/test/java/com/networknt/schema/Issue824Test.java
+++ b/src/test/java/com/networknt/schema/Issue824Test.java
@@ -15,8 +15,8 @@ public class Issue824Test {
void validate() throws JsonProcessingException {
final JsonSchema v201909SpecSchema = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .schemaLoaderBuilder(schemaLoaderBuilder -> {
- schemaLoaderBuilder.mapPrefix("https://json-schema.org", "resource:");
+ .schemaMappers(schemaMappers -> {
+ schemaMappers.mapPrefix("https://json-schema.org", "resource:");
}).build()
.getSchema(SchemaLocation.of(JsonMetaSchema.getV201909().getUri()));
v201909SpecSchema.preloadJsonSchema();
diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
index a2a5f5ed6..f6660f2d3 100644
--- a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
+++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java
@@ -45,7 +45,7 @@ private void runCacheTest(boolean enableCache) throws JsonProcessingException {
private JsonSchemaFactory buildJsonSchemaFactory(CustomURIFetcher uriFetcher, boolean enableUriSchemaCache) {
return JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012))
.enableUriSchemaCache(enableUriSchemaCache)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoader(uriFetcher))
+ .schemaLoaders(schemaLoaders -> schemaLoaders.add(uriFetcher))
.addMetaSchema(JsonMetaSchema.getV202012())
.build();
}
diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java
index be3b3b29b..c6ac2286a 100644
--- a/src/test/java/com/networknt/schema/UriMappingTest.java
+++ b/src/test/java/com/networknt/schema/UriMappingTest.java
@@ -19,7 +19,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.JsonSchemaFactory.Builder;
import com.networknt.schema.uri.SchemaMapper;
-import com.networknt.schema.uri.MapAbsoluteIriMapper;
+import com.networknt.schema.uri.MapSchemaMapper;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
@@ -49,7 +49,7 @@ public void testBuilderUriMappingUri() throws IOException {
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings)));
+ .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings)));
JsonSchemaFactory instance = builder.build();
JsonSchema schema = instance.getSchema(SchemaLocation.of(
"https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
@@ -87,7 +87,7 @@ public void testBuilderExampleMappings() throws IOException {
Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV4.getUri())
.addMetaSchema(draftV4)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings)));
+ .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings)));
instance = builder.build();
JsonSchema schema = instance.getSchema(example);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
@@ -103,7 +103,7 @@ public void testBuilderExampleMappings() throws IOException {
public void testValidatorConfigUriMappingUri() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/uri-mapping.json");
JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
+ .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build();
JsonSchema schema = instance.getSchema(SchemaLocation.of(
"https://raw.githubusercontent.com/networknt/json-schema-validator/master/src/test/resources/draft4/extra/uri_mapping/uri-mapping.schema.json"));
assertEquals(0, schema.validate(mapper.readTree(mappings)).size());
@@ -139,7 +139,7 @@ public void testValidatorConfigExampleMappings() throws IOException {
fail("Unexpected exception thrown");
}
instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
+ .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build();
JsonSchema schema = instance.getSchema(example, config);
assertEquals(0, schema.validate(mapper.createObjectNode()).size());
}
@@ -148,7 +148,7 @@ public void testValidatorConfigExampleMappings() throws IOException {
public void testMappingsForRef() throws IOException {
URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/schema-with-ref-mapping.json");
JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(getUriMappingsFromUrl(mappings))).build();
+ .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build();
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:draft4/extra/uri_mapping/schema-with-ref.json"),
config);
@@ -165,6 +165,6 @@ private SchemaMapper getUriMappingsFromUrl(URL url) {
} catch (IOException e) {
throw new UncheckedIOException(e);
}
- return new MapAbsoluteIriMapper(map);
+ return new MapSchemaMapper(map);
}
}
diff --git a/src/test/java/com/networknt/schema/UrnTest.java b/src/test/java/com/networknt/schema/UrnTest.java
index 02c4d03db..77ef35f25 100644
--- a/src/test/java/com/networknt/schema/UrnTest.java
+++ b/src/test/java/com/networknt/schema/UrnTest.java
@@ -29,7 +29,7 @@ public void testURNToURI() throws Exception {
JsonSchemaFactory.Builder builder = JsonSchemaFactory.builder()
.defaultMetaSchemaURI(draftV7.getUri())
.addMetaSchema(draftV7)
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.absoluteIriMapper(value -> AbsoluteIri.of(String.format("resource:draft7/urn/%s.schema.json", value.toString())))
+ .schemaMappers(schemaMappers -> schemaMappers.add(value -> AbsoluteIri.of(String.format("resource:draft7/urn/%s.schema.json", value.toString())))
);
JsonSchemaFactory instance = builder.build();
JsonSchema schema = instance.getSchema(is);
From ff14724ccf0e629760e5e5eec4375ea7764b952b Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 23 Jan 2024 08:40:49 +0800
Subject: [PATCH 26/65] Update docs
---
doc/schema-retrieval.md | 21 ++++++++-------------
1 file changed, 8 insertions(+), 13 deletions(-)
diff --git a/doc/schema-retrieval.md b/doc/schema-retrieval.md
index 08e37f480..e253699b7 100644
--- a/doc/schema-retrieval.md
+++ b/doc/schema-retrieval.md
@@ -8,14 +8,14 @@ In the event that the schema does not define a schema identifier using the `$id`
## Mapping Schema Identifier to Retrieval IRI
-The schema identifier can be mapped to the retrieval IRI by implementing the `AbsoluteIriMapper` interface.
+The schema identifier can be mapped to the retrieval IRI by implementing the `SchemaMapper` interface.
-### Configuring AbsoluteIriMapper
+### Configuring Schema Mapper
```java
JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
- .absoluteIriMapper(new CustomAbsoluteIriMapper())
+ .schemaMappers(schemaMappers -> schemaMappers
+ .add(new CustomSchemaMapper())
.addMetaSchema(JsonMetaSchema.getV7())
.defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
.build();
@@ -25,7 +25,7 @@ JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
```java
JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder
+ .schemaMappers(schemaMappers -> schemaMappers
.mapPrefix("https://", "http://")
.mapPrefix("http://json-schema.org", "classpath:"))
.addMetaSchema(JsonMetaSchema.getV7())
@@ -58,8 +58,8 @@ public class CustomUriSchemaLoader implements SchemaLoader {
}
@Override
- public InputStreamSource getSchema(SchemaLocation schemaLocation) {
- URI uri = URI.create(schemaLocation.getAbsoluteIri().toString());
+ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
+ URI uri = URI.create(absoluteIri.toString());
return () -> {
HttpRequest request = HttpRequest.newBuilder().uri(uri).header("Authorization", authorizationToken).build();
try {
@@ -83,12 +83,7 @@ Within the `JsonSchemaFactory` the custom `SchemaLoader` must be configured.
CustomUriSchemaLoader uriSchemaLoader = new CustomUriSchemaLoader(authorizationToken);
JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder()
- .schemaLoaderBuilder(schemaLoaderBuilder -> schemaLoaderBuilder.schemaLoaders(schemaLoaders -> {
- for (int x = 0; x < schemaLoaders.size(); x++) {
- if (schemaLoaders.get(x) instanceof UriSchemaLoader) {
- schemaLoaders.set(x, uriSchemaLoader);
- }
- }
+ .schemaLoaders(schemaLoaders -> schemaLoaders.add(uriSchemaLoader))
.addMetaSchema(JsonMetaSchema.getV7())
.defaultMetaSchemaURI(JsonMetaSchema.getV7().getUri())
.build();
From b277106efa526decb76d23106b8627bc8e4057b0 Mon Sep 17 00:00:00 2001
From: Justin Tay <49700559+justin-tay@users.noreply.github.com>
Date: Tue, 23 Jan 2024 09:05:55 +0800
Subject: [PATCH 27/65] Allow yaml to be optional
---
.../networknt/schema/JsonMapperFactory.java | 36 +++++++
.../networknt/schema/JsonSchemaFactory.java | 96 +++++++++++++++----
.../networknt/schema/YamlMapperFactory.java | 36 +++++++
.../schema/AbstractJsonSchemaTest.java | 2 +-
.../schema/AbstractJsonSchemaTestSuite.java | 4 +-
.../schema/BaseJsonSchemaValidatorTest.java | 2 +-
.../com/networknt/schema/Issue285Test.java | 2 +-
.../schema/Issue366FailFastTest.java | 2 +-
.../schema/Issue366FailSlowTest.java | 2 +-
.../com/networknt/schema/Issue428Test.java | 2 +-
.../com/networknt/schema/Issue510Test.java | 2 +-
.../schema/OpenAPI30JsonSchemaTest.java | 2 +-
.../schema/UnknownMetaSchemaTest.java | 6 +-
.../networknt/schema/V4JsonSchemaTest.java | 2 +-
.../networknt/schema/suite/TestSource.java | 3 +-
15 files changed, 164 insertions(+), 35 deletions(-)
create mode 100644 src/main/java/com/networknt/schema/JsonMapperFactory.java
create mode 100644 src/main/java/com/networknt/schema/YamlMapperFactory.java
diff --git a/src/main/java/com/networknt/schema/JsonMapperFactory.java b/src/main/java/com/networknt/schema/JsonMapperFactory.java
new file mode 100644
index 000000000..d6edf05b2
--- /dev/null
+++ b/src/main/java/com/networknt/schema/JsonMapperFactory.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+
+/**
+ * Json Mapper Factory.
+ */
+public class JsonMapperFactory {
+
+ /**
+ * The holder defers the classloading until it is used.
+ */
+ private static class Holder {
+ private static final ObjectMapper INSTANCE = JsonMapper.builder().build();
+ }
+
+ public static ObjectMapper getInstance() {
+ return Holder.INSTANCE;
+ }
+}
diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
index 6895357cd..fb5bb41b5 100644
--- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java
+++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java
@@ -18,7 +18,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.uri.*;
import org.slf4j.Logger;
@@ -41,20 +40,20 @@ public class JsonSchemaFactory {
public static class Builder {
- private ObjectMapper objectMapper = null;
- private YAMLMapper yamlMapper = null;
+ private ObjectMapper jsonMapper = null;
+ private ObjectMapper yamlMapper = null;
private String defaultMetaSchemaURI;
private final ConcurrentMap jsonMetaSchemas = new ConcurrentHashMap();
private SchemaLoaders.Builder schemaLoadersBuilder = SchemaLoaders.builder();
private SchemaMappers.Builder schemaMappersBuilder = SchemaMappers.builder();
private boolean enableUriSchemaCache = true;
- public Builder objectMapper(final ObjectMapper objectMapper) {
- this.objectMapper = objectMapper;
+ public Builder jsonMapper(final ObjectMapper jsonMapper) {
+ this.jsonMapper = jsonMapper;
return this;
}
- public Builder yamlMapper(final YAMLMapper yamlMapper) {
+ public Builder yamlMapper(final ObjectMapper yamlMapper) {
this.yamlMapper = yamlMapper;
return this;
}
@@ -94,8 +93,8 @@ public Builder schemaMappers(Consumer schemaMappersBuilde
public JsonSchemaFactory build() {
// create builtin keywords with (custom) formats.
return new JsonSchemaFactory(
- objectMapper == null ? new ObjectMapper() : objectMapper,
- yamlMapper == null ? new YAMLMapper(): yamlMapper,
+ jsonMapper,
+ yamlMapper,
defaultMetaSchemaURI,
schemaLoadersBuilder,
schemaMappersBuilder,
@@ -106,7 +105,7 @@ public JsonSchemaFactory build() {
}
private final ObjectMapper jsonMapper;
- private final YAMLMapper yamlMapper;
+ private final ObjectMapper yamlMapper;
private final String defaultMetaSchemaURI;
private final SchemaLoaders.Builder schemaLoadersBuilder;
private final SchemaMappers.Builder schemaMappersBuilder;
@@ -118,17 +117,13 @@ public JsonSchemaFactory build() {
private JsonSchemaFactory(
final ObjectMapper jsonMapper,
- final YAMLMapper yamlMapper,
+ final ObjectMapper yamlMapper,
final String defaultMetaSchemaURI,
SchemaLoaders.Builder schemaLoadersBuilder,
SchemaMappers.Builder schemaMappersBuilder,
final Map jsonMetaSchemas,
final boolean enableUriSchemaCache) {
- if (jsonMapper == null) {
- throw new IllegalArgumentException("ObjectMapper must not be null");
- } else if (yamlMapper == null) {
- throw new IllegalArgumentException("YAMLMapper must not be null");
- } else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
+ if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
throw new IllegalArgumentException("defaultMetaSchemaURI must not be null or empty");
} else if (schemaLoadersBuilder == null) {
throw new IllegalArgumentException("SchemaLoaders must not be null");
@@ -203,7 +198,7 @@ public static Builder builder(final JsonSchemaFactory blueprint) {
Builder builder = builder()
.addMetaSchemas(blueprint.jsonMetaSchemas.values())
.defaultMetaSchemaURI(blueprint.defaultMetaSchemaURI)
- .objectMapper(blueprint.jsonMapper)
+ .jsonMapper(blueprint.jsonMapper)
.yamlMapper(blueprint.yamlMapper);
builder.schemaLoadersBuilder.with(blueprint.schemaLoadersBuilder);
builder.schemaMappersBuilder.with(blueprint.schemaMappersBuilder);
@@ -303,7 +298,7 @@ private JsonMetaSchema fromId(String id, SchemaValidatorsConfig config) {
public JsonSchema getSchema(final String schema, final SchemaValidatorsConfig config) {
try {
- final JsonNode schemaNode = jsonMapper.readTree(schema);
+ final JsonNode schemaNode = getJsonMapper().readTree(schema);
return newJsonSchema(null, schemaNode, config);
} catch (IOException ioe) {
logger.error("Failed to load json schema!", ioe);
@@ -317,7 +312,7 @@ public JsonSchema getSchema(final String schema) {
public JsonSchema getSchema(final InputStream schemaStream, final SchemaValidatorsConfig config) {
try {
- final JsonNode schemaNode = jsonMapper.readTree(schemaStream);
+ final JsonNode schemaNode = getJsonMapper().readTree(schemaStream);
return newJsonSchema(null, schemaNode, config);
} catch (IOException ioe) {
logger.error("Failed to load json schema!", ioe);
@@ -329,6 +324,13 @@ public JsonSchema getSchema(final InputStream schemaStream) {
return getSchema(schemaStream, null);
}
+ /**
+ * Gets the schema.
+ *
+ * @param schemaUri the absolute IRI of the schema which can map to the retrieval IRI.
+ * @param config the config
+ * @return the schema
+ */
public JsonSchema getSchema(final SchemaLocation schemaUri, final SchemaValidatorsConfig config) {
if (enableUriSchemaCache) {
JsonSchema cachedUriSchema = uriSchemaCache.computeIfAbsent(schemaUri, key -> {
@@ -339,6 +341,14 @@ public JsonSchema getSchema(final SchemaLocation schemaUri, final SchemaValidato
return getMappedSchema(schemaUri, config);
}
+ protected ObjectMapper getYamlMapper() {
+ return this.yamlMapper != null ? this.yamlMapper : YamlMapperFactory.getInstance();
+ }
+
+ protected ObjectMapper getJsonMapper() {
+ return this.jsonMapper != null ? this.jsonMapper : JsonMapperFactory.getInstance();
+ }
+
protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValidatorsConfig config) {
try (InputStream inputStream = this.schemaLoader.getSchema(schemaUri.getAbsoluteIri()).getInputStream()) {
if (inputStream == null) {
@@ -346,9 +356,9 @@ protected JsonSchema getMappedSchema(final SchemaLocation schemaUri, SchemaValid
}
final JsonNode schemaNode;
if (isYaml(schemaUri)) {
- schemaNode = yamlMapper.readTree(inputStream);
+ schemaNode = getYamlMapper().readTree(inputStream);
} else {
- schemaNode = jsonMapper.readTree(inputStream);
+ schemaNode = getJsonMapper().readTree(inputStream);
}
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode, config);
@@ -385,22 +395,68 @@ public JsonSchema getSchema(final URI schemaUri, final JsonNode jsonNode) {
return newJsonSchema(SchemaLocation.of(schemaUri.toString()), jsonNode, null);
}
+ /**
+ * Gets the schema.
+ *
+ * @param schemaUri the absolute IRI of the schema which can map to the retrieval IRI.
+ * @return the schema
+ */
public JsonSchema getSchema(final SchemaLocation schemaUri) {
return getSchema(schemaUri, new SchemaValidatorsConfig());
}
+ /**
+ * Gets the schema.
+ *
+ * @param schemaUri the base absolute IRI
+ * @param jsonNode the node
+ * @param config the config
+ * @return the schema
+ */
public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode, final SchemaValidatorsConfig config) {
return newJsonSchema(schemaUri, jsonNode, config);
}
+ /**
+ * Gets the schema.
+ *
+ * @param schemaUri the base absolute IRI
+ * @param jsonNode the node
+ * @return the schema
+ */
public JsonSchema getSchema(final SchemaLocation schemaUri, final JsonNode jsonNode) {
return newJsonSchema(schemaUri, jsonNode, null);
}
+ /**
+ * Gets the schema.
+ *
+ * Using this is not recommended as there is potentially no base IRI for
+ * resolving references to the absolute IRI.
+ *
+ * Prefer {@link #getSchema(SchemaLocation, JsonNode, SchemaValidatorsConfig)}
+ * instead to ensure the base IRI if no id is present.
+ *
+ * @param jsonNode the node
+ * @param config the config
+ * @return the schema
+ */
public JsonSchema getSchema(final JsonNode jsonNode, final SchemaValidatorsConfig config) {
return newJsonSchema(null, jsonNode, config);
}
+ /**
+ * Gets the schema.
+ *
+ * Using this is not recommended as there is potentially no base IRI for
+ * resolving references to the absolute IRI.
+ *
+ * Prefer {@link #getSchema(SchemaLocation, JsonNode)} instead to ensure the
+ * base IRI if no id is present.
+ *
+ * @param jsonNode the node
+ * @return the schema
+ */
public JsonSchema getSchema(final JsonNode jsonNode) {
return newJsonSchema(null, jsonNode, null);
}
diff --git a/src/main/java/com/networknt/schema/YamlMapperFactory.java b/src/main/java/com/networknt/schema/YamlMapperFactory.java
new file mode 100644
index 000000000..9a9741e98
--- /dev/null
+++ b/src/main/java/com/networknt/schema/YamlMapperFactory.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+
+/**
+ * YAML Mapper Factory.
+ */
+public class YamlMapperFactory {
+
+ /**
+ * The holder defers the classloading until it is used.
+ */
+ private static class Holder {
+ private static final ObjectMapper INSTANCE = YAMLMapper.builder().build();
+ }
+
+ public static ObjectMapper getInstance() {
+ return Holder.INSTANCE;
+ }
+}
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
index fc718acf9..71a8a7556 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTest.java
@@ -52,7 +52,7 @@ private JsonSchema getJsonSchemaFromDataNode(JsonNode dataNode) {
private JsonNode getJsonNodeFromPath(String dataPath) {
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
- ObjectMapper mapper = new ObjectMapper();
+ ObjectMapper mapper = JsonMapperFactory.getInstance();
try {
return mapper.readTree(dataInputStream);
} catch(IOException e) {
diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
index f318f164f..b2adaba44 100644
--- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
+++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java
@@ -46,7 +46,7 @@
public abstract class AbstractJsonSchemaTestSuite extends HTTPServiceSupport {
- protected ObjectMapper mapper = new ObjectMapper();
+ protected ObjectMapper mapper = JsonMapperFactory.getInstance();
private static String toForwardSlashPath(Path file) {
return file.toString().replace('\\', '/');
@@ -185,7 +185,7 @@ private JsonSchemaFactory buildValidatorFactory(VersionFlag defaultVersion, Test
JsonSchemaFactory base = JsonSchemaFactory.getInstance(specVersion);
return JsonSchemaFactory
.builder(base)
- .objectMapper(this.mapper)
+ .jsonMapper(this.mapper)
.schemaMappers(schemaMappers -> schemaMappers
.mapPrefix("https://", "http://")
.mapPrefix("http://json-schema.org", "resource:"))
diff --git a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
index ad74d5e32..d5444b260 100644
--- a/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
+++ b/src/test/java/com/networknt/schema/BaseJsonSchemaValidatorTest.java
@@ -29,7 +29,7 @@
*/
public class BaseJsonSchemaValidatorTest {
- private static final ObjectMapper mapper = new ObjectMapper();
+ private static final ObjectMapper mapper = JsonMapperFactory.getInstance();
public static JsonNode getJsonNodeFromClasspath(String name) throws IOException {
InputStream is1 = Thread.currentThread().getContextClassLoader()
diff --git a/src/test/java/com/networknt/schema/Issue285Test.java b/src/test/java/com/networknt/schema/Issue285Test.java
index ff868ccae..ce77eeeee 100644
--- a/src/test/java/com/networknt/schema/Issue285Test.java
+++ b/src/test/java/com/networknt/schema/Issue285Test.java
@@ -15,7 +15,7 @@ public class Issue285Test {
private ObjectMapper mapper = new ObjectMapper();
private JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
- .objectMapper(mapper)
+ .jsonMapper(mapper)
.schemaMappers(schemaMappers -> schemaMappers
.mapPrefix("http://json-schema.org", "resource:")
.mapPrefix("https://json-schema.org", "resource:"))
diff --git a/src/test/java/com/networknt/schema/Issue366FailFastTest.java b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
index 6267f51f1..eab951fc6 100644
--- a/src/test/java/com/networknt/schema/Issue366FailFastTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailFastTest.java
@@ -27,7 +27,7 @@ private void setupSchema() throws IOException {
schemaValidatorsConfig.setFailFast(true);
JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
- .objectMapper(objectMapper)
+ .jsonMapper(objectMapper)
.build();
schemaValidatorsConfig.setTypeLoose(false);
diff --git a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
index 4b6814909..7802d4f38 100644
--- a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
+++ b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java
@@ -26,7 +26,7 @@ private void setupSchema() throws IOException {
SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
JsonSchemaFactory schemaFactory = JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
- .objectMapper(objectMapper)
+ .jsonMapper(objectMapper)
.build();
schemaValidatorsConfig.setTypeLoose(false);
diff --git a/src/test/java/com/networknt/schema/Issue428Test.java b/src/test/java/com/networknt/schema/Issue428Test.java
index 5c9114329..c2d33188e 100644
--- a/src/test/java/com/networknt/schema/Issue428Test.java
+++ b/src/test/java/com/networknt/schema/Issue428Test.java
@@ -15,7 +15,7 @@
public class Issue428Test extends HTTPServiceSupport {
protected ObjectMapper mapper = new ObjectMapper();
protected JsonSchemaFactory validatorFactory = JsonSchemaFactory
- .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).objectMapper(mapper).build();
+ .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).jsonMapper(mapper).build();
private void runTestFile(String testCaseFile) throws Exception {
final SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + testCaseFile);
diff --git a/src/test/java/com/networknt/schema/Issue510Test.java b/src/test/java/com/networknt/schema/Issue510Test.java
index 6b251e34d..2e2da1c17 100644
--- a/src/test/java/com/networknt/schema/Issue510Test.java
+++ b/src/test/java/com/networknt/schema/Issue510Test.java
@@ -7,7 +7,7 @@ public class Issue510Test {
@Test
public void testIssue510() {
ObjectMapper objectMapper = new ObjectMapper();
- JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).objectMapper(objectMapper).build();
+ JsonSchemaFactory schemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).jsonMapper(objectMapper).build();
System.out.println("schemaFactory = " + schemaFactory);
}
}
diff --git a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
index 3c7fae236..157efc336 100644
--- a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java
@@ -15,7 +15,7 @@
public class OpenAPI30JsonSchemaTest extends HTTPServiceSupport {
protected ObjectMapper mapper = new ObjectMapper();
protected JsonSchemaFactory validatorFactory = JsonSchemaFactory
- .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).objectMapper(mapper).build();
+ .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).jsonMapper(mapper).build();
public OpenAPI30JsonSchemaTest() {
}
diff --git a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
index 7e6a6d49d..4b7e74265 100644
--- a/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
+++ b/src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
@@ -21,7 +21,7 @@ public void testSchema1() throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(this.json);
- JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).objectMapper(mapper).build();
+ JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
JsonSchema jsonSchema = factory.getSchema(schema1);
Set errors = jsonSchema.validate(jsonNode);
@@ -35,7 +35,7 @@ public void testSchema2() throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(this.json);
- JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).objectMapper(mapper).build();
+ JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
JsonSchema jsonSchema = factory.getSchema(schema2);
Set errors = jsonSchema.validate(jsonNode);
@@ -48,7 +48,7 @@ public void testSchema3() throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(this.json);
- JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).objectMapper(mapper).build();
+ JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).jsonMapper(mapper).build();
JsonSchema jsonSchema = factory.getSchema(schema3);
Set errors = jsonSchema.validate(jsonNode);
diff --git a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
index d0c15a17c..59397702a 100644
--- a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
+++ b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java
@@ -105,7 +105,7 @@ private Set validateFailingFastSchemaFor(final String schemaF
config.setFailFast(true);
return JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4))
- .objectMapper(objectMapper)
+ .jsonMapper(objectMapper)
.build()
.getSchema(schema, config)
.validate(dataFile);
diff --git a/src/test/java/com/networknt/schema/suite/TestSource.java b/src/test/java/com/networknt/schema/suite/TestSource.java
index 7d607e230..1d606d884 100644
--- a/src/test/java/com/networknt/schema/suite/TestSource.java
+++ b/src/test/java/com/networknt/schema/suite/TestSource.java
@@ -12,10 +12,11 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import com.networknt.schema.JsonMapperFactory;
public class TestSource {
protected static final TypeReference> testCaseType = new TypeReference