Skip to content

Commit

Permalink
Improve performance and fixes (#1086)
Browse files Browse the repository at this point in the history
* Improve performance

* Fix resolve
  • Loading branch information
justin-tay authored Jul 5, 2024
1 parent 60d034e commit c8bfc83
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 22 deletions.
7 changes: 2 additions & 5 deletions src/main/java/com/networknt/schema/JsonMetaSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -439,11 +439,8 @@ public String readDynamicAnchor(JsonNode schemaNode) {
}

private static String readText(JsonNode node, String field) {
JsonNode idNode = node.get(field);
if (idNode == null || !idNode.isTextual()) {
return null;
}
return idNode.textValue();
JsonNode fieldNode = node.get(field);
return fieldNode == null ? null : fieldNode.textValue();
}

public String getIri() {
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/com/networknt/schema/JsonNodePath.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class JsonNodePath implements Comparable<JsonNodePath> {
private final int pathSegmentIndex;

private volatile String value = null; // computed lazily
private int hash = 0; // computed lazily

public JsonNodePath(PathType type) {
this.type = type;
Expand Down Expand Up @@ -215,7 +216,12 @@ public String toString() {

@Override
public int hashCode() {
return Objects.hash(parent, pathSegment, pathSegmentIndex, type);
int h = hash;
if (h == 0) {
h = Objects.hash(parent, pathSegment, pathSegmentIndex, type);
hash = h;
}
return h;
}

@Override
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ public class JsonSchema extends BaseJsonValidator {
static JsonSchema from(ValidationContext validationContext, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) {
return new JsonSchema(validationContext, schemaLocation, evaluationPath, schemaNode, parent, suppressSubSchemaRetrieval);
}

private boolean hasNoFragment(SchemaLocation schemaLocation) {
return this.schemaLocation.getFragment() == null || this.schemaLocation.getFragment().getNameCount() == 0;
JsonNodePath fragment = this.schemaLocation.getFragment();
return fragment == null || (fragment.getParent() == null && fragment.getNameCount() == 0);
}

private static SchemaLocation resolve(SchemaLocation schemaLocation, JsonNode schemaNode, boolean rootSchema,
ValidationContext validationContext) {
String id = validationContext.resolveSchemaId(schemaNode);
Expand Down Expand Up @@ -112,7 +113,7 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc
if (id != null) {
// In earlier drafts $id may contain an anchor fragment see draft4/idRef.json
// Note that json pointer fragments in $id are not allowed
SchemaLocation result = id.contains("#") ? schemaLocation.resolve(id) : this.schemaLocation;
SchemaLocation result = id.indexOf('#') != -1 ? schemaLocation.resolve(id) : this.schemaLocation;
if (hasNoFragment(result)) {
this.id = id;
} else {
Expand Down
39 changes: 27 additions & 12 deletions src/main/java/com/networknt/schema/SchemaLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,24 @@ public SchemaLocation resolve(String absoluteIriReferenceOrFragment) {
return new SchemaLocation(this.getAbsoluteIri(), JSON_POINTER);
}
JsonNodePath fragment = JSON_POINTER;
String[] parts = absoluteIriReferenceOrFragment.split("#");
int index = absoluteIriReferenceOrFragment.indexOf('#');
AbsoluteIri absoluteIri = this.getAbsoluteIri();
String part0 = index == -1 ? absoluteIriReferenceOrFragment
: absoluteIriReferenceOrFragment.substring(0, index);
if (absoluteIri != null) {
if (!parts[0].isEmpty()) {
absoluteIri = absoluteIri.resolve(parts[0]);
if (!part0.isEmpty()) {
absoluteIri = absoluteIri.resolve(part0);
}
} else {
absoluteIri = AbsoluteIri.of(parts[0]);
absoluteIri = AbsoluteIri.of(part0);
}
if (parts.length > 1 && !parts[1].isEmpty()) {
fragment = Fragment.of(parts[1]);
if (index != -1) {
if (absoluteIriReferenceOrFragment.length() > index + 1) {
String part1 = absoluteIriReferenceOrFragment.substring(index + 1);
if (!part1.isEmpty()) {
fragment = Fragment.of(part1);
}
}
}
return new SchemaLocation(absoluteIri, fragment);
}
Expand All @@ -168,18 +175,26 @@ public static String resolve(SchemaLocation schemaLocation, String absoluteIriRe
if ("#".equals(absoluteIriReferenceOrFragment)) {
return schemaLocation.getAbsoluteIri().toString() + "#";
}
String[] parts = absoluteIriReferenceOrFragment.split("#");
int index = absoluteIriReferenceOrFragment.indexOf('#');
AbsoluteIri absoluteIri = schemaLocation.getAbsoluteIri();
String resolved = parts[0];
String part0 = index == -1 ? absoluteIriReferenceOrFragment
: absoluteIriReferenceOrFragment.substring(0, index);
String resolved = part0;
if (absoluteIri != null) {
if (!parts[0].isEmpty()) {
resolved = absoluteIri.resolve(parts[0]).toString();
if (!part0.isEmpty()) {
resolved = absoluteIri.resolve(part0).toString();
} else {
resolved = absoluteIri.toString();
}
}
if (parts.length > 1 && !parts[1].isEmpty()) {
resolved = resolved + "#" + parts[1];
String part1 = "";
if (index != -1) {
if (absoluteIriReferenceOrFragment.length() > index + 1) {
part1 = absoluteIriReferenceOrFragment.substring(index + 1);
}
}
if (!part1.isEmpty()) {
resolved = resolved + "#" + part1;
} else {
resolved = resolved + "#";
}
Expand Down
38 changes: 38 additions & 0 deletions src/test/java/com/networknt/schema/SchemaLocationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ void schemaLocationResolveDocumentPointer() {
SchemaLocation.resolve(schemaLocation, "#/allOf/12/properties"));
}

@Test
void schemaLocationResolveHashInFragment() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
assertEquals(
"https://example.com/schemas/address#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema",
SchemaLocation.resolve(schemaLocation,
"#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema"));
}

@Test
void schemaLocationResolvePathHashInFragment() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
assertEquals(
"https://example.com/schemas/hello#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema",
SchemaLocation.resolve(schemaLocation,
"hello#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema"));
}

@Test
void schemaLocationResolveEmptyString() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
Expand Down Expand Up @@ -105,6 +123,26 @@ void resolveDocumentPointer() {
schemaLocation.resolve("#/allOf/10/properties").toString());
}

@Test
void resolveHashInFragment() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
assertEquals(
"https://example.com/schemas/address#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema",
schemaLocation.resolve(
"#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema")
.toString());
}

@Test
void resolvePathHashInFragment() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
assertEquals(
"https://example.com/schemas/hello#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema",
schemaLocation.resolve(
"hello#/paths/~1subscribe/post/callbacks/myEvent/{request.body#~1callbackUrl}/post/requestBody/content/application~1json/schema")
.toString());
}

@Test
void resolveEmptyString() {
SchemaLocation schemaLocation = SchemaLocation.of("https://example.com/schemas/address#street_address");
Expand Down

0 comments on commit c8bfc83

Please sign in to comment.