Skip to content

Commit e8c9a2d

Browse files
"Adding custom message support" (#438)
Co-authored-by: aditya lath <[email protected]>
1 parent 5ec08c7 commit e8c9a2d

File tree

11 files changed

+143
-10
lines changed

11 files changed

+143
-10
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,8 @@ public interface ErrorMessageType {
3535
*/
3636
MessageFormat getMessageFormat();
3737

38+
default String getCustomMessage() {
39+
return null;
40+
}
41+
3842
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,11 @@ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSc
6868
public String getValue() {
6969
return type.getValue();
7070
}
71+
72+
@Override
73+
public void setCustomMessage(String message) {
74+
type.setCustomMessage(message);
75+
}
76+
77+
7178
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ public static JsonMetaSchema getInstance() {
181181
new NonValidationKeyword("$comment"),
182182
new NonValidationKeyword("contentMediaType"),
183183
new NonValidationKeyword("contentEncoding"),
184-
new NonValidationKeyword("examples")
184+
new NonValidationKeyword("examples"),
185+
new NonValidationKeyword("message")
185186
))
186187
.build();
187188
}
@@ -375,8 +376,9 @@ public String getIdKeyword() {
375376
return idKeyword;
376377
}
377378

379+
378380
public JsonValidator newValidator(ValidationContext validationContext, String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
379-
JsonSchema parentSchema) {
381+
JsonSchema parentSchema, String customMessage) {
380382

381383
try {
382384
Keyword kw = keywords.get(keyword);
@@ -386,6 +388,7 @@ public JsonValidator newValidator(ValidationContext validationContext, String sc
386388
}
387389
return null;
388390
}
391+
kw.setCustomMessage(customMessage);
389392
return kw.newValidator(schemaPath, schemaNode, parentSchema, validationContext);
390393
} catch (InvocationTargetException e) {
391394
if (e.getTargetException() instanceof JsonSchemaException) {

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,21 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
208208
Map<String, JsonValidator> validators = new HashMap<String, JsonValidator>();
209209
if (schemaNode.isBoolean()) {
210210
if (schemaNode.booleanValue()) {
211-
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "true", schemaNode, this);
211+
final String customMessage = getCustomMessage(schemaNode, "true");
212+
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "true", schemaNode, this, customMessage);
212213
validators.put(getSchemaPath() + "/true", validator);
213214
} else {
214-
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "false", schemaNode, this);
215+
final String customMessage = getCustomMessage(schemaNode, "false");
216+
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "false", schemaNode, this, customMessage);
215217
validators.put(getSchemaPath() + "/false", validator);
216218
}
217219
} else {
218220
Iterator<String> pnames = schemaNode.fieldNames();
219221
while (pnames.hasNext()) {
220222
String pname = pnames.next();
221223
JsonNode nodeToUse = pname.equals("if") ? schemaNode : schemaNode.get(pname);
222-
223-
JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this);
224+
String customMessage = getCustomMessage(schemaNode, pname);
225+
JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this, customMessage);
224226
if (validator != null) {
225227
validators.put(getSchemaPath() + "/" + pname, validator);
226228

@@ -234,6 +236,24 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
234236
return validators;
235237
}
236238

239+
private String getCustomMessage(JsonNode schemaNode, String pname) {
240+
final JsonSchema parentSchema = getParentSchema();
241+
final JsonNode message = getMessageNode(schemaNode, parentSchema);
242+
if(message != null && message.get(pname) != null) {
243+
return message.get(pname).asText();
244+
}
245+
return null;
246+
}
247+
248+
private JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema) {
249+
JsonNode nodeContainingMessage;
250+
if (parentSchema == null)
251+
nodeContainingMessage = schemaNode;
252+
else
253+
nodeContainingMessage = parentSchema.schemaNode;
254+
return nodeContainingMessage.get("message");
255+
}
256+
237257
/************************ START OF VALIDATE METHODS **********************************/
238258

239259
public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@
2222
public interface Keyword {
2323
String getValue();
2424

25+
default void setCustomMessage(String message) {
26+
//setCustom message
27+
}
28+
2529
JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception;
2630
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaS
5353
}
5454

5555
public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
56-
JsonSchema parentSchema) {
57-
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema);
56+
JsonSchema parentSchema, String customMessage) {
57+
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema, customMessage);
5858
}
5959

