Skip to content

Commit 343fde2

Browse files
committed
improved json pointer resolution
1 parent b22feae commit 343fde2

File tree

3 files changed

+79
-37
lines changed

3 files changed

+79
-37
lines changed

core/src/main/java/org/everit/jsonvalidator/loader/SchemaLoader.java

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,30 @@ public class SchemaLoader {
8282
COMBINED_SUBSCHEMA_PROVIDERS.put("oneOf", CombinedSchema::oneOf);
8383
}
8484

85+
/**
86+
* Loads a JSON schema to a schema validator using a {@link HttpClients#createDefault() default
87+
* HTTP client}.
88+
*
89+
* @param schemaJson
90+
* the JSON representation of the schema.
91+
* @return the schema validator object
92+
*/
8593
public static Schema load(final JSONObject schemaJson) {
8694
return load(schemaJson, HttpClients.createDefault());
8795
}
8896

8997
/**
9098
* Creates Schema instance from its JSON representation.
99+
*
100+
* @param schemaJson
101+
* the JSON representation of the schema.
102+
* @param httpClient
103+
* the HTTP client to be used for resolving remote JSON references.
91104
*/
92105
public static Schema load(final JSONObject schemaJson, final HttpClient httpClient) {
93106
String schemaId = schemaJson.optString("id");
94-
SchemaLoader loader = new SchemaLoader(schemaId, schemaJson, schemaJson, null, httpClient);
95-
Schema.Builder<?> schemaBuilder = loader.load();
96-
Schema schema = schemaBuilder.build();
97-
loader.rootReferences.forEach(ref -> ref.build().setReferredSchema(schema));
98-
return schema;
107+
return new SchemaLoader(schemaId, schemaJson, schemaJson, new HashMap<>(), httpClient)
108+
.load().build();
99109
}
100110

101111
/**
@@ -273,8 +283,6 @@ TypeBasedMultiplexer typeMultiplexer(final String keyOfObj, final Object obj) {
273283
return new TypeBasedMultiplexer(keyOfObj, obj);
274284
}
275285

276-
private final Collection<ReferenceSchema.Builder> rootReferences;
277-
278286
private String id = null;
279287

280288
private final JSONObject schemaJson;
@@ -283,17 +291,19 @@ TypeBasedMultiplexer typeMultiplexer(final String keyOfObj, final Object obj) {
283291

284292
private final HttpClient httpClient;
285293

294+
private final Map<String, ReferenceSchema.Builder> pointerSchemas;
295+
286296
/**
287297
* Constructor.
288298
*/
289299
public SchemaLoader(final String id, final JSONObject schemaJson,
290-
final JSONObject rootSchemaJson, final Collection<ReferenceSchema.Builder> rootReferences,
300+
final JSONObject rootSchemaJson, final Map<String, ReferenceSchema.Builder> pointerSchemas,
291301
final HttpClient httpClient) {
292302
this.schemaJson = Objects.requireNonNull(schemaJson, "schemaJson cannot be null");
293303
this.rootSchemaJson = Objects.requireNonNull(rootSchemaJson, "rootSchemaJson cannot be null");
294304
this.id = id;
295-
this.rootReferences = rootReferences == null ? new ArrayList<>() : rootReferences;
296305
this.httpClient = Objects.requireNonNull(httpClient, "httpClient cannot be null");
306+
this.pointerSchemas = pointerSchemas;
297307
}
298308

