Skip to content

Commit ee30c36

Browse files
authored
Merge pull request #263 from rzukowski/255_issue_id_ref
255 issue id ref
2 parents dcee0d7 + c65023b commit ee30c36

File tree

9 files changed

+225
-6
lines changed

9 files changed

+225
-6
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,11 @@ public String readId(JsonNode schemaNode) {
325325
public String getUri() {
326326
return uri;
327327
}
328-
328+
329+
public String getIdKeyword() {
330+
return idKeyword;
331+
}
332+
329333
public JsonValidator newValidator(ValidationContext validationContext, String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
330334
JsonSchema parentSchema) {
331335

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

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
public class JsonSchema extends BaseJsonValidator {
3939
private static final Pattern intPattern = Pattern.compile("^[0-9]+$");
4040
protected final Map<String, JsonValidator> validators;
41+
private final String idKeyword;
4142
private final ValidationContext validationContext;
4243

4344
/**
@@ -72,6 +73,7 @@ private JsonSchema(ValidationContext validationContext, String schemaPath, URI
7273
validationContext.getConfig() != null && validationContext.getConfig().isFailFast());
7374
this.validationContext = validationContext;
7475
this.config = validationContext.getConfig();
76+
this.idKeyword = validationContext.getMetaSchema().getIdKeyword();
7577
this.currentUri = this.combineCurrentUriWithIds(currentUri, schemaNode);
7678
this.validators = Collections.unmodifiableMap(this.read(schemaNode));
7779
}
@@ -119,16 +121,19 @@ public JsonNode getRefSchemaNode(String ref) {
119121
node = node.get(key);
120122
}
121123
if (node == null){
122-
JsonSchema subSchema = schema.fetchSubSchemaNode(validationContext);
123-
if (subSchema != null) {
124-
node = subSchema.getRefSchemaNode(ref);
125-
}
124+
node = handleNullNode(ref, schema);
126125
}
127126
if (node == null){
128127
break;
129128
}
130129
}
131130
}
131+
else if (ref.startsWith("#") && ref.length() > 1) {
132+
node = getNodeById(ref, node);
133+
if(node == null) {
134+
node = handleNullNode(ref, schema);
135+
}
136+
}
132137
return node;
133138
}
134139

@@ -140,6 +145,37 @@ public JsonSchema findAncestor() {
140145
return ancestor;
141146
}
142147

148+
private JsonNode handleNullNode(String ref, JsonSchema schema) {
149+
JsonSchema subSchema = schema.fetchSubSchemaNode(validationContext);
150+
if (subSchema != null) {
151+
return subSchema.getRefSchemaNode(ref);
152+
}
153+
return null;
154+
}
155+
156+
private JsonNode getNodeById(String ref, JsonNode node) {
157+
if (nodeContainsRef(ref, node)) {
158+
return node;
159+
} else {
160+
Iterator<JsonNode> children = node.elements();
161+
while (children.hasNext()) {
162+
JsonNode refNode = getNodeById(ref, children.next());
163+
if (refNode != null) {
164+
return refNode;
165+
}
166+
}
167+
}
168+
return null;
169+
}
170+
171+
private boolean nodeContainsRef(String ref, JsonNode node) {
172+
JsonNode id = node.get(idKeyword);
173+
if (id != null) {
174+
return ref.equals(id.asText());
175+
}
176+
return false;
177+
}
178+
143179
private Map<String, JsonValidator> read(JsonNode schemaNode) {
144180
Map<String, JsonValidator> validators = new HashMap<String, JsonValidator>();
145181
if(schemaNode.isBoolean()) {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,7 @@ public JsonSchemaRef getReferenceParsingInProgress(String refValue) {
7878
return refParsingInProgress.get(refValue);
7979
}
8080

81+
protected JsonMetaSchema getMetaSchema() {
82+
return metaSchema;
83+
}
8184
}

src/test/java/com/networknt/schema/V4JsonSchemaTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ public void testRefRemoteValidator() throws Exception {
279279
runTestFile("draft4/refRemote.json");
280280
}
281281

282+
@Test
283+
public void testRefIdReference() throws Exception {
284+
runTestFile("draft4/idRef.json");
285+
}
286+
282287
@Test
283288
public void testRelativeRefRemoteValidator() throws Exception {
284289
runTestFile("draft4/relativeRefRemote.json");
@@ -289,7 +294,6 @@ public void testRequiredValidator() throws Exception {
289294
runTestFile("draft4/required.json");
290295
}
291296

292-
293297
@Test
294298
public void testTypeValidator() throws Exception {
295299
runTestFile("draft4/type.json");

src/test/java/com/networknt/schema/V6JsonSchemaTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ public void testRefRemoteValidator() throws Exception {
291291
runTestFile("draft6/refRemote.json");
292292
}
293293

294+
@Test
295+
public void testRefIdReference() throws Exception {
296+
runTestFile("draft6/idRef.json");
297+
}
298+
294299
@Test
295300
@Ignore
296301
public void testRefRemoteValidator_Ignored() throws Exception {

src/test/java/com/networknt/schema/V7JsonSchemaTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,11 @@ public void testRefRemoteValidator() throws Exception {
392392
runTestFile("draft7/refRemote.json");
393393
}
394394

395+
@Test
396+
public void testRefIdReference() throws Exception {
397+
runTestFile("draft7/idRef.json");
398+
}
399+
395400
@Test
396401
@Ignore
397402
public void testRefRemoteValidator_Ignored() throws Exception {

src/test/resources/draft4/idRef.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[
2+
{
3+
"description": "refer to subschema by a unique name ($id)",
4+
"schema": {
5+
"type": "object",
6+
"properties": {
7+
"cars": {
8+
"type": "array",
9+
"items": {
10+
"$ref": "#car"
11+
}
12+
}
13+
},
14+
"definitions": {
15+
"car": {
16+
"id": "#car",
17+
"type": "object",
18+
"properties": {
19+
"model": {
20+
"type": "string"
21+
}
22+
},
23+
"required": [
24+
"model"
25+
]
26+
}
27+
}
28+
},
29+
"tests": [
30+
{
31+
"description": "invalid when element referenced by id is invalid",
32+
"data": {
33+
"cars": [
34+
{
35+
36+
}
37+
]
38+
},
39+
"valid": false
40+
},
41+
{
42+
"description": "valid when element referenced by id is valid",
43+
"data": {
44+
"cars": [
45+
{
46+
"model": "BMW"
47+
}
48+
]
49+
},
50+
"valid": true
51+
}
52+
]
53+
}
54+
]

src/test/resources/draft6/idRef.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[
2+
{
3+
"description": "refer to subschema by a unique name ($id)",
4+
"schema": {
5+
"type": "object",
6+
"properties": {
7+
"cars": {
8+
"type": "array",
9+
"items": {
10+
"$ref": "#car"
11+
}
12+
}
13+
},
14+
"definitions": {
15+
"car": {
16+
"$id": "#car",
17+
"type": "object",
18+
"properties": {
19+
"model": {
20+
"type": "string"
21+
}
22+
},
23+
"required": [
24+
"model"
25+
]
26+
}
27+
}
28+
},
29+
"tests": [
30+
{
31+
"description": "invalid when element referenced by id is invalid",
32+
"data": {
33+
"cars": [
34+
{
35+
36+
}
37+
]
38+
},
39+
"valid": false
40+
},
41+
{
42+
"description": "valid when element referenced by id is valid",
43+
"data": {
44+
"cars": [
45+
{
46+
"model": "BMW"
47+
}
48+
]
49+
},
50+
"valid": true
51+
}
52+
]
53+
}
54+
]

src/test/resources/draft7/idRef.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[
2+
{
3+
"description": "refer to subschema by a unique name ($id)",
4+
"schema": {
5+
"type": "object",
6+
"properties": {
7+
"cars": {
8+
"type": "array",
9+
"items": {
10+
"$ref": "#car"
11+
}
12+
}
13+
},
14+
"definitions": {
15+
"car": {
16+
"$id": "#car",
17+
"type": "object",
18+
"properties": {
19+
"model": {
20+
"type": "string"
21+
}
22+
},
23+
"required": [
24+
"model"
25+
]
26+
}
27+
}
28+
},
29+
"tests": [
30+
{
31+
"description": "invalid when element referenced by id is invalid",
32+
"data": {
33+
"cars": [
34+
{
35+
36+
}
37+
]
38+
},
39+
"valid": false
40+
},
41+
{
42+
"description": "valid when element referenced by id is valid",
43+
"data": {
44+
"cars": [
45+
{
46+
"model": "BMW"
47+
}
48+
]
49+
},
50+
"valid": true
51+
}
52+
]
53+
}
54+
]

0 commit comments

Comments
 (0)