Skip to content

Commit b138dc6

Browse files
665: Can't load JSON schemas with URN value in id field (#906)
* 665: Can't load JSON schemas with URN value in id field * Better handling of URNs
1 parent 97b4cba commit b138dc6

File tree

11 files changed

+153
-4
lines changed

11 files changed

+153
-4
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
<groupId>com.networknt</groupId>
2525
<artifactId>json-schema-validator</artifactId>
26-
<version>1.0.87</version>
26+
<version>1.0.88</version>
2727
<packaging>bundle</packaging>
2828
<name>JsonSchemaValidator</name>
2929
<description>A json schema validator that supports draft v4, v6, v7, v2019-09 and v2020-12</description>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ public JsonNode getRefSchemaNode(String ref) {
214214
if (node.isMissingNode()) {
215215
node = handleNullNode(ref, schema);
216216
}
217-
} else if (ref.startsWith("#") && ref.length() > 1) {
217+
} else if ((ref.startsWith("#") && ref.length() > 1) || (ref.startsWith("urn:") && ref.length() > 4)) {
218218
node = this.metaSchema.getNodeByFragmentRef(ref, node);
219219
if (node == null) {
220220
node = handleNullNode(ref, schema);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public Builder() {
5959
for (final String scheme : URLFactory.SUPPORTED_SCHEMES) {
6060
this.uriFactoryMap.put(scheme, urlFactory);
6161
}
62+
// Adds support for creating URNs.
63+
this.uriFactoryMap.put(URNURIFactory.SCHEME, new URNURIFactory());
6264

6365
// Adds support for fetching with {@link URL}s.
6466
final URIFetcher urlFetcher = new URLFetcher();

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.JsonNode;
2020
import com.networknt.schema.CollectorContext.Scope;
2121
import com.networknt.schema.uri.URIFactory;
22+
import com.networknt.schema.uri.URNURIFactory;
2223
import com.networknt.schema.urn.URNFactory;
2324
import org.slf4j.Logger;
2425
import org.slf4j.LoggerFactory;
@@ -35,6 +36,7 @@ public class RefValidator extends BaseJsonValidator {
3536
private JsonSchema parentSchema;
3637

3738
private static final String REF_CURRENT = "#";
39+
private static final String URN_SCHEME = URNURIFactory.SCHEME;
3840

3941
public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
4042
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.REF, validationContext);
@@ -78,6 +80,12 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
7880
if (schemaUri == null) {
7981
return null;
8082
}
83+
} else if (URN_SCHEME.equals(schemaUri.getScheme())) {
84+
// Try to resolve URN schema as a JsonSchemaRef to some sub-schema of the parent
85+
JsonSchemaRef ref = getJsonSchemaRef(parent, validationContext, schemaUri.toString(), refValueOriginal);
86+
if (ref != null) {
87+
return ref;
88+
}
8189
}
8290

8391
// This should retrieve schemas regardless of the protocol that is in the uri.
@@ -91,6 +99,13 @@ static JsonSchemaRef getRefSchema(JsonSchema parentSchema, ValidationContext val
9199
if (refValue.equals(REF_CURRENT)) {
92100
return new JsonSchemaRef(parent.findAncestor());
93101
}
102+
return getJsonSchemaRef(parent, validationContext, refValue, refValueOriginal);
103+
}
104+
105+
private static JsonSchemaRef getJsonSchemaRef(JsonSchema parent,
106+
ValidationContext validationContext,
107+
String refValue,
108+
String refValueOriginal) {
94109
JsonNode node = parent.getRefSchemaNode(refValue);
95110
if (node != null) {
96111
JsonSchemaRef ref = validationContext.getReferenceParsingInProgress(refValueOriginal);

src/main/java/com/networknt/schema/uri/URLFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
*/
3131
public final class URLFactory implements URIFactory {
3232
// These supported schemes are defined in {@link #URL(String, String, int, String)}.
33-
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<String>(
33+
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(new HashSet<>(
3434
Arrays.asList("http", "https", "ftp", "file", "jar")));
3535

3636
/**

src/main/java/com/networknt/schema/uri/URLFetcher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.net.URI;
2323
import java.net.URL;
2424
import java.net.URLConnection;
25+
import java.util.Arrays;
2526
import java.util.Collections;
27+
import java.util.HashSet;
2628
import java.util.Set;
2729

2830
/**
@@ -32,7 +34,7 @@ public final class URLFetcher implements URIFetcher {
3234

3335
// These supported schemes are defined in {@link #URL(String, String, int, String)}.
3436
// This fetcher also supports the {@link URL}s created with the {@link ClasspathURIFactory}.
35-
public static final Set<String> SUPPORTED_SCHEMES = Collections.unmodifiableSet(URLFactory.SUPPORTED_SCHEMES);
37+
public static final Set<String> SUPPORTED_SCHEMES = URLFactory.SUPPORTED_SCHEMES;
3638

3739
/**
3840
* {@inheritDoc}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.networknt.schema.uri;
2+
3+
import java.net.URI;
4+
import java.util.Collections;
5+
import java.util.Set;
6+
7+
/**
8+
* A URIFactory that handles "urn" scheme of {@link URI}s.
9+
*/
10+
public final class URNURIFactory implements URIFactory {
11+
12+
public static final String SCHEME = "urn";
13+
14+
@Override
15+
public URI create(final String uri) {
16+
try {
17+
return URI.create(uri);
18+
} catch (IllegalArgumentException e) {
19+
throw new IllegalArgumentException("Unable to create URI.", e);
20+
}
21+
}
22+
23+
@Override
24+
public URI create(final URI baseURI, final String segment) {
25+
String urnPart = baseURI.getRawSchemeSpecificPart();
26+
int pos = urnPart.indexOf(':');
27+
String namespace = pos < 0 ? urnPart : urnPart.substring(0, pos);
28+
return URI.create(SCHEME + ":" + namespace + ":" + segment);
29+
}
30+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.networknt.schema;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.io.UncheckedIOException;
9+
import java.net.URI;
10+
import java.util.Collections;
11+
import java.util.Set;
12+
13+
public class Issue665Test extends BaseJsonSchemaValidatorTest {
14+
15+
@Test
16+
void testUrnUriAsLocalRef() throws IOException {
17+
JsonSchema schema = getJsonSchemaFromClasspath("draft7/urn/issue665.json", SpecVersion.VersionFlag.V7);
18+
Assertions.assertNotNull(schema);
19+
Assertions.assertDoesNotThrow(schema::initializeValidators);
20+
Set<ValidationMessage> messages = schema.validate(getJsonNodeFromStringContent(
21+
"{\"myData\": {\"value\": \"hello\"}}"));
22+
Assertions.assertEquals(messages, Collections.emptySet());
23+
}
24+
25+
@Test
26+
void testUrnUriAsLocalRef_ExternalURN() {
27+
JsonSchemaFactory factory = JsonSchemaFactory
28+
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
29+
.uriFetcher(uri -> uri.equals(URI.create("urn:data"))
30+
? Thread.currentThread().getContextClassLoader()
31+
.getResourceAsStream("draft7/urn/issue665_external_urn_subschema.json")
32+
: null,
33+
"urn")
34+
.build();
35+
36+
try (InputStream is = Thread.currentThread().getContextClassLoader()
37+
.getResourceAsStream("draft7/urn/issue665_external_urn_ref.json")) {
38+
JsonSchema schema = factory.getSchema(is);
39+
Assertions.assertNotNull(schema);
40+
Assertions.assertDoesNotThrow(schema::initializeValidators);
41+
Set<ValidationMessage> messages = schema.validate(getJsonNodeFromStringContent(
42+
"{\"myData\": {\"value\": \"hello\"}}"));
43+
Assertions.assertEquals(messages, Collections.emptySet());
44+
} catch (IOException e) {
45+
throw new UncheckedIOException(e);
46+
}
47+
}
48+
49+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"required": [
5+
"myData"
6+
],
7+
"properties": {
8+
"myData": {
9+
"$ref": "urn:data"
10+
}
11+
},
12+
"definitions": {
13+
"data": {
14+
"$id": "urn:data",
15+
"type": "object",
16+
"required": [
17+
"value"
18+
],
19+
"properties": {
20+
"value": {
21+
"type": "string"
22+
}
23+
}
24+
}
25+
}
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"type": "object",
4+
"required": [
5+
"myData"
6+
],
7+
"properties": {
8+
"myData": {
9+
"$ref": "urn:data"
10+
}
11+
}
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "urn:data",
4+
"type": "object",
5+
"required": [
6+
"value"
7+
],
8+
"properties": {
9+
"value": {
10+
"type": "string"
11+
}
12+
}
13+
}

0 commit comments

Comments
 (0)