299309
private void addDependencies(final Builder builder, final JSONObject deps) {
@@ -486,7 +496,8 @@ private EnumSchema.Builder buildEnumSchema() {
486496
}
487497

488498
private Schema.Builder<?> loadChild(final JSONObject childJson) {
489-
return new SchemaLoader(id, childJson, rootSchemaJson, rootReferences, httpClient).load();
499+
return new SchemaLoader(id, childJson, rootSchemaJson, pointerSchemas,
500+
httpClient).load();
490501
}
491502

492503
private Schema.Builder<?> loadForExplicitType(final String typeString) {
@@ -514,21 +525,28 @@ private Schema.Builder<?> loadForExplicitType(final String typeString) {
514525
* Returns a schema builder instance after looking up the JSON pointer.
515526
*/
516527
private Schema.Builder<?> lookupReference(final String relPointerString) {
517-
if ("#".equals(relPointerString)) {
518-
ReferenceSchema.Builder rval = new ReferenceSchema.Builder();
519-
rootReferences.add(rval);
520-
return rval;
521-
}
522528
JSONPointer pointer;
523529
String absPointerString = id + relPointerString;
524530
if (absPointerString.startsWith("#")) {
525-
pointer = JSONPointer.forDocument(rootSchemaJson, absPointerString);
531+
if (pointerSchemas.containsKey(absPointerString)) {
532+
return pointerSchemas.get(absPointerString);
533+
} else {
534+
ReferenceSchema.Builder refBuilder = ReferenceSchema.builder();
535+
pointerSchemas.put(absPointerString, refBuilder);
536+
pointer = JSONPointer.forDocument(rootSchemaJson, absPointerString);
537+
QueryResult result = pointer.query();
538+
SchemaLoader childLoader = new SchemaLoader(id, result.getQueryResult(),
539+
result.getContainingDocument(), pointerSchemas, httpClient);
540+
Schema referredSchema = childLoader.load().build();
541+
refBuilder.build().setReferredSchema(referredSchema);
542+
return refBuilder;
543+
}
526544
} else {
527545
pointer = JSONPointer.forURL(httpClient, absPointerString);
528546
}
529547
QueryResult result = pointer.query();
530548
return new SchemaLoader(id, result.getQueryResult(), result.getContainingDocument(),
531-
rootReferences, httpClient).load();
549+
pointerSchemas, httpClient).load();
532550
}
533551

534552
private boolean schemaHasAnyOf(final Collection<String> propNames) {

core/src/test/java/org/everit/jsonvalidator/loader/SchemaLoaderTest.java

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.everit.jsonvalidator.loader;
1717

1818
import java.io.InputStream;
19+
import java.util.HashMap;
1920
import java.util.Map;
2021

2122
import org.apache.http.client.HttpClient;
@@ -29,6 +30,7 @@
2930
import org.everit.jsonvalidator.NullSchema;
3031
import org.everit.jsonvalidator.NumberSchema;
3132
import org.everit.jsonvalidator.ObjectSchema;
33+
import org.everit.jsonvalidator.ReferenceSchema;
3234
import org.everit.jsonvalidator.Schema;
3335
import org.everit.jsonvalidator.SchemaException;
3436
import org.everit.jsonvalidator.StringSchema;
@@ -47,34 +49,36 @@ public class SchemaLoaderTest {
4749

4850
@Test
4951
public void typeBasedMultiplexerTest() {
50-
SchemaLoader loader = new SchemaLoader(null, new JSONObject(), new JSONObject(), null,
52+
SchemaLoader loader = new SchemaLoader(null, new JSONObject(), new JSONObject(),
53+
new HashMap<>(),
5154
httpClient);
5255
loader.typeMultiplexer(new JSONObject())
53-
.ifObject().then(jsonObj -> {
54-
})
55-
.ifIs(JSONArray.class).then(jsonArr -> {
56-
})
57-
.orElse(obj -> {
58-
});
56+
.ifObject().then(jsonObj -> {
57+
})
58+
.ifIs(JSONArray.class).then(jsonArr -> {
59+
})
60+
.orElse(obj -> {
61+
});
5962

6063
loader.typeMultiplexer(new JSONObject())
61-
.ifObject().then(jsonObj -> {
62-
})
63-
.ifIs(JSONArray.class).then(jsonArr -> {
64-
})
65-
.requireAny();
64+
.ifObject().then(jsonObj -> {
65+
})
66+
.ifIs(JSONArray.class).then(jsonArr -> {
67+
})
68+
.requireAny();
6669
}
6770

6871
@Test(expected = SchemaException.class)
6972
public void typeBasedMultiplexerFailure() {
70-
SchemaLoader loader = new SchemaLoader(null, new JSONObject(), new JSONObject(), null,
73+
SchemaLoader loader = new SchemaLoader(null, new JSONObject(), new JSONObject(),
74+
new HashMap<>(),
7175
httpClient);
7276
loader.typeMultiplexer("foo")
73-
.ifObject().then(o -> {
74-
})
75-
.ifIs(JSONArray.class).then(o -> {
76-
})
77-
.requireAny();
77+
.ifObject().then(o -> {
78+
})
79+
.ifIs(JSONArray.class).then(o -> {
80+
})
81+
.requireAny();
7882
}
7983

8084
@BeforeClass
@@ -251,9 +255,12 @@ public void objectWithSchemaDep() {
251255
@Test
252256
public void pointerResolution() {
253257
ObjectSchema actual = (ObjectSchema) SchemaLoader.load(get("pointerResolution"));
254-
ObjectSchema rectangleSchema = (ObjectSchema) actual.getPropertySchemas().get("rectangle");
258+
ObjectSchema rectangleSchema = (ObjectSchema) ((ReferenceSchema) actual.getPropertySchemas()
259+
.get("rectangle"))
260+
.getReferredSchema();
255261
Assert.assertNotNull(rectangleSchema);
256-
Assert.assertTrue(rectangleSchema.getPropertySchemas().get("a") instanceof NumberSchema);
262+
ReferenceSchema aRef = (ReferenceSchema) rectangleSchema.getPropertySchemas().get("a");
263+
Assert.assertTrue(aRef.getReferredSchema() instanceof NumberSchema);
257264
}
258265

259266
@Test(expected = SchemaException.class)
@@ -353,4 +360,9 @@ public void genericProperties() {
353360
Assert.assertEquals("my description", actual.getDescription());
354361
}
355362

363+
@Test
364+
public void recursiveSchema() {
365+
SchemaLoader.load(get("recursiveSchema"));
366+
}
367+
356368
}

core/src/test/resources/org/everit/jsonvalidator/testschemas.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,5 +233,17 @@
233233
"id" : "myId",
234234
"title" : "my title",
235235
"description" : "my description"
236+
},
237+
"recursiveSchema" : {
238+
"properties" : {
239+
"prop" : {"$ref" : "#/definitions/Prop"}
240+
},
241+
"definitions" : {
242+
"Prop" : {
243+
"properties" : {
244+
"subprop" : {"$ref" : "#/definitions/Prop"}
245+
}
246+
}
247+
}
236248
}
237249
}

0 commit comments

Comments
 (0)