Skip to content

Commit f297b1f

Browse files
authored
Merge pull request #141 from jawaff/fix/ref-validator
Improved Ref Validator
2 parents 278533e + f87b314 commit f297b1f

21 files changed

+279
-106
lines changed

src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public AdditionalPropertiesValidator(String schemaPath, JsonNode schemaNode, Jso
4747
additionalPropertiesSchema = null;
4848
} else if (schemaNode.isObject()) {
4949
allowAdditionalProperties = true;
50-
additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema);
50+
additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
5151
} else {
5252
allowAdditionalProperties = false;
5353
additionalPropertiesSchema = null;

src/main/java/com/networknt/schema/AllOfValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public AllOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
3535
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ALL_OF, validationContext);
3636
int size = schemaNode.size();
3737
for (int i = 0; i < size; i++) {
38-
schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode.get(i), parentSchema));
38+
schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema));
3939
}
4040
}
4141

src/main/java/com/networknt/schema/AnyOfValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public AnyOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
3636
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ANY_OF, validationContext);
3737
int size = schemaNode.size();
3838
for (int i = 0; i < size; i++) {
39-
schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode.get(i), parentSchema));
39+
schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema));
4040
}
4141
}
4242

src/main/java/com/networknt/schema/DependenciesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public DependenciesValidator(String schemaPath, JsonNode schemaNode, JsonSchema
4444
depsProps.add(pvalue.get(i).asText());
4545
}
4646
} else if (pvalue.isObject()) {
47-
schemaDeps.put(pname, new JsonSchema(validationContext, pname, pvalue, parentSchema));
47+
schemaDeps.put(pname, new JsonSchema(validationContext, pname, parentSchema.getCurrentUrl(), pvalue, parentSchema));
4848
}
4949
}
5050

src/main/java/com/networknt/schema/ItemsValidator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,19 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator {
3838
public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3939
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ITEMS, validationContext);
4040
if (schemaNode.isObject()) {
41-
schema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema);
41+
schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
4242
} else {
4343
tupleSchema = new ArrayList<JsonSchema>();
4444
for (JsonNode s : schemaNode) {
45-
tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), s, parentSchema));
45+
tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), s, parentSchema));
4646
}
4747

4848
JsonNode addItemNode = getParentSchema().getSchemaNode().get(PROPERTY_ADDITIONAL_ITEMS);
4949
if (addItemNode != null) {
5050
if (addItemNode.isBoolean()) {
5151
additionalItems = addItemNode.asBoolean();
5252
} else if (addItemNode.isObject()) {
53-
additionalSchema = new JsonSchema(validationContext, addItemNode);
53+
additionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUrl(), addItemNode);
5454
}
5555
}
5656
}

src/main/java/com/networknt/schema/JsonSchema.java

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616

1717
package com.networknt.schema;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
2119
import java.io.UnsupportedEncodingException;
20+
import java.net.MalformedURLException;
21+
import java.net.URL;
2222
import java.net.URLDecoder;
23-
import java.util.*;
23+
import java.util.Collections;
24+
import java.util.HashMap;
25+
import java.util.Iterator;
26+
import java.util.LinkedHashSet;
27+
import java.util.Map;
28+
import java.util.Set;
2429
import java.util.regex.Matcher;
2530
import java.util.regex.Pattern;
2631

32+
import com.fasterxml.jackson.databind.JsonNode;
33+
import com.networknt.schema.url.URLFactory;
34+
2735
/**
2836
* This is the core of json constraint implementation. It parses json constraint
2937
* file and generates JsonValidators. The class is thread safe, once it is
@@ -34,27 +42,60 @@ public class JsonSchema extends BaseJsonValidator {
3442
protected final Map<String, JsonValidator> validators;
3543
private final ValidationContext validationContext;
3644

45+
/**
46+
* This is the current url of this schema. This url could refer to the url of this schema's file
47+
* or it could potentially be a url that has been altered by an id. An 'id' is able to completely overwrite
48+
* the current url or add onto it. This is necessary so that '$ref's are able to be relative to a
49+
* combination of the current schema file's url and 'id' urls visible to this schema.
50+
*
51+
* This can be null. If it is null, then the creation of relative urls will fail. However, an absolute
52+
* 'id' would still be able to specify an absolute url.
53+
*/
54+
private final URL currentUrl;
55+
3756
private JsonValidator requiredValidator = null;
3857

39-
public JsonSchema(ValidationContext validationContext, JsonNode schemaNode) {
40-
this(validationContext, "#", schemaNode, null);
58+
public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode) {
59+
this(validationContext, "#", baseUrl, schemaNode, null);
4160
}
4261

