org.mockito
mockito-core
diff --git a/src/main/java/com/networknt/schema/ApplyDefaultsStrategy.java b/src/main/java/com/networknt/schema/ApplyDefaultsStrategy.java
new file mode 100644
index 000000000..391a160a0
--- /dev/null
+++ b/src/main/java/com/networknt/schema/ApplyDefaultsStrategy.java
@@ -0,0 +1,43 @@
+package com.networknt.schema;
+
+public class ApplyDefaultsStrategy {
+ static final ApplyDefaultsStrategy EMPTY_APPLY_DEFAULTS_STRATEGY = new ApplyDefaultsStrategy(false, false, false);
+
+ private final boolean applyPropertyDefaults;
+ private final boolean applyPropertyDefaultsIfNull;
+ private final boolean applyArrayDefaults;
+
+ /**
+ * Specify which default values to apply.
+ * We can apply property defaults only if they are missing or if they are declared to be null in the input json,
+ * and we can apply array defaults if they are declared to be null in the input json.
+ *
+ * Note that the walker changes the input object in place.
+ * If validation fails, the input object will be changed.
+ *
+ * @param applyPropertyDefaults if true then apply defaults inside json objects if the attribute is missing
+ * @param applyPropertyDefaultsIfNull if true then apply defaults inside json objects if the attribute is explicitly null
+ * @param applyArrayDefaults if true then apply defaults inside json arrays if the attribute is explicitly null
+ * @throws IllegalArgumentException if applyPropertyDefaults is false and applyPropertyDefaultsIfNull is true
+ */
+ public ApplyDefaultsStrategy(boolean applyPropertyDefaults, boolean applyPropertyDefaultsIfNull, boolean applyArrayDefaults) {
+ if (!applyPropertyDefaults && applyPropertyDefaultsIfNull) {
+ throw new IllegalArgumentException();
+ }
+ this.applyPropertyDefaults = applyPropertyDefaults;
+ this.applyPropertyDefaultsIfNull = applyPropertyDefaultsIfNull;
+ this.applyArrayDefaults = applyArrayDefaults;
+ }
+
+ public boolean shouldApplyPropertyDefaults() {
+ return applyPropertyDefaults;
+ }
+
+ public boolean shouldApplyPropertyDefaultsIfNull() {
+ return applyPropertyDefaultsIfNull;
+ }
+
+ public boolean shouldApplyArrayDefaults() {
+ return applyArrayDefaults;
+ }
+}
diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java
index 3aa9a0b39..89283e386 100644
--- a/src/main/java/com/networknt/schema/BaseJsonValidator.java
+++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java
@@ -38,16 +38,29 @@ public abstract class BaseJsonValidator implements JsonValidator {
private ErrorMessageType errorMessageType;
protected final boolean failFast;
+ protected final ApplyDefaultsStrategy applyDefaultsStrategy;
public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidatorTypeCode validatorType, ValidationContext validationContext) {
this(schemaPath, schemaNode, parentSchema, validatorType, false,
- validationContext.getConfig() != null && validationContext.getConfig().isFailFast());
+ validationContext.getConfig() != null && validationContext.getConfig().isFailFast(),
+ validationContext.getConfig() != null ? validationContext.getConfig().getApplyDefaultsStrategy() : null);
}
+ // TODO: can this be made package private?
+ @Deprecated // use the BaseJsonValidator below
public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
ValidatorTypeCode validatorType, boolean suppressSubSchemaRetrieval, boolean failFast) {
+ this(schemaPath, schemaNode, parentSchema, validatorType, false, failFast, null);
+ }
+ public BaseJsonValidator(String schemaPath,
+ JsonNode schemaNode,
+ JsonSchema parentSchema,
+ ValidatorTypeCode validatorType,
+ boolean suppressSubSchemaRetrieval,
+ boolean failFast,
+ ApplyDefaultsStrategy applyDefaultsStrategy) {
this.errorMessageType = validatorType;
this.schemaPath = schemaPath;
this.schemaNode = schemaNode;
@@ -55,6 +68,7 @@ public BaseJsonValidator(String schemaPath, JsonNode schemaNode, JsonSchema pare
this.validatorType = validatorType;
this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval;
this.failFast = failFast;
+ this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy : ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY;
}
public String getSchemaPath() {
diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java
index 69abc8bab..80c70426a 100644
--- a/src/main/java/com/networknt/schema/ItemsValidator.java
+++ b/src/main/java/com/networknt/schema/ItemsValidator.java
@@ -17,6 +17,7 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.networknt.schema.walk.DefaultItemWalkListenerRunner;
import com.networknt.schema.walk.WalkListenerRunner;
@@ -118,9 +119,21 @@ private void doValidate(Set errors, int i, JsonNode node, Jso
@Override
public Set walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
HashSet validationMessages = new LinkedHashSet();
- if (node != null && node.isArray()) {
+ if (node instanceof ArrayNode) {
+ ArrayNode arrayNode = (ArrayNode) node;
+ JsonNode defaultNode = null;
+ if (applyDefaultsStrategy.shouldApplyArrayDefaults() && schema != null) {
+ defaultNode = schema.getSchemaNode().get("default");
+ if (defaultNode != null && defaultNode.isNull()) {
+ defaultNode = null;
+ }
+ }
int i = 0;
- for (JsonNode n : node) {
+ for (JsonNode n : arrayNode) {
+ if (n.isNull() && defaultNode != null) {
+ arrayNode.set(i, defaultNode);
+ n = defaultNode;
+ }
doWalk(validationMessages, i, n, rootNode, at, shouldValidateSchema);
i++;
}
diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java
index a4de67329..8d52d0647 100644
--- a/src/main/java/com/networknt/schema/JsonSchema.java
+++ b/src/main/java/com/networknt/schema/JsonSchema.java
@@ -19,14 +19,15 @@
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
-import java.sql.Ref;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -37,8 +38,6 @@
import com.networknt.schema.walk.JsonSchemaWalker;
import com.networknt.schema.walk.WalkListenerRunner;
-import javax.xml.validation.Schema;
-
/**
* This is the core of json constraint implementation. It parses json constraint
* file and generates JsonValidators. The class is thread safe, once it is
@@ -80,7 +79,8 @@ public JsonSchema(ValidationContext validationContext, URI baseUri, JsonNode sch
private JsonSchema(ValidationContext validationContext, String schemaPath, URI currentUri, JsonNode schemaNode,
JsonSchema parent, boolean suppressSubSchemaRetrieval) {
super(schemaPath, schemaNode, parent, null, suppressSubSchemaRetrieval,
- validationContext.getConfig() != null && validationContext.getConfig().isFailFast());
+ validationContext.getConfig() != null && validationContext.getConfig().isFailFast(),
+ validationContext.getConfig() != null ? validationContext.getConfig().getApplyDefaultsStrategy() : null);
this.validationContext = validationContext;
this.idKeyword = validationContext.getMetaSchema().getIdKeyword();
this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
@@ -208,7 +208,7 @@ private boolean nodeContainsRef(String ref, JsonNode node) {
* used in {@link com.networknt.schema.walk.DefaultKeywordWalkListenerRunner} to derive the keyword.
*/
private Map read(JsonNode schemaNode) {
- Map validators = new HashMap();
+ Map validators = new TreeMap<>(VALIDATOR_SORT);
if (schemaNode.isBoolean()) {
if (schemaNode.booleanValue()) {
final String customMessage = getCustomMessage(schemaNode, "true");
@@ -239,6 +239,20 @@ private Map read(JsonNode schemaNode) {
return validators;
}
+ /**
+ * A comparator that sorts validators, such such that 'properties' comes before 'required',
+ * so that we can apply default values before validating required.
+ */
+ private static Comparator VALIDATOR_SORT = (lhs, rhs) -> {
+ if (lhs.endsWith("/properties")) {
+ return -1;
+ }
+ if (rhs.endsWith("/properties")) {
+ return 1;
+ }
+ return lhs.compareTo(rhs);
+ };
+
private String getCustomMessage(JsonNode schemaNode, String pname) {
final JsonSchema parentSchema = getParentSchema();
final JsonNode message = getMessageNode(schemaNode, parentSchema);
@@ -320,7 +334,7 @@ protected ValidationResult validateAndCollect(JsonNode jsonNode, JsonNode rootNo
SchemaValidatorsConfig config = validationContext.getConfig();
// Get the collector context from the thread local.
CollectorContext collectorContext = getCollectorContext();
- // Valdiate.
+ // Validate.
Set errors = validate(jsonNode, rootNode, at);
// When walk is called in series of nested call we don't want to load the collectors every time. Leave to the API to decide when to call collectors.
if (config.doLoadCollectors()) {
@@ -374,7 +388,7 @@ public Set walk(JsonNode node, JsonNode rootNode, String at,
JsonSchemaWalker jsonWalker = entry.getValue();
String schemaPathWithKeyword = entry.getKey();
try {
- // Call all the pre-walk listeners. If atleast one of the pre walk listeners
+ // Call all the pre-walk listeners. If at least one of the pre walk listeners
// returns SKIP, then skip the walk.
if (keywordWalkListenerRunner.runPreWalkListeners(schemaPathWithKeyword,
node,
diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java
index b0e1154a0..69138a719 100644
--- a/src/main/java/com/networknt/schema/PropertiesValidator.java
+++ b/src/main/java/com/networknt/schema/PropertiesValidator.java
@@ -17,6 +17,7 @@
package com.networknt.schema;
import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.walk.DefaultPropertyWalkListenerRunner;
import com.networknt.schema.walk.WalkListenerRunner;
import org.slf4j.Logger;
@@ -52,7 +53,6 @@ public Set validate(JsonNode node, JsonNode rootNode, String
for (Map.Entry entry : schemas.entrySet()) {
JsonSchema propertySchema = entry.getValue();
JsonNode propertyNode = node.get(entry.getKey());
-
if (propertyNode != null) {
// check whether this is a complex validator. save the state
boolean isComplex = state.isComplexValidator();
@@ -102,6 +102,9 @@ public Set validate(JsonNode node, JsonNode rootNode, String
@Override
public Set walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
HashSet validationMessages = new LinkedHashSet();
+ if (applyDefaultsStrategy.shouldApplyPropertyDefaults()) {
+ applyPropertyDefaults((ObjectNode) node);
+ }
if (shouldValidateSchema) {
validationMessages.addAll(validate(node, rootNode, at));
} else {
@@ -113,6 +116,21 @@ public Set walk(JsonNode node, JsonNode rootNode, String at,
return validationMessages;
}
+ private void applyPropertyDefaults(ObjectNode node) {
+ for (Map.Entry entry : schemas.entrySet()) {
+ JsonNode propertyNode = node.get(entry.getKey());
+
+ if (propertyNode == null || (applyDefaultsStrategy.shouldApplyPropertyDefaultsIfNull() && propertyNode.isNull())) {
+ JsonSchema propertySchema = entry.getValue();
+ JsonNode defaultNode = propertySchema.getSchemaNode().get("default");
+ if (defaultNode != null && !defaultNode.isNull()) {
+ // mutate the input json
+ node.set(entry.getKey(), defaultNode);
+ }
+ }
+ }
+ }
+
private void walkSchema(Map.Entry entry, JsonNode node, JsonNode rootNode, String at,
boolean shouldValidateSchema, Set validationMessages, WalkListenerRunner propertyWalkListenerRunner) {
JsonSchema propertySchema = entry.getValue();
diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
index bb935ee35..b822e612f 100644
--- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
+++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java
@@ -37,6 +37,11 @@ public class SchemaValidatorsConfig {
*/
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;
+
/**
* When set to true, use ECMA-262 compatible validator
*/
@@ -112,6 +117,14 @@ public boolean isFailFast() {
return this.failFast;
}
+ public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) {
+ this.applyDefaultsStrategy = applyDefaultsStrategy;
+ }
+
+ public ApplyDefaultsStrategy getApplyDefaultsStrategy() {
+ return applyDefaultsStrategy;
+ }
+
public Map getUriMappings() {
// return a copy of the mappings
return new HashMap(uriMappings);
diff --git a/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java
new file mode 100644
index 000000000..4afe9a5c7
--- /dev/null
+++ b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java
@@ -0,0 +1,132 @@
+package com.networknt.schema;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+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;
+
+
+class JsonWalkApplyDefaultsTest {
+
+ @AfterEach
+ void cleanup() {
+ CollectorContext.getInstance().reset();
+ }
+
+ @ParameterizedTest
+ @ValueSource(booleans = { true, false})
+ void testApplyDefaults3(boolean shouldValidateSchema) throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data-default.json"));
+ JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, true, true));
+ ValidationResult result = jsonSchema.walk(inputNode, shouldValidateSchema);
+ if (shouldValidateSchema) {
+ assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
+ Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_missingButError: string found, integer expected",
+ "$.outer.badArray[1]: integer found, string expected"));
+ } else {
+ assertThat(result.getValidationMessages(), Matchers.empty());
+ }
+ // TODO: In Java 14 use text blocks
+ assertEquals(
+ objectMapper.readTree(
+ "{\"outer\":{\"mixedObject\":{\"intValue_present\":8,\"intValue_missing\":15,\"intValue_missing_notRequired\":25,\"intValue_null\":35,\"intValue_missingButError\":\"forty-five\"},\"goodArray\":[\"hello\",\"five\"],\"badArray\":[\"hello\",5],\"reference\":{\"stringValue_missing\":\"hello\"}}}"),
+ inputNode);
+ }
+
+ @Test
+ void testApplyDefaults2() throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data-default.json"));
+ JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, true, false));
+ ValidationResult result = jsonSchema.walk(inputNode, true);
+ assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
+ Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_missingButError: string found, integer expected",
+ "$.outer.goodArray[1]: null found, string expected",
+ "$.outer.badArray[1]: null found, string expected"));
+ assertEquals(
+ objectMapper.readTree(
+ "{\"outer\":{\"mixedObject\":{\"intValue_present\":8,\"intValue_missing\":15,\"intValue_missing_notRequired\":25,\"intValue_null\":35,\"intValue_missingButError\":\"forty-five\"},\"goodArray\":[\"hello\",null],\"badArray\":[\"hello\",null],\"reference\":{\"stringValue_missing\":\"hello\"}}}"),
+ inputNode);
+ }
+
+ @Test
+ void testApplyDefaults1() throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data-default.json"));
+ JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, false, false));
+ ValidationResult result = jsonSchema.walk(inputNode, true);
+ assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
+ Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_null: null found, integer expected",
+ "$.outer.mixedObject.intValue_missingButError: string found, integer expected",
+ "$.outer.goodArray[1]: null found, string expected",
+ "$.outer.badArray[1]: null found, string expected"));
+ assertEquals(
+ objectMapper.readTree(
+ "{\"outer\":{\"mixedObject\":{\"intValue_present\":8,\"intValue_missing\":15,\"intValue_missing_notRequired\":25,\"intValue_null\":null,\"intValue_missingButError\":\"forty-five\"},\"goodArray\":[\"hello\",null],\"badArray\":[\"hello\",null],\"reference\":{\"stringValue_missing\":\"hello\"}}}"),
+ inputNode);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = { "walkWithEmptyStrategy", "walkWithNoDefaults", "validateWithApplyAllDefaults"} )
+ void testApplyDefaults0(String method) throws IOException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data-default.json"));
+ JsonNode inputNodeOriginal = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data/walk-data-default.json"));
+ Set validationMessages;
+ switch (method) {
+ case "walkWithEmptyStrategy": {
+ JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(false, false, false));
+ validationMessages = jsonSchema.walk(inputNode, true).getValidationMessages();
+ break;
+ }
+ case "walkWithNoDefaults": {
+ // same empty strategy, but tests for NullPointerException
+ JsonSchema jsonSchema = createSchema(null);
+ validationMessages = jsonSchema.walk(inputNode, true).getValidationMessages();
+ break;
+ }
+ case "validateWithApplyAllDefaults": {
+ JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, true, true));
+ validationMessages = jsonSchema.validate(inputNode);
+ break;
+ }
+ default:
+ throw new UnsupportedOperationException();
+ }
+ assertThat(validationMessages.stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
+ Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_missing: is missing but it is required",
+ "$.outer.mixedObject.intValue_missingButError: is missing but it is required",
+ "$.outer.mixedObject.intValue_null: null found, integer expected",
+ "$.outer.goodArray[1]: null found, string expected",
+ "$.outer.badArray[1]: null found, string expected",
+ "$.outer.reference.stringValue_missing: is missing but it is required"));
+ assertEquals(inputNodeOriginal, inputNode);
+ }
+
+ @Test
+ void testIllegalArgumentException() {
+ try {
+ new ApplyDefaultsStrategy(false, true, false);
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException ignored) {
+ }
+ }
+
+ private JsonSchema createSchema(ApplyDefaultsStrategy applyDefaultsStrategy) {
+ JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
+ SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig();
+ schemaValidatorsConfig.setApplyDefaultsStrategy(applyDefaultsStrategy);
+ return schemaFactory.getSchema(getClass().getClassLoader().getResourceAsStream("schema/walk-schema-default.json"), schemaValidatorsConfig);
+ }
+}
diff --git a/src/test/resources/data/walk-data-default.json b/src/test/resources/data/walk-data-default.json
new file mode 100644
index 000000000..8a5245726
--- /dev/null
+++ b/src/test/resources/data/walk-data-default.json
@@ -0,0 +1,12 @@
+{
+ "outer": {
+ "mixedObject": {
+ "intValue_present": 8,
+ "intValue_null": null
+ },
+ "goodArray": ["hello", null],
+ "badArray": ["hello", null],
+ "reference": {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/test/resources/schema/walk-schema-default.json b/src/test/resources/schema/walk-schema-default.json
new file mode 100644
index 000000000..a3209136c
--- /dev/null
+++ b/src/test/resources/schema/walk-schema-default.json
@@ -0,0 +1,92 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Schema with default values ",
+
+ "definitions": {
+ "RandomObject": {
+ "type": "object",
+ "properties": {
+ "stringValue_missing": {
+ "description": "the test data does not have this attribute, so default should be applied if the ApplyDefaultsStrategy requires it",
+ "type": "string",
+ "default": "hello"
+ }
+ },
+ "required": [
+ "stringValue_missing"
+ ]
+ }
+ },
+
+ "type": "object",
+ "properties": {
+ "outer": {
+ "type": "object",
+ "properties": {
+ "mixedObject": {
+ "type": "object",
+ "properties": {
+ "intValue_present": {
+ "description": "the test data supplies a value for this attribute so the default is ignored",
+ "type": "integer",
+ "default": 5
+ },
+ "intValue_missing": {
+ "description": "the test data does not have this attribute, so default should be applied if the ApplyDefaultsStrategy requires it",
+ "type": "integer",
+ "default": 15
+ },
+ "intValue_missing_notRequired": {
+ "description": "the test data does not have this attribute, so default should be applied if the ApplyDefaultsStrategy requires it",
+ "type": "integer",
+ "default": 25
+ },
+ "intValue_null": {
+ "description": "the test data supplies the value null for this attribute so the default should be applied if the ApplyDefaultsStrategy requires it",
+ "type": "integer",
+ "default": 35
+ },
+ "intValue_missingButError": {
+ "description": "the test data does not have this attribute, so default should be applied if the ApplyDefaultsStrategy requires it, but the default is wrong so there should be an error",
+ "type": "integer",
+ "default": "forty-five"
+ }
+ },
+ "additionalProperties": false,
+ "required": [
+ "intValue_present",
+ "intValue_missing",
+ "intValue_null",
+ "intValue_missingButError"
+ ]
+ },
+
+ "goodArray": {
+ "type": "array",
+ "items": {
+ "description": "if an item in the array is null, then default value should be applied if the ApplyDefaultsStrategy requires it",
+ "type": "string",
+ "default": "five"
+ }
+ },
+
+ "badArray": {
+ "type": "array",
+ "items": {
+ "description": "if an item in the array is null, then default value should be applied if the ApplyDefaultsStrategy requires it, but the default is wrong so there should be an error",
+ "type": "string",
+ "default": 5
+ }
+ },
+
+ "reference": {
+ "$ref": "#/definitions/RandomObject"
+ }
+ },
+ "additionalProperties": false,
+ "required": ["mixedObject", "goodArray", "badArray", "reference"]
+ }
+ },
+ "additionalProperties": false,
+ "required": ["outer"]
+}