Skip to content

Commit a56e330

Browse files
committed
fixing #321 (ref-by-$id not working in remote schema references)
* added integ test for reproducing the problem * test update: `JsonPointerEvaluatorTest#JsonPointerEvaluatorTest()` now expects the source URI to be set as location for remote fetched documents - updating `JsonPointerEvaluator#configureBasedOnState()` to pass the test * for reference schemas, using the location of the found object (instead of the resolved URI) as schema location * changing `JsonPointerEvaluator#query()` to first try to lookup the result schema by $id in the containing document, before trying to use it as a json pointer * adding some missing `final` modifiers to fields
1 parent 99ce8c3 commit a56e330

File tree

6 files changed

+77
-49
lines changed

6 files changed

+77
-49
lines changed

core/src/main/java/org/everit/json/schema/loader/JsonPointerEvaluator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.everit.json.schema.loader;
22

33
import static java.util.Arrays.asList;
4+
import static java.util.Collections.emptyList;
45
import static java.util.Objects.requireNonNull;
56
import static org.everit.json.schema.loader.OrgJsonUtil.toMap;
67

@@ -10,6 +11,7 @@
1011
import java.io.InputStreamReader;
1112
import java.io.UncheckedIOException;
1213
import java.io.UnsupportedEncodingException;
14+
import java.net.URI;
1315
import java.net.URISyntaxException;
1416
import java.net.URLDecoder;
1517
import java.nio.charset.Charset;
@@ -108,10 +110,10 @@ static final JsonPointerEvaluator forDocument(JsonObject document, String fragme
108110
}
109111

110112
private static JsonObject configureBasedOnState(JsonObject obj, LoadingState callingState, String id) {
113+
URI documentURI = validateURI(callingState, id).asJavaURI();
111114
obj.ls = new LoadingState(callingState.config,
112115
callingState.pointerSchemas, obj, obj,
113-
validateURI(callingState, id).asJavaURI(),
114-
SchemaLocation.empty());
116+
documentURI, new SchemaLocation(documentURI, emptyList()));
115117
return obj;
116118
}
117119