43-
public JsonSchema(ValidationContext validationContext, String schemaPath, JsonNode schemaNode,
62+
public JsonSchema(ValidationContext validationContext, String schemaPath, URL currentUrl, JsonNode schemaNode,
4463
JsonSchema parent) {
45-
this(validationContext, schemaPath, schemaNode, parent, false);
64+
this(validationContext, schemaPath, currentUrl, schemaNode, parent, false);
4665
}
4766

48-
public JsonSchema(ValidationContext validationContext, String schemaPath, JsonNode schemaNode,
67+
public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) {
68+
this(validationContext, "#", baseUrl, schemaNode, null, suppressSubSchemaRetrieval);
69+
}
70+
71+
private JsonSchema(ValidationContext validationContext, String schemaPath, URL currentUrl, JsonNode schemaNode,
4972
JsonSchema parent, boolean suppressSubSchemaRetrieval) {
5073
super(schemaPath, schemaNode, parent, null, suppressSubSchemaRetrieval);
5174
this.validationContext = validationContext;
5275
this.config = validationContext.getConfig();
76+
this.currentUrl = this.combineCurrentUrlWithIds(currentUrl, schemaNode);
5377
this.validators = Collections.unmodifiableMap(this.read(schemaNode));
5478
}
55-
56-
public JsonSchema(ValidationContext validationContext, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) {
57-
this(validationContext, "#", schemaNode, null, suppressSubSchemaRetrieval);
79+
80+
private URL combineCurrentUrlWithIds(URL currentUrl, JsonNode schemaNode) {
81+
final JsonNode idNode = schemaNode.get("id");
82+
if (idNode == null) {
83+
return currentUrl;
84+
} else {
85+
try
86+
{
87+
return URLFactory.toURL(currentUrl, idNode.asText());
88+
}
89+
catch (MalformedURLException e)
90+
{
91+
throw new IllegalArgumentException(String.format("Invalid 'id' in schema: %s", schemaNode), e);
92+
}
93+
}
94+
}
95+
96+
public URL getCurrentUrl()
97+
{
98+
return this.currentUrl;
5899
}
59100

60101
/**

src/main/java/com/networknt/schema/JsonSchemaFactory.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,12 @@ public JsonSchemaFactory build() {
9494
private final Map<String, JsonMetaSchema> jsonMetaSchemas;
9595
private final Map<String, String> urlMap;
9696

97-
private JsonSchemaFactory(ObjectMapper mapper, URLFetcher urlFetcher, String defaultMetaSchemaURI, Map<String, JsonMetaSchema> jsonMetaSchemas, Map<String, String> urlMap) {
97+
private JsonSchemaFactory(
98+
ObjectMapper mapper,
99+
URLFetcher urlFetcher,
100+
String defaultMetaSchemaURI,
101+
Map<String, JsonMetaSchema> jsonMetaSchemas,
102+
Map<String, String> urlMap) {
98103
if (mapper == null) {
99104
throw new IllegalArgumentException("ObjectMapper must not be null");
100105
}
@@ -152,10 +157,10 @@ public static Builder builder(JsonSchemaFactory blueprint) {
152157
.addUrlMappings(blueprint.urlMap);
153158
}
154159

155-
private JsonSchema newJsonSchema(JsonNode schemaNode, SchemaValidatorsConfig config) {
160+
private JsonSchema newJsonSchema(URL schemaUrl, JsonNode schemaNode, SchemaValidatorsConfig config) {
156161
final ValidationContext validationContext = createValidationContext(schemaNode);
157162
validationContext.setConfig(config);
158-
JsonSchema jsonSchema = new JsonSchema(validationContext, schemaNode);
163+
JsonSchema jsonSchema = new JsonSchema(validationContext, schemaUrl, schemaNode);
159164
return jsonSchema;
160165
}
161166

@@ -173,11 +178,11 @@ private JsonMetaSchema findMetaSchemaForSchema(JsonNode schemaNode) {
173178
}
174179
return jsonMetaSchema;
175180
}
176-
181+
177182
public JsonSchema getSchema(String schema, SchemaValidatorsConfig config) {
178183
try {
179184
final JsonNode schemaNode = mapper.readTree(schema);
180-
return newJsonSchema(schemaNode, config);
185+
return newJsonSchema(null, schemaNode, config);
181186
} catch (IOException ioe) {
182187
logger.error("Failed to load json schema!", ioe);
183188
throw new JsonSchemaException(ioe);
@@ -187,11 +192,11 @@ public JsonSchema getSchema(String schema, SchemaValidatorsConfig config) {
187192
public JsonSchema getSchema(String schema) {
188193
return getSchema(schema, null);
189194
}
190-
195+
191196
public JsonSchema getSchema(InputStream schemaStream, SchemaValidatorsConfig config) {
192197
try {
193198
final JsonNode schemaNode = mapper.readTree(schemaStream);
194-
return newJsonSchema(schemaNode, config);
199+
return newJsonSchema(null, schemaNode, config);
195200
} catch (IOException ioe) {
196201
logger.error("Failed to load json schema!", ioe);
197202
throw new JsonSchemaException(ioe);
@@ -201,7 +206,7 @@ public JsonSchema getSchema(InputStream schemaStream, SchemaValidatorsConfig con
201206
public JsonSchema getSchema(InputStream schemaStream) {
202207
return getSchema(schemaStream, null);
203208
}
204-
209+
205210
public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) {
206211
try {
207212
InputStream inputStream = null;
@@ -214,11 +219,10 @@ public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) {
214219
final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode);
215220

216221
if (idMatchesSourceUrl(jsonMetaSchema, schemaNode, schemaURL)) {
217-
218-
return new JsonSchema(new ValidationContext(jsonMetaSchema, this), schemaNode, true /*retrieved via id, resolving will not change anything*/);
222+
return new JsonSchema(new ValidationContext(jsonMetaSchema, this), mappedURL, schemaNode, true /*retrieved via id, resolving will not change anything*/);
219223
}
220224

