com.networknt.schema.SchemaValidatorsConfig Maven / Gradle / Ivy
Show all versions of json-schema-validator Show documentation
/*
* Copyright (c) 2016 Network New Technologies Inc.
*
* Licensed under the Apache LicenseBuilder 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.i18n.DefaultMessageSource;
import com.networknt.schema.i18n.MessageSource;
import com.networknt.schema.regex.ECMAScriptRegularExpressionFactory;
import com.networknt.schema.regex.JDKRegularExpressionFactory;
import com.networknt.schema.regex.RegularExpressionFactory;
import com.networknt.schema.walk.DefaultItemWalkListenerRunner;
import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
import com.networknt.schema.walk.DefaultPropertyWalkListenerRunner;
import com.networknt.schema.walk.JsonSchemaWalkListener;
import com.networknt.schema.walk.WalkListenerRunner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Configuration for validators.
*/
public class SchemaValidatorsConfig {
// This is just a constant for listening to all Keywords.
public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener";
public static final int DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH = 40;
/**
* The strategy the walker uses to sets nodes that are missing or NullNode to
* the default value, if any, and mutate the input json.
*/
private ApplyDefaultsStrategy applyDefaultsStrategy = ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
/**
* Controls if schemas loaded from refs will be cached and reused for subsequent runs.
*/
private boolean cacheRefs = true;
/**
* When set to true, "messages" provided in schema are used for forming validation errors
* else default messages are used
*/
private String errorMessageKeyword = "message";
private ExecutionContextCustomizer executionContextCustomizer;
/**
* When set to true, validator process is stop immediately when a very first
* validation error is discovered.
*/
private boolean failFast;
/**
* Since Draft 2019-09 format assertions are not enabled by default.
*/
private Boolean formatAssertionsEnabled = null;
/**
* When a field is set as nullable in the OpenAPI specification, the schema
* validator validates that it is nullable however continues with validation
* against the nullable field
*
* If handleNullableField is set to true && incoming field is nullable && value
* is field: null --> succeed If handleNullableField is set to false && incoming
* field is nullable && value is field: null --> it is up to the type validator
* using the SchemaValidator to handle it.
*/
private boolean nullableKeywordEnabled = true;
private final WalkListenerRunner itemWalkListenerRunner;
private final List itemWalkListeners;
/**
* When set to true, use Java-specific semantics rather than native JavaScript
* semantics
*/
private boolean javaSemantics;
private final WalkListenerRunner keywordWalkListenerRunner;
private final Map> keywordWalkListenersMap;
/**
* The Locale to consider when loading validation messages from the default resource bundle.
*/
private Locale locale;
/**
* When set to true, can interpret round doubles as integers
*/
private boolean losslessNarrowing;
/**
* The message source to use for generating localised messages.
*/
private MessageSource messageSource;
/**
* When set to true, support for discriminators is enabled for validations of
* oneOf, anyOf and allOf as described on GitHub.
*/
private boolean discriminatorKeywordEnabled = false;
/**
* The approach used to generate paths in reported messages, logs and errors. Default is the legacy "JSONPath-like" approach.
*/
private PathType pathType = PathType.DEFAULT;
/**
* Controls if the schema will automatically be preloaded.
*/
private boolean preloadJsonSchema = true;
/**
* Controls the max depth of the evaluation path to preload when preloading refs.
*/
private int preloadJsonSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH;
private final WalkListenerRunner propertyWalkListenerRunner;
private final List propertyWalkListeners;
/**
* When set to true assumes that schema is used to validate incoming data from an API.
*/
private Boolean readOnly = null;
/**
* Used to create {@link com.networknt.schema.regex.RegularExpression}.
*/
private RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance();
/**
* Used to validate the acceptable $id values.
*/
private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT;
/**
* Contains a mapping of how strict a keyword's validators should be.
* Defaults to {@literal true}.
*
* Each validator has its own understanding of what constitutes strict
* and permissive.
*/
private final Map strictness;
/**
* when validate type, if TYPE_LOOSE = true, will try to convert string to
* different types to match the type defined in schema.
*/
private boolean typeLoose;
/**
* When set to true assumes that schema is used to validate outgoing data from an API.
*/
private Boolean writeOnly = null;
/**
* Constructor to create an instance.
*
* This is deprecated in favor of using the builder
* {@link SchemaValidatorsConfig#builder()} to create an instance. Migration
* note: The builder has different defaults from the constructor.
*
* SchemaValidatorsConfig config = SchemaValidatorsConfig.builder()
* .pathType(PathType.LEGACY)
* .errorMessageKeyword("message")
* .nullableKeywordEnabled(true)
* .build();
*
*
* - customMessageSupported (errorMessageKeyword): change from message to null
*
- pathType: changed from PathType.LEGACY to PathType.JSON_POINTER.
*
- handleNullableField (nullableKeywordEnabled): changed from true to false
*
*/
@Deprecated
public SchemaValidatorsConfig() {
this.strictness = new HashMap<>(0);
this.keywordWalkListenersMap = new HashMap<>();
this.propertyWalkListeners = new ArrayList<>();
this.itemWalkListeners = new ArrayList<>();
this.itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners());
this.keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap());
this.propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners());
}
SchemaValidatorsConfig(ApplyDefaultsStrategy applyDefaultsStrategy, boolean cacheRefs,
String errorMessageKeyword, ExecutionContextCustomizer executionContextCustomizer, boolean failFast,
Boolean formatAssertionsEnabled, boolean nullableKeywordEnabled,
List itemWalkListeners, boolean javaSemantics,
Map> keywordWalkListenersMap, Locale locale, boolean losslessNarrowing,
MessageSource messageSource, boolean discriminatorKeywordEnabled, PathType pathType,
boolean preloadJsonSchema, int preloadJsonSchemaRefMaxNestingDepth,
List propertyWalkListeners, Boolean readOnly,
RegularExpressionFactory regularExpressionFactory, JsonSchemaIdValidator schemaIdValidator,
Map strictness, boolean typeLoose, Boolean writeOnly) {
super();
this.applyDefaultsStrategy = applyDefaultsStrategy;
this.cacheRefs = cacheRefs;
this.errorMessageKeyword = errorMessageKeyword;
this.executionContextCustomizer = executionContextCustomizer;
this.failFast = failFast;
this.formatAssertionsEnabled = formatAssertionsEnabled;
this.nullableKeywordEnabled = nullableKeywordEnabled;
this.itemWalkListeners = itemWalkListeners;
this.javaSemantics = javaSemantics;
this.keywordWalkListenersMap = keywordWalkListenersMap;
this.locale = locale;
this.losslessNarrowing = losslessNarrowing;
this.messageSource = messageSource;
this.discriminatorKeywordEnabled = discriminatorKeywordEnabled;
this.pathType = pathType;
this.preloadJsonSchema = preloadJsonSchema;
this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth;
this.propertyWalkListeners = propertyWalkListeners;
this.readOnly = readOnly;
this.regularExpressionFactory = regularExpressionFactory;
this.schemaIdValidator = schemaIdValidator;
this.strictness = strictness;
this.typeLoose = typeLoose;
this.writeOnly = writeOnly;
this.itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners());
this.keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap());
this.propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners());
}
public void addItemWalkListener(JsonSchemaWalkListener itemWalkListener) {
this.itemWalkListeners.add(itemWalkListener);
}
public void addItemWalkListeners(List itemWalkListeners) {
this.itemWalkListeners.addAll(itemWalkListeners);
}
public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) {
if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
List keywordWalkListeners = new ArrayList<>();
this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListeners);
}
this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).add(keywordWalkListener);
}
public void addKeywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) {
if (this.keywordWalkListenersMap.get(keyword) == null) {
List keywordWalkListeners = new ArrayList<>();
this.keywordWalkListenersMap.put(keyword, keywordWalkListeners);
}
this.keywordWalkListenersMap.get(keyword).add(keywordWalkListener);
}
public void addKeywordWalkListeners(List keywordWalkListeners) {
if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) {
List ikeywordWalkListeners = new ArrayList<>();
this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, ikeywordWalkListeners);
}
this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).addAll(keywordWalkListeners);
}
public void addKeywordWalkListeners(String keyword, List keywordWalkListeners) {
if (this.keywordWalkListenersMap.get(keyword) == null) {
List ikeywordWalkListeners = new ArrayList<>();
this.keywordWalkListenersMap.put(keyword, ikeywordWalkListeners);
}
this.keywordWalkListenersMap.get(keyword).addAll(keywordWalkListeners);
}
public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) {
this.propertyWalkListeners.add(propertyWalkListener);
}
public void addPropertyWalkListeners(List propertyWalkListeners) {
this.propertyWalkListeners.addAll(propertyWalkListeners);
}
public ApplyDefaultsStrategy getApplyDefaultsStrategy() {
return this.applyDefaultsStrategy;
}
public List getArrayItemWalkListeners() {
return this.itemWalkListeners;
}
public ExecutionContextCustomizer getExecutionContextCustomizer() {
return this.executionContextCustomizer;
}
/**
* 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;
}
WalkListenerRunner getItemWalkListenerRunner() {
return this.itemWalkListenerRunner;
}
WalkListenerRunner getKeywordWalkListenerRunner() {
return this.keywordWalkListenerRunner;
}
public Map> getKeywordWalkListenersMap() {
return this.keywordWalkListenersMap;
}
/**
* Get the locale to consider when generating localised messages (default is the
* JVM default).
*
* This locale is on a schema basis and will be used as the default locale for
* {@link com.networknt.schema.ExecutionConfig}.
*
* @return The locale.
*/
public Locale getLocale() {
if (this.locale == null) {
// This should not be cached as it can be changed using Locale#setDefault(Locale)
return Locale.getDefault();
}
return this.locale;
}
/**
* Get the message source to use for generating localised messages.
*
* @return the message source
*/
public MessageSource getMessageSource() {
if (this.messageSource == null) {
return DefaultMessageSource.getInstance();
}
return this.messageSource;
}
/**
* Get the approach used to generate paths in messages, logs and errors.
*
* @return The path generation approach.
*/
public PathType getPathType() {
return this.pathType;
}
/**
* Gets the max depth of the evaluation path to preload when preloading refs.
*
* @return the max depth to preload
*/
public int getPreloadJsonSchemaRefMaxNestingDepth() {
return preloadJsonSchemaRefMaxNestingDepth;
}
WalkListenerRunner getPropertyWalkListenerRunner() {
return this.propertyWalkListenerRunner;
}
public List getPropertyWalkListeners() {
return this.propertyWalkListeners;
}
/**
* Gets the regular expression factory.
*
* This defaults to the JDKRegularExpressionFactory and the implementations
* require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies.
*
* @return the factory
*/
public RegularExpressionFactory getRegularExpressionFactory() {
return regularExpressionFactory;
}
/**
* Gets the schema id validator to validate $id.
*
* @return the validator
*/
public JsonSchemaIdValidator getSchemaIdValidator() {
return schemaIdValidator;
}
/**
* Gets if schemas loaded from refs will be cached and reused for subsequent
* runs.
*
* @return true if schemas loaded from refs should be cached
*/
public boolean isCacheRefs() {
return cacheRefs;
}
@Deprecated
public boolean isCustomMessageSupported() {
return this.errorMessageKeyword != null;
}
public String getErrorMessageKeyword() {
return this.errorMessageKeyword;
}
/**
* Gets whether to use a ECMA-262 compliant regular expression validator.
*
* This defaults to the false and setting true require inclusion of optional
* org.jruby.joni:joni or org.graalvm.js:js dependencies.
*
* @return true if ECMA-262 compliant
*/
public boolean isEcma262Validator() {
return !(this.regularExpressionFactory instanceof JDKRegularExpressionFactory);
}
public boolean isFailFast() {
return this.failFast;
}
/**
* Deprecated use {{@link #isNullableKeywordEnabled()} instead.
*
* @return true if the nullable keyword is enabled
*/
@Deprecated
public boolean isHandleNullableField() {
return isNullableKeywordEnabled();
}
/**
* Gets if the nullable keyword is enabled.
*
* @return true if the nullable keyword is enabled
*/
public boolean isNullableKeywordEnabled() {
return this.nullableKeywordEnabled;
}
public boolean isJavaSemantics() {
return this.javaSemantics;
}
public boolean isLosslessNarrowing() {
return this.losslessNarrowing;
}
/**
* Indicates whether OpenAPI 3 style discriminators should be supported
*
* Deprecated use {{@link #isDiscriminatorKeywordEnabled()} instead.
*
* @return true in case discriminators are enabled
* @since 1.0.51
*/
@Deprecated
public boolean isOpenAPI3StyleDiscriminators() {
return isDiscriminatorKeywordEnabled();
}
/**
* Gets if the discriminator keyword is enabled.
*
* @return true if the discriminator keyword is enabled
*/
public boolean isDiscriminatorKeywordEnabled() {
return this.discriminatorKeywordEnabled;
}
/**
* Gets if the schema should be preloaded.
*
* @return true if it should be preloaded
*/
public boolean isPreloadJsonSchema() {
return preloadJsonSchema;
}
public boolean isReadOnly() {
return null != this.readOnly && this.readOnly;
}
/**
* Answers whether a keyword's validators may relax their analysis. The
* default is to perform strict checking. One must explicitly allow a
* validator to be more permissive.
*
* Each validator has its own understanding of what is permissive and
* strict. Consult the keyword's documentation for details.
*
* @param keyword the keyword to adjust (not null)
* @return Whether to perform a strict validation.
*/
public boolean isStrict(String keyword) {
return isStrict(keyword, Boolean.TRUE);
}
/**
* Determines if the validator should perform strict checking.
*
* @param keyword the keyword
* @param defaultValue the default value
* @return whether to perform a strict validation
*/
public boolean isStrict(String keyword, Boolean defaultValue) {
return this.strictness.getOrDefault(Objects.requireNonNull(keyword, "keyword cannot be null"), defaultValue);
}
/**
*
* @return true if type loose is used.
*/
public boolean isTypeLoose() {
return this.typeLoose;
}
public boolean isWriteOnly() {
return null != this.writeOnly && this.writeOnly;
}
public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) {
this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy
: ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
}
/**
* Sets if schemas loaded from refs will be cached and reused for subsequent
* runs.
*
* Note that setting this to false will affect performance as refs will need to
* be repeatedly resolved for each evaluation run. It may be needed to be set to
* false if there are multiple nested applicators like anyOf, oneOf and allOf as
* that will consume a lot of memory to cache all the permutations.
*
* @param cacheRefs true to cache
*/
public void setCacheRefs(boolean cacheRefs) {
this.cacheRefs = cacheRefs;
}
/**
* Sets whether custom error messages in the schema are used.
*
* This is deprecated in favor of setting the error message keyword to use.
*
* @param customMessageSupported true to use message as the error message keyword
*/
@Deprecated
public void setCustomMessageSupported(boolean customMessageSupported) {
this.errorMessageKeyword = customMessageSupported ? "message" : null;
}
/**
* Sets whether to use a ECMA-262 compliant regular expression validator.
*
* This defaults to the false and setting true require inclusion of optional
* org.jruby.joni:joni or org.graalvm.js:js dependencies.
*
* @param ecma262Validator true if ECMA-262 compliant
*/
public void setEcma262Validator(boolean ecma262Validator) {
this.regularExpressionFactory = ecma262Validator ? ECMAScriptRegularExpressionFactory.getInstance()
: JDKRegularExpressionFactory.getInstance();
}
public void setExecutionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) {
this.executionContextCustomizer = executionContextCustomizer;
}
/**
* When enabled,
* {@link JsonValidator#validate(ExecutionContext, JsonNode, JsonNode, JsonNodePath)}
* doesn't return any {@link java.util.Set}<{@link ValidationMessage}>,
* instead a {@link JsonSchemaException} is thrown as soon as a validation
* errors is discovered.
*
* @param failFast boolean
*/
public void setFailFast(final boolean failFast) {
this.failFast = failFast;
}
/**
* Sets the format assertion enabled flag.
*
* This is deprecated. Either set this using the builder
* SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build() or
* this should be set via
* executionContext.getExecutionConfig().setFormatAssertionsEnabled(true).
*
* @param formatAssertionsEnabled the format assertions enabled flag
*/
@Deprecated
public void setFormatAssertionsEnabled(Boolean formatAssertionsEnabled) {
this.formatAssertionsEnabled = formatAssertionsEnabled;
}
public void setHandleNullableField(boolean handleNullableField) {
this.nullableKeywordEnabled = handleNullableField;
}
public void setJavaSemantics(boolean javaSemantics) {
this.javaSemantics = javaSemantics;
}
/**
* Set the locale to consider when generating localised messages.
*
* Note that this locale is set on a schema basis. To configure the schema on a
* per execution basis use
* {@link com.networknt.schema.ExecutionConfig#setLocale(Locale)}.
*
* This is deprecated. Either set this using the builder
* SchemaValidatorsConfig.builder().locale(locale).build() or this should be set
* via executionContext.getExecutionConfig().setLocale(locale).
*
* @param locale The locale.
*/
@Deprecated
public void setLocale(Locale locale) {
this.locale = locale;
}
public void setLosslessNarrowing(boolean losslessNarrowing) {
this.losslessNarrowing = losslessNarrowing;
}
/**
* Set the message source to use for generating localised messages.
*
* @param messageSource the message source
*/
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
/**
* When enabled, the validation of anyOf
and allOf
in
* polymorphism will respect OpenAPI 3 style discriminators as described in the
* OpenAPI
* 3.0.3 spec. The presence of a discriminator configuration on the schema
* will lead to the following changes in the behavior:
*
* - for
oneOf
the spec is unfortunately very vague. Whether
* oneOf
semantics should be affected by discriminators or not is
* not even 100% clear within the members of the OAS steering committee.
* Therefore oneOf
at the moment ignores discriminators
* - for
anyOf
the validation will choose one of the candidate
* schemas for validation based on the discriminator property value and will
* pass validation when this specific schema passes. This is in particular
* useful when the payload could match multiple candidates in the
* anyOf
list and could lead to ambiguity. Example: type B has all
* mandatory properties of A and adds more mandatory ones. Whether the payload
* is an A or B is determined via the discriminator property name. A payload
* indicating it is an instance of B then requires passing the validation of B
* and passing the validation of A would not be sufficient anymore.
* - for
allOf
use cases with discriminators defined on the
* copied-in parent type, it is possible to automatically validate against a
* subtype. Example: some schema specifies that there is a field of type A. A
* carries a discriminator field and B inherits from A. Then B is automatically
* a candidate for validation as well and will be chosen in case the
* discriminator property matches
*
*
* @param openAPI3StyleDiscriminators whether discriminators should be used.
* Defaults to false
* @since 1.0.51
*/
public void setOpenAPI3StyleDiscriminators(boolean openAPI3StyleDiscriminators) {
this.discriminatorKeywordEnabled = openAPI3StyleDiscriminators;
}
/**
* Set the approach used to generate paths in messages, logs and errors (default is PathType.LEGACY).
*
* @param pathType The path generation approach.
*/
public void setPathType(PathType pathType) {
this.pathType = pathType;
}
/**
* Sets if the schema should be preloaded.
*
* @param preloadJsonSchema true to preload
*/
public void setPreloadJsonSchema(boolean preloadJsonSchema) {
this.preloadJsonSchema = preloadJsonSchema;
}
/**
* Sets the max depth of the evaluation path to preload when preloading refs.
*
* @param preloadJsonSchemaRefMaxNestingDepth the max depth to preload
*/
public void setPreloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) {
this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
/**
* Sets the regular expression factory.
*
* This defaults to the JDKRegularExpressionFactory and the implementations
* require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies.
*
* @see JDKRegularExpressionFactory
* @see ECMAScriptRegularExpressionFactory
* @param regularExpressionFactory the factory
*/
public void setRegularExpressionFactory(RegularExpressionFactory regularExpressionFactory) {
this.regularExpressionFactory = regularExpressionFactory;
}
/**
* Sets the schema id validator to validate $id.
*
* @param schemaIdValidator the validator
*/
public void setSchemaIdValidator(JsonSchemaIdValidator schemaIdValidator) {
this.schemaIdValidator = schemaIdValidator;
}
/**
* Alters the strictness of validations for a specific keyword. When set to
* {@literal true}, instructs the keyword's validators to perform strict
* validation. Otherwise, a validator may perform a more permissive check.
*
* @param keyword The keyword to adjust (not null)
* @param strict Whether to perform strict validations
*/
public void setStrict(String keyword, boolean strict) {
this.strictness.put(Objects.requireNonNull(keyword, "keyword cannot be null"), strict);
}
public void setTypeLoose(boolean typeLoose) {
this.typeLoose = typeLoose;
}
public void setWriteOnly(boolean writeOnly) {
this.writeOnly = writeOnly;
}
public static Builder builder() {
return new Builder();
}
public static Builder builder(SchemaValidatorsConfig config) {
Builder builder = new Builder();
builder.applyDefaultsStrategy = config.applyDefaultsStrategy;
builder.cacheRefs = config.cacheRefs;
builder.errorMessageKeyword = config.errorMessageKeyword;
builder.executionContextCustomizer = config.executionContextCustomizer;
builder.failFast = config.failFast;
builder.formatAssertionsEnabled = config.formatAssertionsEnabled;
builder.nullableKeywordEnabled = config.nullableKeywordEnabled;
builder.itemWalkListeners = config.itemWalkListeners;
builder.javaSemantics = config.javaSemantics;
builder.keywordWalkListeners = config.keywordWalkListenersMap;
builder.locale = config.locale;
builder.losslessNarrowing = config.losslessNarrowing;
builder.messageSource = config.messageSource;
builder.discriminatorKeywordEnabled = config.discriminatorKeywordEnabled;
builder.pathType = config.pathType;
builder.preloadJsonSchema = config.preloadJsonSchema;
builder.preloadJsonSchemaRefMaxNestingDepth = config.preloadJsonSchemaRefMaxNestingDepth;
builder.propertyWalkListeners = config.propertyWalkListeners;
builder.readOnly = config.readOnly;
builder.regularExpressionFactory = config.regularExpressionFactory;
builder.schemaIdValidator = config.schemaIdValidator;
builder.strictness = config.strictness;
builder.typeLoose = config.typeLoose;
builder.writeOnly = config.writeOnly;
return builder;
}
/**
* Builder for {@link SchemaValidatorsConfig}.
*/
public static class Builder {
private ApplyDefaultsStrategy applyDefaultsStrategy = ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
private boolean cacheRefs = true;
private String errorMessageKeyword = null;
private ExecutionContextCustomizer executionContextCustomizer = null;
private boolean failFast = false;
private Boolean formatAssertionsEnabled = false;
private boolean nullableKeywordEnabled = false;
private List itemWalkListeners = new ArrayList<>();
private boolean javaSemantics = false;
private Map> keywordWalkListeners = new HashMap<>();
private Locale locale = null; // This must be null to use Locale.getDefault() as the default can be changed
private boolean losslessNarrowing = false;
private MessageSource messageSource = null;
private boolean discriminatorKeywordEnabled = false;
private PathType pathType = PathType.JSON_POINTER;
private boolean preloadJsonSchema = true;
private int preloadJsonSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH;
private List propertyWalkListeners = new ArrayList<>();
private Boolean readOnly = null;
private RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance();
private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT;
private Map strictness = new HashMap<>(0);
private boolean typeLoose = false;
private Boolean writeOnly = null;
/**
* Sets the strategy the walker uses to sets nodes to the default value.
*
* Defaults to {@link ApplyDefaultsStrategy#EMPTY_APPLY_DEFAULTS_STRATEGY}.
*
* @param applyDefaultsStrategy the strategy
* @return the builder
*/
public Builder applyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) {
this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy
: ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
return this;
}
/**
* Sets if schemas loaded from refs will be cached and reused for subsequent runs.
*
* Defaults to true.
*
* @param cacheRefs true to cache
* @return the builder
*/
public Builder cacheRefs(boolean cacheRefs) {
this.cacheRefs = cacheRefs;
return this;
}
/**
* Sets the error message keyword for setting custom messages in the schema.
*
* Defaults to null meaning custom messages are not enabled.
*
* @param errorMessageKeyword to use for custom messages in the schema
* @return the builder
*/
public Builder errorMessageKeyword(String errorMessageKeyword) {
this.errorMessageKeyword = errorMessageKeyword;
return this;
}
/**
* Sets the execution context customizer that is run before each run.
*
* @param executionContextCustomizer the customizer
* @return the builder
*/
public Builder executionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) {
this.executionContextCustomizer = executionContextCustomizer;
return this;
}
/**
* Sets if the validation should immediately return once a validation error has
* occurred. This can improve performance if inputs are invalid but cannot
* return all error messages to the caller.
*
* Defaults to false.
*
* @param failFast true to enable
* @return the builder
*/
public Builder failFast(boolean failFast) {
this.failFast = failFast;
return this;
}
/**
* Sets if format assertions are enabled. If format assertions are not enabled
* the format keyword will behave like an annotation and not attempt to validate
* if the inputs are valid.
*
* Defaults to not enabling format assertions for Draft 2019-09 and above and
* enabling format assertions for Draft 7 and below.
*
* @param formatAssertionsEnabled true to enable
* @return the builder
*/
public Builder formatAssertionsEnabled(Boolean formatAssertionsEnabled) {
this.formatAssertionsEnabled = formatAssertionsEnabled;
return this;
}
/**
* Sets if the nullable keyword is enabled.
*
* @param nullableKeywordEnabled true to enable
* @return the builder
*/
public Builder nullableKeywordEnabled(boolean nullableKeywordEnabled) {
this.nullableKeywordEnabled = nullableKeywordEnabled;
return this;
}
public Builder itemWalkListeners(List itemWalkListeners) {
this.itemWalkListeners = itemWalkListeners;
return this;
}
public Builder javaSemantics(boolean javaSemantics) {
this.javaSemantics = javaSemantics;
return this;
}
public Builder keywordWalkListeners(Map> keywordWalkListeners) {
this.keywordWalkListeners = keywordWalkListeners;
return this;
}
/**
* Set the locale to consider when generating localised messages.
*
* Note that this locale is set on a schema basis. To configure the schema on a
* per execution basis use
* {@link com.networknt.schema.ExecutionConfig#setLocale(Locale)}.
*
* Defaults to use {@link Locale#getDefault()}.
*
* @param locale The locale.
* @return the builder
*/
public Builder locale(Locale locale) {
this.locale = locale;
return this;
}
public Builder losslessNarrowing(boolean losslessNarrowing) {
this.losslessNarrowing = losslessNarrowing;
return this;
}
/**
* Sets the message source to use for generating localised messages.
*
* @param messageSource the message source
* @return the builder
*/
public Builder messageSource(MessageSource messageSource) {
this.messageSource = messageSource;
return this;
}
/**
* Sets if the discriminator keyword is enabled.
*
* Defaults to false.
*
* @param discriminatorKeywordEnabled true to enable
* @return the builder
*/
public Builder discriminatorKeywordEnabled(boolean discriminatorKeywordEnabled) {
this.discriminatorKeywordEnabled = discriminatorKeywordEnabled;
return this;
}
/**
* Sets the path type to use when reporting the instance location of errors.
*
* Defaults to {@link PathType#JSON_POINTER}.
*
* @param pathType the path type
* @return the path type
*/
public Builder pathType(PathType pathType) {
this.pathType = pathType;
return this;
}
/**
* Sets if the schema should be preloaded.
*
* Defaults to true.
*
* @param preloadJsonSchema true to preload
* @return the builder
*/
public Builder preloadJsonSchema(boolean preloadJsonSchema) {
this.preloadJsonSchema = preloadJsonSchema;
return this;
}
/**
* Sets the max depth of the evaluation path to preload when preloading refs.
*
* Defaults to 40.
*
* @param preloadJsonSchemaRefMaxNestingDepth to preload
* @return the builder
*/
public Builder preloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) {
this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth;
return this;
}
public Builder propertyWalkListeners(List propertyWalkListeners) {
this.propertyWalkListeners = propertyWalkListeners;
return this;
}
public Builder readOnly(Boolean readOnly) {
this.readOnly = readOnly;
return this;
}
/**
* Sets the regular expression factory.
*
* Defaults to the {@link JDKRegularExpressionFactory}
*
* The {@link ECMAScriptRegularExpressionFactory} requires the inclusion of
* optional org.jruby.joni:joni or org.graalvm.js:js dependencies.
*
* @see JDKRegularExpressionFactory
* @see ECMAScriptRegularExpressionFactory
* @param regularExpressionFactory the factory
* @return the builder
*/
public Builder regularExpressionFactory(RegularExpressionFactory regularExpressionFactory) {
this.regularExpressionFactory = regularExpressionFactory;
return this;
}
/**
* Sets the schema id validator to use.
*
* Defaults to {@link JsonSchemaIdValidator#DEFAULT}.
*
* @param schemaIdValidator the builder
* @return the builder
*/
public Builder schemaIdValidator(JsonSchemaIdValidator schemaIdValidator) {
this.schemaIdValidator = schemaIdValidator;
return this;
}
public Builder strict(Map strict) {
this.strictness = strict;
return this;
}
public Builder typeLoose(boolean typeLoose) {
this.typeLoose = typeLoose;
return this;
}
public Builder writeOnly(Boolean writeOnly) {
this.writeOnly = writeOnly;
return this;
}
public SchemaValidatorsConfig build() {
return new ImmutableSchemaValidatorsConfig(applyDefaultsStrategy, cacheRefs, errorMessageKeyword,
executionContextCustomizer, failFast, formatAssertionsEnabled, nullableKeywordEnabled,
itemWalkListeners, javaSemantics, keywordWalkListeners, locale, losslessNarrowing, messageSource,
discriminatorKeywordEnabled, pathType, preloadJsonSchema, preloadJsonSchemaRefMaxNestingDepth,
propertyWalkListeners, readOnly, regularExpressionFactory, schemaIdValidator, strictness, typeLoose,
writeOnly);
}
public Builder strict(String keyword, boolean strict) {
this.strictness.put(Objects.requireNonNull(keyword, "keyword cannot be null"), strict);
return this;
}
public Builder keywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) {
this.keywordWalkListeners.computeIfAbsent(keyword, key -> new ArrayList<>()).add(keywordWalkListener);
return this;
}
public Builder keywordWalkListener(JsonSchemaWalkListener keywordWalkListener) {
return keywordWalkListener(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListener);
}
public Builder keywordWalkListeners(Consumer