6060
public String resolveSchemaId(JsonNode schemaNode) {

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.networknt.schema;
1818

19+
import org.apache.commons.lang3.StringUtils;
20+
1921
import java.text.MessageFormat;
2022
import java.util.Arrays;
2123
import java.util.Map;
@@ -114,7 +116,8 @@ public void setType(String type) {
114116
public static ValidationMessage of(String type, ErrorMessageType errorMessageType, String at, String... arguments) {
115117
ValidationMessage.Builder builder = new ValidationMessage.Builder();
116118
builder.code(errorMessageType.getErrorCode()).path(at).arguments(arguments)
117-
.format(errorMessageType.getMessageFormat()).type(type);
119+
.format(errorMessageType.getMessageFormat()).type(type)
120+
.customMessage(errorMessageType.getCustomMessage());
118121
return builder.build();
119122
}
120123

@@ -132,6 +135,7 @@ public static class Builder {
132135
private String[] arguments;
133136
private Map<String, Object> details;
134137
private MessageFormat format;
138+
private String customMessage;
135139

136140
public Builder type(String type) {
137141
this.type = type;
@@ -163,6 +167,11 @@ public Builder format(MessageFormat format) {
163167
return this;
164168
}
165169

170+
public Builder customMessage(String customMessage) {
171+
this.customMessage = customMessage;
172+
return this;
173+
}
174+
166175
public ValidationMessage build() {
167176
ValidationMessage msg = new ValidationMessage();
168177
msg.setType(type);
@@ -179,7 +188,12 @@ public ValidationMessage build() {
179188
objs[i] = arguments[i - 1];
180189
}
181190
}
182-
msg.setMessage(format.format(objs));
191+
if(StringUtils.isNotBlank(customMessage)) {
192+
msg.setMessage(customMessage);
193+
} else {
194+
msg.setMessage(format.format(objs));
195+
}
196+
183197
}
184198

185199
return msg;

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSc
8686
private final String value;
8787
private final String errorCode;
8888
private final MessageFormat messageFormat;
89+
private String customMessage;
8990
private final String errorCodeKey;
9091
private final Class validator;
9192
private final long versionCode;
@@ -98,6 +99,7 @@ private ValidatorTypeCode(String value, String errorCode, MessageFormat messageF
9899
this.errorCodeKey = value + "ErrorCode";
99100
this.validator = validator;
100101
this.versionCode = versionCode;
102+
this.customMessage = null;
101103
}
102104

103105
public static List<ValidatorTypeCode> getNonFormatKeywords(SpecVersion.VersionFlag versionFlag) {
@@ -147,6 +149,14 @@ public MessageFormat getMessageFormat() {
147149
return messageFormat;
148150
}
149151

152+
public void setCustomMessage(String message) {
153+
this.customMessage = message;
154+
}
155+
156+
public String getCustomMessage() {
157+
return customMessage;
158+
}
159+
150160
public String getErrorCodeKey() {
151161
return errorCodeKey;
152162
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.networknt.schema;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import org.junit.Assert;
6+
import org.junit.Test;
7+
8+
import java.io.InputStream;
9+
import java.util.Set;
10+
11+
/**
12+
* Validating custom message
13+
*/
14+
public class Issue426Test {
15+
protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) {
16+
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
17+
return factory.getSchema(schemaContent);
18+
}
19+
20+
protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception {
21+
ObjectMapper mapper = new ObjectMapper();
22+
return mapper.readTree(content);
23+
}
24+
25+
@Test
26+
public void shouldWorkV7() throws Exception {
27+
String schemaPath = "/schema/issue426-v7.json";
28+
String dataPath = "/data/issue426.json";
29+
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
30+
JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
31+
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
32+
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
33+
Set<ValidationMessage> errors = schema.validate(node);
34+
Assert.assertEquals(2, errors.size());
35+
final JsonNode message = schema.schemaNode.get("message");
36+
for(ValidationMessage error : errors) {
37+
//validating custom message
38+
Assert.assertEquals(message.get(error.getType()).asText(), error.getMessage());
39+
}
40+
}
41+
}
42+

src/test/resources/data/issue426.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"firstName": {},
3+
"foo": [
4+
1,
5+
2,
6+
3,
7+
4
8+
]
9+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$id": "https://example.com/person.schema.json",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"title": "Person",
5+
"type": "object",
6+
"properties": {
7+
"firstName": {
8+
"type": "string",
9+
"description": "The person's first name."
10+
},
11+
"foo": {
12+
"type": "array",
13+
"maxItems": 3
14+
}
15+
},
16+
"message": {
17+
"maxItems" : "MaxItem must be 3 only",
18+
"type" : "Invalid type"
19+
}
20+
}

0 commit comments

Comments
 (0)