Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignores siblings of $ref when dialect is Draft 4, 6 or 7 #809

Merged
merged 1 commit into from Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/main/java/com/networknt/schema/JsonSchema.java
Expand Up @@ -19,6 +19,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.CollectorContext.Scope;
import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.ValidationContext.DiscriminatorContext;
import com.networknt.schema.utils.StringUtils;
import com.networknt.schema.walk.DefaultKeywordWalkListenerRunner;
Expand All @@ -38,6 +39,8 @@
* constructed, it can be used to validate multiple json data concurrently.
*/
public class JsonSchema extends BaseJsonValidator {
private static final long V201909_VALUE = VersionFlag.V201909.getVersionFlagValue();

private Map<String, JsonValidator> validators;
private final JsonMetaSchema metaSchema;
private boolean validatorsLoaded = false;
Expand Down Expand Up @@ -252,6 +255,8 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
validators.put(getSchemaPath() + "/false", validator);
}
} else {
JsonValidator refValidator = null;

Iterator<String> pnames = schemaNode.fieldNames();
while (pnames.hasNext()) {
String pname = pnames.next();
Expand All @@ -262,6 +267,10 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
if (validator != null) {
validators.put(getSchemaPath() + "/" + pname, validator);

if ("$ref".equals(pname)) {
refValidator = validator;
}

if ("required".equals(pname)) {
this.requiredValidator = validator;
}
Expand All @@ -272,10 +281,24 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
}

}

// Ignore siblings for older drafts
if (null != refValidator && activeDialect() < V201909_VALUE) {
validators.clear();
validators.put(getSchemaPath() + "/$ref", refValidator);
}
}

return validators;
}

private long activeDialect() {
return this.validationContext
.activeDialect()
.map(VersionFlag::getVersionFlagValue)
.orElse(Long.MAX_VALUE);
}

/**
* A comparator that sorts validators, such that 'properties' comes before 'required',
* so that we can apply default values before validating required.
Expand Down
53 changes: 30 additions & 23 deletions src/main/java/com/networknt/schema/ValidationContext.java
Expand Up @@ -18,10 +18,12 @@

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Stack;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.SpecVersion.VersionFlag;
import com.networknt.schema.uri.URIFactory;
import com.networknt.schema.urn.URNFactory;

Expand All @@ -31,8 +33,8 @@ public class ValidationContext {
private final JsonMetaSchema metaSchema;
private final JsonSchemaFactory jsonSchemaFactory;
private SchemaValidatorsConfig config;
private final Map<String, JsonSchemaRef> refParsingInProgress = new HashMap<String, JsonSchemaRef>();
private final Stack<DiscriminatorContext> discriminatorContexts = new Stack<DiscriminatorContext>();
private final Map<String, JsonSchemaRef> refParsingInProgress = new HashMap<>();
private final Stack<DiscriminatorContext> discriminatorContexts = new Stack<>();

public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaSchema metaSchema,
JsonSchemaFactory jsonSchemaFactory, SchemaValidatorsConfig config) {
Expand All @@ -58,11 +60,11 @@ public JsonSchema newSchema(String schemaPath, JsonNode schemaNode, JsonSchema p

public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
JsonSchema parentSchema, String customMessage) {
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema, customMessage);
return this.metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema, customMessage);
}

public String resolveSchemaId(JsonNode schemaNode) {
return metaSchema.readId(schemaNode);
return this.metaSchema.readId(schemaNode);
}

public URIFactory getURIFactory() {
Expand All @@ -74,66 +76,71 @@ public URNFactory getURNFactory() {
}

public JsonSchemaFactory getJsonSchemaFactory() {
return jsonSchemaFactory;
return this.jsonSchemaFactory;
}

public SchemaValidatorsConfig getConfig() {
if (config == null) {
config = new SchemaValidatorsConfig();
if (this.config == null) {
this.config = new SchemaValidatorsConfig();
}
return config;
return this.config;
}

public void setConfig(SchemaValidatorsConfig config) {
this.config = config;
}

public void setReferenceParsingInProgress(String refValue, JsonSchemaRef ref) {
refParsingInProgress.put(refValue, ref);
this.refParsingInProgress.put(refValue, ref);
}

public JsonSchemaRef getReferenceParsingInProgress(String refValue) {
return refParsingInProgress.get(refValue);
return this.refParsingInProgress.get(refValue);
}

public DiscriminatorContext getCurrentDiscriminatorContext() {
if (!discriminatorContexts.empty()) {
return discriminatorContexts.peek();
if (!this.discriminatorContexts.empty()) {
return this.discriminatorContexts.peek();
}
return null; // this is the case when we get on a schema that has a discriminator, but it's not used in anyOf
}

public void enterDiscriminatorContext(final DiscriminatorContext ctx, String at) {
discriminatorContexts.push(ctx);
public void enterDiscriminatorContext(final DiscriminatorContext ctx, @SuppressWarnings("unused") String at) {
this.discriminatorContexts.push(ctx);
}

public void leaveDiscriminatorContextImmediately(String at) {
discriminatorContexts.pop();
public void leaveDiscriminatorContextImmediately(@SuppressWarnings("unused") String at) {
this.discriminatorContexts.pop();
}

public JsonMetaSchema getMetaSchema() {
return metaSchema;
return this.metaSchema;
}

public Optional<VersionFlag> activeDialect() {
String metaSchema = getMetaSchema().getUri();
return SpecVersionDetector.detectOptionalVersion(metaSchema);
}

public static class DiscriminatorContext {
private final Map<String, ObjectNode> discriminators = new HashMap<String, ObjectNode>();
private final Map<String, ObjectNode> discriminators = new HashMap<>();

private boolean discriminatorMatchFound = false;

public void registerDiscriminator(final String schemaPath, final ObjectNode discriminator) {
discriminators.put(schemaPath, discriminator);
this.discriminators.put(schemaPath, discriminator);
}

public ObjectNode getDiscriminatorForPath(final String schemaPath) {
return discriminators.get(schemaPath);
return this.discriminators.get(schemaPath);
}

public void markMatch() {
discriminatorMatchFound = true;
this.discriminatorMatchFound = true;
}

public boolean isDiscriminatorMatchFound() {
return discriminatorMatchFound;
return this.discriminatorMatchFound;
}

/**
Expand All @@ -142,7 +149,7 @@ public boolean isDiscriminatorMatchFound() {
* @return true in case there are discriminator candidates
*/
public boolean isActive() {
return !discriminators.isEmpty();
return !this.discriminators.isEmpty();
}
}
}
Expand Up @@ -74,7 +74,6 @@ private void disableV202012Tests() {
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/ref.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"), "Unsupported behavior");
}

Expand All @@ -83,23 +82,21 @@ private void disableV201909Tests() {
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/defs.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/recursiveRef.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/ref.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft2019-09/vocabulary.json"), "Unsupported behavior");
}

private void disableV7Tests() {
this.disabled.put(Paths.get("src/test/suite/tests/draft7/anchor.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft7/defs.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft7/optional/content.json"), "Unsupported behavior");
this.disabled.put(Paths.get("src/test/suite/tests/draft7/ref.json"), "Unsupported behavior");
}

private void disableV6Tests() {
this.disabled.put(Paths.get("src/test/suite/tests/draft6/ref.json"), "Unsupported behavior");
}
// nothing here
}

private void disableV4Tests() {
this.disabled.put(Paths.get("src/test/suite/tests/draft4/ref.json"), "Unsupported behavior");
// nothing here
}

}