Skip to content

Commit 35747e0

Browse files
Merge remote-tracking branch 'upstream/master'
2 parents 09b2b04 + dfd63aa commit 35747e0

29 files changed

+1379
-288
lines changed

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,28 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
### Changed
1111

12+
## 1.0.51 - 2021-03-30
13+
14+
### Added
15+
16+
### Changed
17+
18+
- fixes #392 NPE due to concurrency bug. Thanks @Keymaster65
19+
- fixes #391 override default EmailValidator, if set custom email format. Thanks @whirosan
20+
- fixes #390 Add discriminator support. Thanks @FWiesner
21+
22+
## 1.0.50 - 2021-03-18
23+
24+
### Added
25+
26+
### Changed
27+
28+
- fixes #387 Resolve the test case errors for TypeFactoryTest
29+
- fixes #385 Fixing concurrency and compilation issues. Thanks @prashanthjos
30+
- fixes #383 Nested oneOf gives incorrect validation error. Thanks @JonasProgrammer
31+
- fixes #379 Add lossless narrowing convertion. Thanks @hkupty
32+
- fixes #378 Upgrade Jackson to 2.12.1 and Undertow to 2.2.4.Final
33+
1234
## 1.0.49 - 2021-02-17
1335

1436
### Added

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ Maven:
8282
<dependency>
8383
<groupId>com.networknt</groupId>
8484
<artifactId>json-schema-validator</artifactId>
85-
<version>1.0.49</version>
85+
<version>1.0.51</version>
8686
</dependency>
8787
```
8888

8989
Gradle:
9090

9191
```
9292
dependencies {
93-
compile(group: "com.networknt", name: "json-schema-validator", version: "1.0.49");
93+
compile(group: "com.networknt", name: "json-schema-validator", version: "1.0.51");
9494
}
9595
```
9696

doc/openapi-discriminators.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
[//]: # (Copyright 2021, Oracle and/or its affiliates.)
2+
3+
## OpenAPI 3.x discriminator support
4+
5+
Starting with `1.0.51`, `json-schema-validator` partly supports the use of discriminators as described under
6+
https://github.com/OAI/OpenAPI-Specification/blame/master/versions/3.0.3.md#L2693 and following.
7+
8+
## How to use
9+
10+
1. Configure `SchemaValidatorsConfig` accordingly:
11+
```java
12+
class Demo{
13+
void demo() {
14+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
15+
config.setOpenAPI3StyleDiscriminators(true); // defaults to false
16+
}
17+
}
18+
```
19+
2. Use the configured `SchemaValidatorsConfig` with the `JSONSchemaFactory` when creating the `JSONSchema`
20+
```java
21+
class Demo{
22+
void demo() {
23+
SchemaValidatorsConfig config = new SchemaValidatorsConfig();
24+
config.setOpenAPI3StyleDiscriminators(true); // defaults to false
25+
JsonSchema schema = validatorFactory.getSchema(schemaURI, schemaJacksonJsonNode, config);
26+
}
27+
}
28+
```
29+
3. Ensure that the type field that you want to use as discriminator `propertyName` is required in your schema
30+
31+
## Scope of Support
32+
33+
Discriminators are unfortunately somewhat vague in their definition, especially in regard to JSON Schema validation. So, only
34+
those parts that are indisputable are considered at this moment.
35+
36+
### Supported:
37+
38+
* Polymorphism using `allOf` and `anyOf` with implicit and explicit `mapping`
39+
* `discriminator` on base types and types derived
40+
thereof `A(with base discriminator) -> B(with optional additive discriminator) -> C(with optional additive discriminator)`
41+
42+
### Not supported:
43+
44+
* `propertyName` redefinition is prohibited on additive discriminators
45+
* `mapping` key redefinition is also prohibited on additive discriminators
46+
* `oneOf` ignores discriminators as today it is not clear from the spec whether `oneOf` + `discriminator` should be equal to
47+
`anyOf` + `discriminator` or not. Especially if `oneOf` should respect the discriminator and skip the other schemas, it's
48+
functionally not JSON Schema `oneOf` anymore as multiple matches would not make the validation fail anymore.
49+
* the specification indicates that inline properties should be ignored.
50+
So, this example would respect `foo`
51+
```yaml
52+
allOf:
53+
- $ref: otherSchema
54+
- type: object
55+
properties:
56+
foo:
57+
type: string
58+
required: ["foo"]
59+
```
60+
while
61+
```yaml
62+
properties:
63+
foo:
64+
type: string
65+
required: ["foo"]
66+
allOf:
67+
- $ref: otherSchema
68+
```
69+
should ignore `foo`. **Ignoring `foo` in the second example is currently not implemented**
70+
* You won't get a warning if your `discriminator` uses a field for `propertyName` that is not `required`
71+
72+
## Schema Examples
73+
74+
more examples in https://github.com/networknt/json-schema-validator/blob/master/src/test/resources/openapi3/discriminator.json
75+
76+
### Base type and extended type (the `anyOf` forward references are required)
77+
78+
#### the simplest example:
79+
80+
```json
81+
{
82+
"anyOf": [
83+
{
84+
"$ref": "#/components/schemas/Room"
85+
},
86+
{
87+
"$ref": "#/components/schemas/BedRoom"
88+
}
89+
],
90+
"components": {
91+
"schemas": {
92+
"Room": {
93+
"type": "object",
94+
"properties": {
95+
"@type": {
96+
"type": "string"
97+
},
98+
"floor": {
99+
"type": "integer"
100+
}
101+
},
102+
"required": [
103+
"@type"
104+
],
105+
"discriminator": {
106+
"propertyName": "@type"
107+
}
108+
},
109+
"BedRoom": {
110+
"type": "object",
111+
"allOf": [
112+
{
113+
"$ref": "#/components/schemas/Room"
114+
},
115+
{
116+
"type": "object",
117+
"properties": {
118+
"numberOfBeds": {
119+
"type": "integer"
120+
}
121+
},
122+
"required": [
123+
"numberOfBeds"
124+
]
125+
}
126+
]
127+
}
128+
}
129+
}
130+
}
131+
```
132+
133+
#### Here the default mapping key for `BedRoom` is overridden with `bed` from `Room`
134+
135+
```json
136+
{
137+
"anyOf": [
138+
{
139+
"$ref": "#/components/schemas/Room"
140+
},
141+
{
142+
"$ref": "#/components/schemas/BedRoom"
143+
}
144+
],
145+
"components": {
146+
"schemas": {
147+
"Room": {
148+
"type": "object",
149+
"properties": {
150+
"@type": {
151+
"type": "string"
152+
},
153+
"floor": {
154+
"type": "integer"
155+
}
156+
},
157+
"required": [
158+
"@type"
159+
],
160+
"discriminator": {
161+
"propertyName": "@type",
162+
"mapping": {
163+
"bed": "#/components/schemas/BedRoom"
164+
}
165+
}
166+
},
167+
"BedRoom": {
168+
"type": "object",
169+
"allOf": [
170+
{
171+
"$ref": "#/components/schemas/Room"
172+
},
173+
{
174+
"type": "object",
175+
"properties": {
176+
"numberOfBeds": {
177+
"type": "integer"
178+
}
179+
},
180+
"required": [
181+
"numberOfBeds"
182+
]
183+
}
184+
]
185+
}
186+
}
187+
}
188+
}
189+
```
190+
191+
###

doc/validators.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,27 @@ You can use GroovyKeyword like below:
8080
}
8181
}
8282
````
83+
84+
### Override Email/UUID/DateTime Validator
85+
86+
In this library, if the format keyword is "email", "uuid", "date", "date-time", default validator provided by the library will be used.
87+
88+
If you want to override this behaivor, do as belows.
89+
90+
```
91+
public JsonSchemaFactory mySchemaFactory() {
92+
// base on JsonMetaSchema.V201909 copy code below
93+
String URI = "https://json-schema.org/draft/2019-09/schema";
94+
String ID = "$id";
95+
96+
JsonMetaSchema overrideEmailValidatorMetaSchema = new JsonMetaSchema.Builder(URI)
97+
.idKeyword(ID)
98+
// Override EmailValidator
99+
.addFormat(new PatternFormat("email", "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"))
100+
.build();
101+
102+
return new JsonSchemaFactory.Builder().defaultMetaSchemaURI(overrideEmailValidatorMetaSchema.getUri())
103+
.addMetaSchema(overrideEmailValidatorMetaSchema)
104+
.build();
105+
}
106+
```

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
<modelVersion>4.0.0</modelVersion>
2121
<groupId>com.networknt</groupId>
2222
<artifactId>json-schema-validator</artifactId>
23-
<version>1.0.49</version>
23+
<version>1.0.51</version>
2424
<packaging>bundle</packaging>
2525
<description>A json schema validator that supports draft v4, v6, v7 and v2019-09</description>
2626
<url>https://github.com/networknt/json-schema-validator</url>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,9 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
9393
errors.add(buildValidationMessage(at, pname));
9494
} else {
9595
if (additionalPropertiesSchema != null) {
96-
ValidatorState state = validatorState.get();
96+
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
9797
if (state != null && state.isWalkEnabled()) {
98-
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabledWhileWalking()));
98+
errors.addAll(additionalPropertiesSchema.walk(node.get(pname), rootNode, at + "." + pname, state.isValidationEnabled()));
9999
} else {
100100
errors.addAll(additionalPropertiesSchema.validate(node.get(pname), rootNode, at + "." + pname));
101101
}

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

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,34 @@
1616

1717
package com.networknt.schema;
1818

19+
import java.util.ArrayList;
20+
import java.util.Collections;
21+
import java.util.Iterator;
22+
import java.util.LinkedHashSet;
23+
import java.util.List;
24+
import java.util.Set;
25+
1926
import com.fasterxml.jackson.databind.JsonNode;
27+
import com.fasterxml.jackson.databind.node.ObjectNode;
2028
import org.slf4j.Logger;
2129
import org.slf4j.LoggerFactory;
2230

23-
import java.util.*;
24-
2531
public class AllOfValidator extends BaseJsonValidator implements JsonValidator {
2632
private static final Logger logger = LoggerFactory.getLogger(AllOfValidator.class);
2733

28-
private List<JsonSchema> schemas = new ArrayList<JsonSchema>();
29-
34+
private final ValidationContext validationContext;
35+
private final List<JsonSchema> schemas = new ArrayList<JsonSchema>();
36+
3037
public AllOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) {
3138
super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ALL_OF, validationContext);
39+
this.validationContext = validationContext;
3240
int size = schemaNode.size();
3341
for (int i = 0; i < size; i++) {
34-
schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUri(), schemaNode.get(i), parentSchema));
42+
schemas.add(new JsonSchema(validationContext,
43+
getValidatorType().getValue(),
44+
parentSchema.getCurrentUri(),
45+
schemaNode.get(i),
46+
parentSchema));
3547
}
3648
}
3749

@@ -42,20 +54,49 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
4254

4355
for (JsonSchema schema : schemas) {
4456
errors.addAll(schema.validate(node, rootNode, at));
57+
58+
if (config.isOpenAPI3StyleDiscriminators()) {
59+
final Iterator<JsonNode> arrayElements = schemaNode.elements();
60+
while (arrayElements.hasNext()) {
61+
final ObjectNode allOfEntry = (ObjectNode) arrayElements.next();
62+
final JsonNode $ref = allOfEntry.get("$ref");
63+
if (null != $ref) {
64+
final ValidationContext.DiscriminatorContext currentDiscriminatorContext = validationContext
65+
.getCurrentDiscriminatorContext();
66+
final ObjectNode discriminator = currentDiscriminatorContext
67+
.getDiscriminatorForPath(allOfEntry.get("$ref").asText());
68+
if (null != discriminator) {
69+
registerAndMergeDiscriminator(currentDiscriminatorContext, discriminator, parentSchema, at);
70+
// now we have to check whether we have hit the right target
71+
final String discriminatorPropertyName = discriminator.get("propertyName").asText();
72+
final String discriminatorPropertyValue = node.get(discriminatorPropertyName).textValue();
73+
74+
final JsonSchema jsonSchema = parentSchema;
75+
checkDiscriminatorMatch(
76+
currentDiscriminatorContext,
77+
discriminator,
78+
discriminatorPropertyValue,
79+
jsonSchema);
80+
}
81+
}
82+
}
83+
}
4584
}
4685

4786
return Collections.unmodifiableSet(errors);
4887
}
49-
50-
@Override
51-
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
52-
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
53-
54-
for (JsonSchema schema : schemas) {
55-
// Walk through the schema
56-
validationMessages.addAll(schema.walk(node, rootNode, at, shouldValidateSchema));
57-
}
58-
return Collections.unmodifiableSet(validationMessages);
59-
}
88+
89+
90+
91+
@Override
92+
public Set<ValidationMessage> walk(JsonNode node, JsonNode rootNode, String at, boolean shouldValidateSchema) {
93+
Set<ValidationMessage> validationMessages = new LinkedHashSet<ValidationMessage>();
94+
95+
for (JsonSchema schema : schemas) {
96+
// Walk through the schema
97+
validationMessages.addAll(schema.walk(node, rootNode, at, shouldValidateSchema));
98+
}
99+
return Collections.unmodifiableSet(validationMessages);
100+
}
60101

61102
}

0 commit comments

Comments
 (0)