@@ -152,6 +154,10 @@ public QueryResult query() {
152154
if (fragment.isEmpty()) {
153155
return new QueryResult(document, document);
154156
}
157+
JsonObject foundById = ReferenceLookup.lookupObjById(document, fragment);
158+
if (foundById != null) {
159+
return new QueryResult(document, foundById);
160+
}
155161
String[] path = fragment.split("/");
156162
if ((path[0] == null) || !path[0].startsWith("#")) {
157163
throw new IllegalArgumentException("JSON pointers must start with a '#'");

core/src/main/java/org/everit/json/schema/loader/ReferenceLookup.java

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ReferenceKnot {
2020

2121
private Schema referredSchema;
2222

23-
private List<ReferenceSchema.Builder> refs = new ArrayList<>(1);
23+
private final List<ReferenceSchema.Builder> refs = new ArrayList<>(1);
2424

2525
ReferenceSchema.Builder initReference(String refValue) {
2626
ReferenceSchema.Builder builder = new ReferenceSchema.Builder().refValue(refValue);
@@ -67,21 +67,32 @@ static Map<String, Object> extend(Map<String, Object> additional, Map<String, Ob
6767
return rawObj;
6868
}
6969

70-
private LoadingState ls;
71-
72-
private SchemaClient schemaClient;
73-
74-
public ReferenceLookup(LoadingState ls) {
75-
this.ls = requireNonNull(ls, "ls cannot be null");
76-
this.schemaClient = ls.config.schemaClient;
77-
}
78-
79-
private Map<String, Object> doExtend(Map<String, Object> additional, Map<String, Object> original) {
80-
if (ls.specVersion() == SpecificationVersion.DRAFT_4) {
81-
return extend(additional, original);
82-
} else {
83-
return original;
70+
static JsonObject lookupObjById(JsonValue val, String idAttrVal) {
71+
String idKeyword = val.ls.specVersion().idKeyword();
72+
if (val instanceof JsonObject) {
73+
JsonObject obj = (JsonObject) val;
74+
if (obj.containsKey(idKeyword)
75+
&& obj.require(idKeyword).typeOfValue() == String.class
76+
&& obj.require(idKeyword).requireString().equals(idAttrVal)) {
77+
return obj;
78+
}
79+
for (String key : obj.keySet()) {
80+
JsonObject maybeFound = lookupObjById(obj.require(key), idAttrVal);
81+
if (maybeFound != null) {
82+
return maybeFound;
83+
}
84+
}
85+
} else if (val instanceof JsonArray) {
86+
JsonArray arr = (JsonArray) val;
87+
for (int i = 0; i < arr.length(); ++i) {
88+
JsonObject maybeFound = lookupObjById(arr.at(i), idAttrVal);
89+
if (maybeFound != null) {
90+
return maybeFound;
91+
}
92+
}
8493
}
94+
95+
return null;
8596
}
8697

8798
/**
@@ -106,6 +117,23 @@ static URI withoutFragment(final String fullUri) {
106117
}
107118
}
108119

120+
private final LoadingState ls;
121+
122+
private final SchemaClient schemaClient;
123+
124+
public ReferenceLookup(LoadingState ls) {
125+
this.ls = requireNonNull(ls, "ls cannot be null");
126+
this.schemaClient = ls.config.schemaClient;
127+
}
128+
129+
private Map<String, Object> doExtend(Map<String, Object> additional, Map<String, Object> original) {
130+
if (ls.specVersion() == SpecificationVersion.DRAFT_4) {
131+
return extend(additional, original);
132+
} else {
133+
return original;
134+
}
135+
}
136+
109137
Map<String, Object> withoutRef(JsonObject original) {
110138
Map<String, Object> rawObj = new HashMap<>();
111139
original.keySet().stream()
@@ -114,34 +142,6 @@ Map<String, Object> withoutRef(JsonObject original) {
114142
return rawObj;
115143
}
116144

117-
private JsonObject lookupObjById(JsonValue val, String idAttrVal) {
118-
String idKeyword = val.ls.specVersion().idKeyword();
119-
if (val instanceof JsonObject) {
120-
JsonObject obj = (JsonObject) val;
121-
if (obj.containsKey(idKeyword)
122-
&& obj.require(idKeyword).typeOfValue() == String.class
123-
&& obj.require(idKeyword).requireString().equals(idAttrVal)) {
124-
return obj;
125-
}
126-
for (String key : obj.keySet()) {
127-
JsonObject maybeFound = lookupObjById(obj.require(key), idAttrVal);
128-
if (maybeFound != null) {
129-
return maybeFound;
130-
}
131-
}
132-
} else if (val instanceof JsonArray) {
133-
JsonArray arr = (JsonArray) val;
134-
for (int i = 0; i < arr.length(); ++i) {
135-
JsonObject maybeFound = lookupObjById(arr.at(i), idAttrVal);
136-
if (maybeFound != null) {
137-
return maybeFound;
138-
}
139-
}
140-
}
141-
142-
return null;
143-
}
144-
145145
private Schema.Builder<?> performQueryEvaluation(String mapKey, JsonPointerEvaluator pointerEvaluator) {
146146
String absolutePointer = ReferenceResolver.resolve(ls.id, mapKey).toString();
147147
if (ls.pointerSchemas.containsKey(absolutePointer)) {
@@ -174,13 +174,14 @@ Schema.Builder<?> lookup(String relPointerString, JsonObject ctx) {
174174

175175
URI resolutionScope = !isSameDocumentRef(absPointerString) ? withoutFragment(absPointerString) : ls.id;
176176
JsonObject containingDocument = result.getContainingDocument();
177+
SchemaLocation resultLocation = result.getQueryResult().ls.pointerToCurrentObj;
177178
SchemaLoader childLoader = ls.initNewDocumentLoader()
178-
.pointerToCurrentObj(SchemaLocation.parseURI(absPointerString))
179+
.pointerToCurrentObj(resultLocation)
179180
.resolutionScope(resolutionScope)
180181
.schemaJson(result.getQueryResult())
181182
.rootSchemaJson(containingDocument).build();
182183
Schema referredSchema = childLoader.load().build();
183-
refBuilder.schemaLocation(SchemaLocation.parseURI(absPointerString));
184+
refBuilder.schemaLocation(resultLocation);
184185
knot.resolveWith(referredSchema);
185186
return refBuilder;
186187
}

core/src/test/java/org/everit/json/schema/loader/JsonPointerEvaluatorTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import java.io.InputStream;
1616
import java.net.URI;
17+
import java.net.URISyntaxException;
1718
import java.util.HashMap;
1819

1920
import org.everit.json.schema.ResourceLoader;
@@ -79,7 +80,7 @@ private LoadingState createLoadingState(SchemaClient schemaClient, String ref) {
7980
}
8081

8182
@Test
82-
public void remoteDocumentSuccess() {
83+
public void remoteDocumentSuccess() throws URISyntaxException {
8384
SchemaClient schemaClient = mock(SchemaClient.class);
8485
when(schemaClient.get("http://localhost:1234/hello")).thenReturn(rootSchemaJsonAsStream());
8586
JsonPointerEvaluator pointer = JsonPointerEvaluator
@@ -88,7 +89,8 @@ public void remoteDocumentSuccess() {
8889
JsonObject actual = pointer.query().getQueryResult().requireObject();
8990
assertEquals("dummy schema at #/definitions/Bar", actual.require("description").requireString());
9091
assertEquals("http://localhost:1234/folder/", actual.ls.id.toString());
91-
assertEquals(new SchemaLocation(asList("definitions", "Bar")), actual.ls.pointerToCurrentObj);
92+
assertEquals(new SchemaLocation(new URI("http://localhost:1234/hello"), asList("definitions", "Bar")),
93+
actual.ls.pointerToCurrentObj);
9294
}
9395

9496
@Test
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$defs": {
4+
"a": {
5+
"$id": "#a",
6+
"type": "integer"
7+
}
8+
}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"properties": {
3+
"a": {
4+
"$ref": "http://localhost:1234/schema.json#a"
5+
}
6+
}
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"a": 42
3+
}

0 commit comments

Comments
 (0)