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 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.i18n.DefaultMessageSource;
import com.networknt.schema.i18n.MessageSource;
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;
public class SchemaValidatorsConfig {
/**
* Used to validate the acceptable $id values.
*/
private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT;
/**
* 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, validator process is stop immediately when a very first
* validation error is discovered.
*/
private boolean failFast;
/**
* When set to true, walker 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;
/**
* When set to true, use ECMA-262 compatible validator
*/
private boolean ecma262Validator;
/**
* When set to true, use Java-specific semantics rather than native JavaScript
* semantics
*/
private boolean javaSemantics;
/**
* When set to true, can interpret round doubles as integers
*/
private boolean losslessNarrowing;
/**
* When set to true, "messages" provided in schema are used for forming validation errors
* else default messages are used
*/
private boolean isCustomMessageSupported = true;
/**
* When set to true, support for discriminators is enabled for validations of
* oneOf, anyOf and allOf as described on GitHub.
*/
private boolean openAPI3StyleDiscriminators = false;
/**
* 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 = new HashMap<>(0);
/**
* 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 handleNullableField = true;
/**
* When set to true assumes that schema is used to validate incoming data from an API.
*/
private Boolean readOnly = null;
/**
* When set to true assumes that schema is used to to validate outgoing data from an API.
*/
private Boolean writeOnly = null;
/**
* 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;
// This is just a constant for listening to all Keywords.
public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener";
private final Map> keywordWalkListenersMap = new HashMap<>();
private final List propertyWalkListeners = new ArrayList<>();
private final List itemWalkListeners = new ArrayList<>();
private ExecutionContextCustomizer executionContextCustomizer;
private boolean loadCollectors = true;
/**
* The Locale to consider when loading validation messages from the default resource bundle.
*/
private Locale locale;
/**
* The message source to use for generating localised messages.
*/
private MessageSource messageSource;
/**
* Since Draft 2019-09 format assertions are not enabled by default.
*/
private Boolean formatAssertionsEnabled = null;
/************************ START OF UNEVALUATED CHECKS **********************************/
// 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();
return this;
}
public SchemaValidatorsConfig disableUnevaluatedItems() {
this.disableUnevaluatedItems = true;
return this;
}
public SchemaValidatorsConfig disableUnevaluatedProperties() {
this.disableUnevaluatedProperties = true;
return this;
}
public SchemaValidatorsConfig enableUnevaluatedAnalysis() {
enableUnevaluatedItems();
enableUnevaluatedProperties();
return this;
}
public SchemaValidatorsConfig enableUnevaluatedItems() {
this.disableUnevaluatedItems = false;
return this;
}
public SchemaValidatorsConfig enableUnevaluatedProperties() {
this.disableUnevaluatedProperties = false;
return this;
}
public boolean isUnevaluatedItemsAnalysisDisabled() {
return this.disableUnevaluatedItems;
}
public boolean isUnevaluatedItemsAnalysisEnabled() {
return !isUnevaluatedItemsAnalysisDisabled();
}
public boolean isUnevaluatedPropertiesAnalysisDisabled() {
return this.disableUnevaluatedProperties;
}
public boolean isUnevaluatedPropertiesAnalysisEnabled() {
return !isUnevaluatedPropertiesAnalysisDisabled();
}
/************************ END OF UNEVALUATED CHECKS **********************************/
/**
*
* @return true if type loose is used.
*/
public boolean isTypeLoose() {
return this.typeLoose;
}
public void setTypeLoose(boolean typeLoose) {
this.typeLoose = typeLoose;
}
/**
* 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;
}
public boolean isFailFast() {
return this.failFast;
}
public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) {
this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy
: ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
}
public ApplyDefaultsStrategy getApplyDefaultsStrategy() {
return this.applyDefaultsStrategy;
}
public boolean isHandleNullableField() {
return this.handleNullableField;
}
public void setHandleNullableField(boolean handleNullableField) {
this.handleNullableField = handleNullableField;
}
public boolean isEcma262Validator() {
return this.ecma262Validator;
}
public void setEcma262Validator(boolean ecma262Validator) {
this.ecma262Validator = ecma262Validator;
}
public boolean isJavaSemantics() {
return this.javaSemantics;
}
public void setJavaSemantics(boolean javaSemantics) {
this.javaSemantics = javaSemantics;
}
public boolean isCustomMessageSupported() {
return isCustomMessageSupported;
}
public void setCustomMessageSupported(boolean customMessageSupported) {
this.isCustomMessageSupported = customMessageSupported;
}
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 addPropertyWalkListeners(List propertyWalkListeners) {
this.propertyWalkListeners.addAll(propertyWalkListeners);
}
public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) {
this.propertyWalkListeners.add(propertyWalkListener);
}
public void addItemWalkListener(JsonSchemaWalkListener itemWalkListener) {
this.itemWalkListeners.add(itemWalkListener);
}
public void addItemWalkListeners(List itemWalkListeners) {
this.itemWalkListeners.addAll(itemWalkListeners);
}
public List getPropertyWalkListeners() {
return this.propertyWalkListeners;
}
public Map> getKeywordWalkListenersMap() {
return this.keywordWalkListenersMap;
}
public List getArrayItemWalkListeners() {
return this.itemWalkListeners;
}
private final WalkListenerRunner itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners());
WalkListenerRunner getItemWalkListenerRunner() {
return this.itemWalkListenerRunner;
}
private WalkListenerRunner keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap());
WalkListenerRunner getKeywordWalkListenerRunner() {
return this.keywordWalkListenerRunner;
}
private WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners());
WalkListenerRunner getPropertyWalkListenerRunner() {
return this.propertyWalkListenerRunner;
}
public SchemaValidatorsConfig() {
}
public ExecutionContextCustomizer getExecutionContextCustomizer() {
return this.executionContextCustomizer;
}
public void setExecutionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) {
this.executionContextCustomizer = executionContextCustomizer;
}
public boolean isLosslessNarrowing() {
return this.losslessNarrowing;
}
public void setLosslessNarrowing(boolean losslessNarrowing) {
this.losslessNarrowing = losslessNarrowing;
}
/**
* Indicates whether OpenAPI 3 style discriminators should be supported
*
* @return true in case discriminators are enabled
* @since 1.0.51
*/
public boolean isOpenAPI3StyleDiscriminators() {
return this.openAPI3StyleDiscriminators;
}
/**
* 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 or not discriminators should be
* used. Defaults to false
* @since 1.0.51
*/
public void setOpenAPI3StyleDiscriminators(boolean openAPI3StyleDiscriminators) {
this.openAPI3StyleDiscriminators = openAPI3StyleDiscriminators;
}
public void setLoadCollectors(boolean loadCollectors) {
this.loadCollectors = loadCollectors;
}
public boolean doLoadCollectors() {
return this.loadCollectors;
}
public boolean isReadOnly() {
return null != this.readOnly && this.readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public boolean isWriteOnly() {
return null != this.writeOnly && this.writeOnly;
}
public void setWriteOnly(boolean writeOnly) {
this.writeOnly = writeOnly;
}
/**
* Use {@code isReadOnly} or {@code isWriteOnly}
* @return true if schema is used to write data
*/
@Deprecated
public boolean isWriteMode() {
return null == this.writeOnly || this.writeOnly;
}
/**
* 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;
}
/**
* Get the approach used to generate paths in messages, logs and errors.
*
* @return The path generation approach.
*/
public PathType getPathType() {
return this.pathType;
}
/**
* 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);
}
/**
* 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);
}
/**
* 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;
}
/**
* 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)}.
*
* @param locale The locale.
*/
public void setLocale(Locale locale) {
this.locale = 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;
}
/**
* Set the message source to use for generating localised messages.
*
* @param messageSource the message source
*/
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
/**
* 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;
}
/**
* Gets the schema id validator to validate $id.
*
* @return the validator
*/
public JsonSchemaIdValidator getSchemaIdValidator() {
return schemaIdValidator;
}
/**
* Sets the schema id validator to validate $id.
*
* @param schemaIdValidator the validator
*/
public void setSchemaIdValidator(JsonSchemaIdValidator schemaIdValidator) {
this.schemaIdValidator = schemaIdValidator;
}
/**
* Gets if the schema should be preloaded.
*
* @return true if it should be preloaded
*/
public boolean isPreloadJsonSchema() {
return preloadJsonSchema;
}
/**
* Sets if the schema should be preloaded.
*
* @param preloadJsonSchema true to preload
*/
public void setPreloadJsonSchema(boolean preloadJsonSchema) {
this.preloadJsonSchema = preloadJsonSchema;
}
}