221-
return newJsonSchema(schemaNode, config);
225+
return newJsonSchema(mappedURL, schemaNode, config);
222226
} finally {
223227
if (inputStream != null) {
224228
inputStream.close();
@@ -231,15 +235,24 @@ public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) {
231235
}
232236

233237
public JsonSchema getSchema(URL schemaURL) {
234-
return getSchema(schemaURL, null);
238+
return getSchema(schemaURL, new SchemaValidatorsConfig());
235239
}
236240

241+
public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode, SchemaValidatorsConfig config) {
242+
return newJsonSchema(schemaUrl, jsonNode, config);
243+
}
244+
245+
237246
public JsonSchema getSchema(JsonNode jsonNode, SchemaValidatorsConfig config) {
238-
return newJsonSchema(jsonNode, config);
247+
return newJsonSchema(null, jsonNode, config);
239248
}
240249

250+
public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode) {
251+
return newJsonSchema(schemaUrl, jsonNode, null);
252+
}
253+
241254
public JsonSchema getSchema(JsonNode jsonNode) {
242-
return newJsonSchema(jsonNode, null);
255+
return newJsonSchema(null, jsonNode, null);
243256
}
244257

245258
private boolean idMatchesSourceUrl(JsonMetaSchema metaSchema, JsonNode schema, URL schemaUrl) {

src/main/java/com/networknt/schema/NotValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class NotValidator extends BaseJsonValidator implements JsonValidator {
3030

3131
public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3232
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.NOT, validationContext);
33-
schema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema);
33+
schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema);
3434

3535
parseErrorCode(getValidatorType().getErrorCodeKey());
3636
}

src/main/java/com/networknt/schema/OneOfValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS
121121
int size = schemaNode.size();
122122
for (int i = 0; i < size; i++) {
123123
JsonNode childNode = schemaNode.get(i);
124-
JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), childNode, parentSchema);
124+
JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), childNode, parentSchema);
125125
schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema));
126126
}
127127

src/main/java/com/networknt/schema/PatternPropertiesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public PatternPropertiesValidator(String schemaPath, JsonNode schemaNode, JsonSc
3838
Iterator<String> names = schemaNode.fieldNames();
3939
while (names.hasNext()) {
4040
String name = names.next();
41-
schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, schemaNode.get(name), parentSchema));
41+
schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, parentSchema.getCurrentUrl(), schemaNode.get(name), parentSchema));
4242
}
4343
}
4444

src/main/java/com/networknt/schema/PropertiesValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public PropertiesValidator(String schemaPath, JsonNode schemaNode, JsonSchema pa
3232
schemas = new HashMap<String, JsonSchema>();
3333
for (Iterator<String> it = schemaNode.fieldNames(); it.hasNext(); ) {
3434
String pname = it.next();
35-
schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, schemaNode.get(pname), parentSchema));
35+
schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, parentSchema.getCurrentUrl(), schemaNode.get(pname), parentSchema));
3636
}
3737
}
3838

0 commit comments

Comments
 (0)