Skip to content

Commit 227c1f7

Browse files
committed
Refactor discriminator
1 parent 98a5f5f commit 227c1f7

File tree

3 files changed

+101
-16
lines changed

3 files changed

+101
-16
lines changed

src/main/java/com/networknt/schema/keyword/AnyOfValidator.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -113,23 +113,11 @@ && canShortCircuit(executionContext)) {
113113
return;
114114
} else if (this.schemaContext.isDiscriminatorKeywordEnabled()) {
115115
JsonNode refNode = schema.getSchemaNode().get("$ref");
116-
DiscriminatorState state = executionContext.getDiscriminatorMapping().get(instanceLocation);
116+
DiscriminatorState discriminator = executionContext.getDiscriminatorMapping()
117+
.get(instanceLocation);
117118
boolean discriminatorMatchFound = false;
118119
if (refNode != null) {
119-
// Check if there is a match
120-
String mappedSchema = state.getMappedSchema();
121-
if (mappedSchema != null) {
122-
String ref = refNode.asText();
123-
if (state.isExplicitMapping() && ref.equals(mappedSchema)) {
124-
// Explicit matching
125-
discriminatorMatchFound = true;
126-
state.setMatchedSchema(ref);
127-
} else if (!state.isExplicitMapping() && ref.endsWith(mappedSchema)) {
128-
// Implicit matching
129-
discriminatorMatchFound = true;
130-
state.setMatchedSchema(ref);
131-
}
132-
}
120+
discriminatorMatchFound = discriminator.matches(refNode.asText());
133121
}
134122
if (discriminatorMatchFound) {
135123
/*

src/main/java/com/networknt/schema/keyword/DiscriminatorState.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,80 @@
2020
* Discriminator state for an instance location.
2121
*/
2222
public class DiscriminatorState {
23+
/**
24+
* The propertyName field defined in the discriminator keyword schema.
25+
*/
2326
private String propertyName;
27+
/**
28+
* The discriminating value from the payload matching the property name.
29+
*/
2430
private String discriminatingValue;
31+
/**
32+
* The mapped schema from the mapping or the discriminating value if there is no
33+
* mapping.
34+
*/
2535
private String mappedSchema = null;
36+
37+
/**
38+
* Whether the mapped schema is explicitly mapped using mapping or is the
39+
* discriminating value.
40+
*/
2641
private boolean explicitMapping = false;
42+
43+
/**
44+
* The matched schema for the instance.
45+
*/
2746
private String matchedSchema;
2847

48+
/**
49+
* Determines if there is a match of the mapped schema to the ref and update the
50+
* matched schema if there is a match.
51+
*
52+
* @param refSchema the $ref value
53+
* @return true if there is a match
54+
*/
55+
public boolean matches(String refSchema) {
56+
boolean discriminatorMatchFound = false;
57+
String mappedSchema = getMappedSchema();
58+
if (mappedSchema != null) {
59+
if (isExplicitMapping() && refSchema.equals(mappedSchema)) {
60+
// Explicit matching
61+
discriminatorMatchFound = true;
62+
setMatchedSchema(refSchema);
63+
} else if (!isExplicitMapping() && isImplicitMatch(refSchema, mappedSchema)) {
64+
// Implicit matching
65+
discriminatorMatchFound = true;
66+
setMatchedSchema(refSchema);
67+
}
68+
}
69+
return discriminatorMatchFound;
70+
}
71+
72+
/**
73+
* Determine if there is an implicit match of the mapped schema to the ref.
74+
*
75+
* @param refSchema the $ref value
76+
* @param mappedSchema the mapped schema
77+
* @return true if there is a match
78+
*/
79+
private static boolean isImplicitMatch(String refSchema, String mappedSchema) {
80+
/*
81+
* To ensure that an ambiguous value (e.g. "foo") is treated as a relative URI
82+
* reference by all implementations, authors MUST prefix it with the "." path
83+
* segment (e.g. "./foo").
84+
*/
85+
if (mappedSchema.startsWith(".")) {
86+
return refSchema.equals(mappedSchema);
87+
} else {
88+
int found = refSchema.lastIndexOf('/');
89+
if (found == -1) {
90+
return refSchema.equals(mappedSchema);
91+
} else {
92+
return refSchema.substring(found + 1).equals(mappedSchema);
93+
}
94+
}
95+
}
96+
2997
/**
3098
* Gets the property name defined in the discriminator keyword schema.
3199
*
@@ -149,5 +217,4 @@ public boolean hasMatchedSchema() {
149217
return this.matchedSchema != null;
150218
}
151219

152-
153220
}

src/main/java/com/networknt/schema/keyword/DiscriminatorValidator.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,16 @@ public class DiscriminatorValidator extends BaseKeywordValidator {
5454
*/
5555
private final Map<String, String> mapping;
5656

57+
/**
58+
* The schema name or URI reference to a schema that is expected to validate the
59+
* structure of the model when the discriminating property is not present in the
60+
* payload or contains a value for which there is no explicit or implicit
61+
* mapping.
62+
* <p>
63+
* Since OpenAPI 3.2.0
64+
*/
65+
private final String defaultMapping;
66+
5767
public DiscriminatorValidator(SchemaLocation schemaLocation, NodePath evaluationPath, JsonNode schemaNode,
5868
Schema parentSchema, SchemaContext schemaContext) {
5969
super(KeywordType.DISCRIMINATOR, schemaNode, schemaLocation, parentSchema, schemaContext, evaluationPath);
@@ -77,9 +87,19 @@ public DiscriminatorValidator(SchemaLocation schemaLocation, NodePath evaluation
7787
} else {
7888
this.mapping = Collections.emptyMap();
7989
}
90+
91+
// Check if OpenAPI 3.2.0
92+
JsonNode defaultMapping = discriminator.get("defaultMapping");
93+
if (defaultMapping != null) {
94+
this.defaultMapping = defaultMapping.asText();
95+
} else {
96+
this.defaultMapping = null;
97+
}
98+
8099
} else {
81100
this.propertyName = "";
82101
this.mapping = Collections.emptyMap();
102+
this.defaultMapping = null;
83103
}
84104
}
85105

@@ -151,6 +171,15 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
151171
}
152172
}
153173
} else {
174+
/*
175+
* Since OpenAPI 3.2.0 if defaultMapping is set, then the property is optional.
176+
*/
177+
if (this.defaultMapping != null) {
178+
state.setMappedSchema(defaultMapping);
179+
state.setExplicitMapping(true);
180+
return;
181+
}
182+
154183
/*
155184
* The property is not present in the payload. This property SHOULD be required
156185
* in the payload schema, as the behavior when the property is absent is
@@ -166,6 +195,7 @@ public void validate(ExecutionContext executionContext, JsonNode node, JsonNode
166195
*/
167196
if (this.schemaContext.getSchemaRegistryConfig().isStrict("discriminator", Boolean.FALSE)) {
168197
executionContext.addError(error().instanceNode(node).instanceLocation(instanceLocation)
198+
.locale(executionContext.getExecutionConfig().getLocale())
169199
.messageKey("discriminator.missing_discriminating_value").arguments(this.propertyName).build());
170200
}
171201
}

0 commit comments

Comments
 (0)