Skip to content

Commit 9c95c06

Browse files
authored
Add annotation support refactor keywords to use annotations implement output formats (#942)
* Support annotations * Refactor recursive ref * Fix IllegalStateException on recursive call in computeIfAbsent * Refactor * Refactor results * Refactor * Remove scope * Refactor * Add can short circuit logic * Fix performance * Fix * Refactor * Enable disabled tests * Fix contains * Fix fast fail * Refactor * Update docs * Add schemaNode and instanceNode to validation message and configure serialization * Fail fast based on execution context config * Fix fail fast * Refactor annotation config. * Update doc * Fix 939 and add 940 test * Fix 936 * Fix 935 * Fix * Add convenience method for schema loader * Add convenience method for schema mappers * Support annotation collection for reporting * Support output formatting * Refactor * Refactor * Collect format annotations * Refactor * Refactor * Refactor * Refactor * Redesign and fix fail fast logic * Support hierarchical output * Update docs * Throw specific exceptions if ref cannot be resolved * Update docs * Add 857 test * Javadoc * Add 927 test * Move out non suite tests * Update to latest test suite * Fix os line ending difference * Fix os line ending difference * Fix javadoc * Fix id handling * Add tests for format output formatting * Add test for type union
1 parent e95642c commit 9c95c06

File tree

215 files changed

+7013
-2429
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

215 files changed

+7013
-2429
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4-
The format is based on [Keep a Changelog](http://keepachangelog.com/)
5-
and this project adheres to [Semantic Versioning](http://semver.org/).
4+
This format is based on [Keep a Changelog](http://keepachangelog.com/).
5+
6+
This project does not adhere to [Semantic Versioning](https://semver.org/) and minor version changes can have incompatible API changes. These incompatible API changes will largely affect those who have custom validator or walker implementations. Those who just use the library to validate using the standard JSON Schema Draft specifications may not need changes.
67

78
## [Unreleased]
89

README.md

Lines changed: 264 additions & 22 deletions
Large diffs are not rendered by default.

doc/compatibility.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
## Compatibility with JSON Schema versions
22

3-
This implementation does not currently generate annotations.
4-
53
The `pattern` validator by default uses the JDK regular expression implementation which is not ECMA-262 compliant and is thus not compliant with the JSON Schema specification. The library can however be configured to use a ECMA-262 compliant regular expression implementation.
64

5+
Annotation processing and reporting are implemented. Note that the collection of annotations will have an adverse performance impact.
6+
7+
This implements the Flag, List and Hierarchical output formats defined in the [Specification for Machine-Readable Output for JSON Schema Validation and Annotation](https://github.com/json-schema-org/json-schema-spec/blob/8270653a9f59fadd2df0d789f22d486254505bbe/jsonschema-validation-output-machines.md).
8+
79
### Known Issues
8-
* The `anyOf` applicator currently returns immediately on matching a schema. This results in the `unevaluatedItems` and `unevaluatedProperties` keywords potentially returning an incorrect result as the rest of the schemas in the `anyOf` aren't processed.
9-
* The `unevaluatedItems` keyword does not currently consider `contains`.
1010

11+
There are currently no known issues with the required functionality from the specification.
12+
13+
The following are the tests results after running the [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) as at 29 Jan 2024 using version 1.3.1. As the test suite is continously updated, this can result in changes in the results subsequently.
14+
15+
| Implementations | Overall | DRAFT_03 | DRAFT_04 | DRAFT_06 | DRAFT_07 | DRAFT_2019_09 | DRAFT_2020_12 |
16+
|-----------------|-------------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------|
17+
| NetworkNt | pass: r:4703 (100.0%) o:2369 (100.0%)<br>fail: r:0 (0.0%) o:1 (0.0%) | | pass: r:600 (100.0%) o:251 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:796 (100.0%) o:318 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:880 (100.0%) o:541 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1201 (100.0%) o:625 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1226 (100.0%) o:634 (99.8%)<br>fail: r:0 (0.0%) o:1 (0.2%) |
1118

1219
### Legend
1320

@@ -79,15 +86,15 @@ The `pattern` validator by default uses the JDK regular expression implementatio
7986

8087
#### Content Encoding
8188

82-
Since Draft 2019-09, the `contentEncoding` keyword does not generate assertions. As the implementation currently does not collect annotations this only generates assertions in Draft 7.
89+
Since Draft 2019-09, the `contentEncoding` keyword does not generate assertions.
8390

8491
#### Content Media Type
8592

86-
Since Draft 2019-09, the `contentMediaType` keyword does not generate assertions. As the implementation currently does not collect annotations this only generates assertions in Draft 7.
93+
Since Draft 2019-09, the `contentMediaType` keyword does not generate assertions.
8794

8895
#### Content Schema
8996

90-
The `contentSchema` keyword does not generate assertions. As the implementation currently does not collect annotations this doesn't do anything.
97+
The `contentSchema` keyword does not generate assertions.
9198

9299
#### Pattern
93100

@@ -108,7 +115,7 @@ This also requires adding the `joni` dependency.
108115
</dependency>
109116
```
110117

111-
### Format
118+
#### Format
112119

113120
Since Draft 2019-09 the `format` keyword only generates annotations by default and does not generate assertions.
114121

@@ -119,7 +126,7 @@ This can be configured on a schema basis by using a meta schema with the appropr
119126
| Draft 2019-09 | `https://json-schema.org/draft/2019-09/vocab/format` | `true` |
120127
| Draft 2020-12 | `https://json-schema.org/draft/2020-12/vocab/format-assertion`| `true`/`false` |
121128

122-
This behavior can be overridden to generate assertions on a per-execution basis by setting the `setFormatAssertionsEnabled` to `true`.
129+
This behavior can be overridden to generate assertions by setting the `setFormatAssertionsEnabled` option to `true`.
123130

124131
| Format | Draft 4 | Draft 6 | Draft 7 | Draft 2019-09 | Draft 2020-12 |
125132
|:----------------------|:-------:|:-------:|:-------:|:-------------:|:-------------:|
@@ -143,7 +150,7 @@ This behavior can be overridden to generate assertions on a per-execution basis
143150
| uri-template | 🚫 | 🟢 | 🟢 | 🟢 | 🟢 |
144151
| uuid | 🚫 | 🚫 | 🟢 | 🟢 | 🟢 |
145152

146-
### Footnotes
153+
##### Footnotes
147154
1. Note that the validation are only optional for some of the keywords/formats.
148155
2. Refer to the corresponding JSON schema for more information on whether the keyword/format is optional or not.
149156

doc/quickstart.md

Lines changed: 48 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,65 @@
11
## Quick Start
22

3-
To use the validator, we need to have both the `JsonSchema` object and `JsonNode` object constructed.
4-
There are many ways to do that.
5-
Here is base test class, that shows several ways to construct these from `String`, `Stream`, `Url`, and `JsonNode`.
6-
Please pay attention to the `JsonSchemaFactory` class as it is the way to construct the `JsonSchema` object.
3+
To use the validator, we need to have the `JsonSchema` loaded and cached.
74

8-
```java
9-
public class BaseJsonSchemaValidatorTest {
5+
For simplicity the following test loads a schema from a `String` or `JsonNode`. Note that loading a schema in this manner is not recommended as a relative `$ref` will not be properly resolved as there is no base IRI.
106

11-
private ObjectMapper mapper = new ObjectMapper();
7+
The preferred method of loading a schema is by using a `SchemaLocation` and by configuring the appropriate `SchemaMapper` and `SchemaLoader` on the `JsonSchemaFactory`.
128

13-
protected JsonNode getJsonNodeFromClasspath(String name) throws IOException {
14-
InputStream is1 = Thread.currentThread().getContextClassLoader()
15-
.getResourceAsStream(name);
16-
return mapper.readTree(is1);
17-
}
9+
```java
10+
package com.example;
1811

19-
protected JsonNode getJsonNodeFromStringContent(String content) throws IOException {
20-
return mapper.readTree(content);
21-
}
12+
import static org.junit.jupiter.api.Assertions.assertEquals;
2213

23-
protected JsonNode getJsonNodeFromUrl(String url) throws IOException {
24-
return mapper.readTree(new URL(url));
25-
}
14+
import java.util.Set;
2615

27-
protected JsonSchema getJsonSchemaFromClasspath(String name) {
28-
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
29-
InputStream is = Thread.currentThread().getContextClassLoader()
30-
.getResourceAsStream(name);
31-
return factory.getSchema(is);
32-
}
16+
import org.junit.jupiter.api.Test;
3317

34-
protected JsonSchema getJsonSchemaFromStringContent(String schemaContent) {
35-
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
36-
return factory.getSchema(schemaContent);
37-
}
18+
import com.fasterxml.jackson.core.JsonProcessingException;
19+
import com.fasterxml.jackson.databind.JsonMappingException;
20+
import com.fasterxml.jackson.databind.JsonNode;
21+
import com.networknt.schema.*;
22+
import com.networknt.schema.serialization.JsonMapperFactory;
3823

39-
protected JsonSchema getJsonSchemaFromUrl(String uri) throws URISyntaxException {
24+
public class SampleTest {
25+
@Test
26+
void schemaFromString() throws JsonMappingException, JsonProcessingException {
4027
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
41-
return factory.getSchema(SchemaLocation.of(uri));
28+
/*
29+
* This should be cached for performance.
30+
*
31+
* Loading from a String is not recommended as there is no base IRI to use for
32+
* resolving relative $ref.
33+
*/
34+
JsonSchema schemaFromString = factory
35+
.getSchema("{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}");
36+
Set<ValidationMessage> errors = schemaFromString.validate("7", InputFormat.JSON);
37+
assertEquals(1, errors.size());
4238
}
4339

44-
protected JsonSchema getJsonSchemaFromJsonNode(JsonNode jsonNode) {
40+
@Test
41+
void schemaFromJsonNode() throws JsonMappingException, JsonProcessingException {
4542
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
46-
return factory.getSchema(jsonNode);
47-
}
48-
49-
// Automatically detect version for given JsonNode
50-
protected JsonSchema getJsonSchemaFromJsonNodeAutomaticVersion(JsonNode jsonNode) {
51-
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersionDetector.detect(jsonNode));
52-
return factory.getSchema(jsonNode);
53-
}
54-
55-
}
56-
```
57-
And the following is one of the test cases in one of the test classes that extend from the above base class. As you can see, it constructs `JsonSchema` and `JsonNode` from `String`.
58-
59-
```java
60-
class Sample extends BaseJsonSchemaValidatorTest {
61-
62-
void test() {
63-
JsonSchema schema = getJsonSchemaFromStringContent("{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}");
64-
JsonNode node = getJsonNodeFromStringContent("7");
65-
Set<ValidationMessage> errors = schema.validate(node);
66-
assertThat(errors.size(), is(1));
67-
68-
// With automatic version detection
69-
JsonNode schemaNode = getJsonNodeFromStringContent(
70-
"{\"$schema\": \"http://json-schema.org/draft-06/schema#\", \"properties\": { \"id\": {\"type\": \"number\"}}}");
71-
JsonSchema schema = getJsonSchemaFromJsonNodeAutomaticVersion(schemaNode);
72-
73-
schema.initializeValidators(); // by default all schemas are loaded lazily. You can load them eagerly via
74-
// initializeValidators()
75-
76-
JsonNode node = getJsonNodeFromStringContent("{\"id\": \"2\"}");
77-
Set<ValidationMessage> errors = schema.validate(node);
78-
assertThat(errors.size(), is(1));
43+
JsonNode schemaNode = JsonMapperFactory.getInstance().readTree(
44+
"{\"$schema\": \"http://json-schema.org/draft-06/schema#\", \"properties\": { \"id\": {\"type\": \"number\"}}}");
45+
/*
46+
* This should be cached for performance.
47+
*
48+
* Loading from a JsonNode is not recommended as there is no base IRI to use for
49+
* resolving relative $ref.
50+
*
51+
* Note that the V4 from the factory is the default version if $schema is not
52+
* specified. As $schema is specified in the data, V6 is used.
53+
*/
54+
JsonSchema schemaFromNode = factory.getSchema(schemaNode);
55+
/*
56+
* By default all schemas are preloaded eagerly but ref resolve failures are not
57+
* thrown. You check if there are issues with ref resolving using
58+
* initializeValidators()
59+
*/
60+
schemaFromNode.initializeValidators();
61+
Set<ValidationMessage> errors = schemaFromNode.validate("{\"id\": \"2\"}", InputFormat.JSON);
62+
assertEquals(1, errors.size());
7963
}
80-
8164
}
82-
8365
```

doc/upgrading.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,76 @@
11
## Upgrading to new versions
22

3+
This library can contain breaking changes in `minor` version releases.
4+
35
This contains information on the notable or breaking changes in each version.
46

7+
### 1.3.1
8+
9+
This does not contain any breaking changes from 1.3.0
10+
11+
* Annotation collection and reporting has been implemented
12+
* Keywords have been refactored to use annotations for evaluation to improve performance and meet functional requirements
13+
* The list and hierarchical output formats have been implemented as per the [Specification for Machine-Readable Output for JSON Schema Validation and Annotation](https://github.com/json-schema-org/json-schema-spec/blob/main/jsonschema-validation-output-machines.md).
14+
* The fail fast evaluation processing has been redesigned and fixed. This currently passes the [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) with fail fast enabled. Previously contains and union type may cause incorrect results.
15+
* This also contains fixes for regressions introduced in 1.3.0
16+
17+
The following keywords were refactored to improve performance and meet the functional requirements.
18+
19+
In particular this converts the `unevaluatedItems` and `unevaluatedProperties` validators to use annotations to perform the evaluation instead of the current mechanism which affects performance. This also refactors `$recursiveRef` to not rely on that same mechanism.
20+
21+
* `unevaluatedProperties`
22+
* `unevaluatedItems`
23+
* `properties`
24+
* `patternProperties`
25+
* `items` / `additionalItems`
26+
* `prefixItems` / `items`
27+
* `contains`
28+
* `$recursiveRef`
29+
30+
This also fixes the issue where the `unevaluatedItems` keyword does not take into account the `contains` keyword when performing the evaluation.
31+
32+
This also fixes cases where `anyOf` short-circuits to not short-circuit the evaluation if a adjacent `unevaluatedProperties` or `unevaluatedItems` keyword exists.
33+
34+
This should fix most of the remaining functional and performance issues.
35+
36+
#### Functional
37+
38+
| Implementations | Overall | DRAFT_03 | DRAFT_04 | DRAFT_06 | DRAFT_07 | DRAFT_2019_09 | DRAFT_2020_12 |
39+
|-----------------|-------------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------|
40+
| NetworkNt | pass: r:4703 (100.0%) o:2369 (100.0%)<br>fail: r:0 (0.0%) o:1 (0.0%) | | pass: r:600 (100.0%) o:251 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:796 (100.0%) o:318 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:880 (100.0%) o:541 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1201 (100.0%) o:625 (100.0%)<br>fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1226 (100.0%) o:634 (99.8%)<br>fail: r:0 (0.0%) o:1 (0.2%) |
41+
42+
#### Performance
43+
44+
##### NetworkNT 1.3.1
45+
46+
```
47+
Benchmark Mode Cnt Score Error Units
48+
NetworkntBenchmark.testValidate thrpt 10 6776.693 ± 115.309 ops/s
49+
NetworkntBenchmark.testValidate:·gc.alloc.rate thrpt 10 971.191 ± 16.420 MB/sec
50+
NetworkntBenchmark.testValidate:·gc.alloc.rate.norm thrpt 10 165318.816 ± 0.459 B/op
51+
NetworkntBenchmark.testValidate:·gc.churn.G1_Eden_Space thrpt 10 968.894 ± 51.234 MB/sec
52+
NetworkntBenchmark.testValidate:·gc.churn.G1_Eden_Space.norm thrpt 10 164933.962 ± 8636.203 B/op
53+
NetworkntBenchmark.testValidate:·gc.churn.G1_Survivor_Space thrpt 10 0.002 ± 0.001 MB/sec
54+
NetworkntBenchmark.testValidate:·gc.churn.G1_Survivor_Space.norm thrpt 10 0.274 ± 0.218 B/op
55+
NetworkntBenchmark.testValidate:·gc.count thrpt 10 89.000 counts
56+
NetworkntBenchmark.testValidate:·gc.time thrpt 10 99.000 ms
57+
```
58+
59+
###### Everit 1.14.1
60+
61+
```
62+
Benchmark Mode Cnt Score Error Units
63+
EveritBenchmark.testValidate thrpt 10 3719.192 ± 125.592 ops/s
64+
EveritBenchmark.testValidate:·gc.alloc.rate thrpt 10 1448.208 ± 74.746 MB/sec
65+
EveritBenchmark.testValidate:·gc.alloc.rate.norm thrpt 10 449621.927 ± 7400.825 B/op
66+
EveritBenchmark.testValidate:·gc.churn.G1_Eden_Space thrpt 10 1446.397 ± 79.919 MB/sec
67+
EveritBenchmark.testValidate:·gc.churn.G1_Eden_Space.norm thrpt 10 449159.799 ± 18614.931 B/op
68+
EveritBenchmark.testValidate:·gc.churn.G1_Survivor_Space thrpt 10 0.001 ± 0.001 MB/sec
69+
EveritBenchmark.testValidate:·gc.churn.G1_Survivor_Space.norm thrpt 10 0.364 ± 0.391 B/op
70+
EveritBenchmark.testValidate:·gc.count thrpt 10 133.000 counts
71+
EveritBenchmark.testValidate:·gc.time thrpt 10 148.000 ms
72+
```
73+
574
### 1.3.0
675

776
This adds support for Draft 2020-12

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
*/
1616
package com.networknt.schema;
1717

18+
/**
19+
* Base collector.
20+
*
21+
* @param <E> the type
22+
*/
1823
public abstract class AbstractCollector<E> implements Collector<E> {
1924

2025
@Override

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy