Repository: networknt/json-schema-validator
Pull request #391 (2021-03-24 07:24:20)

override default EmailValidator, if set custom email format
This pull request is overwrite the EmailValidator if I set custom email format.
The EmailValidator applied in "format: email" doesn't meet the requirements of my project, and I would like to overwrite it.
But, constructor of JsonSchema is private, and inner class Builder is forced to use EmailValidator in the case of "format: email" in the build method.


stevehu: @whirosan What is your customized email format? If it is generic, do you think it can replace the default one in the library? When I am looking at the solution, I am thinking about if we can make the solution to be applicable to all the other formats? What do you think? 
whirosan: @stevehu 
I want to use custom Pattern Fomat, because I have to meet the application's special EMail validation requirements.
I think that the validation of the email address may be changed depending on the application's requirements.
On the other hand, regarding UUID, date, and date-time, I don't feel the need for customization at the moment, but in some cases I may want to customize it.
I modified it so that it can be overridden by UUID, date, and date-time, so please review it.
stevehu: @whirosan Look good to me. Would you be able to add a test case and write a small document so that others can learn how to use it? Thanks.
whirosan: @stevehu Thank you very much.
I've added a small test and documentation, so please review it.

Patch:
diff --git a/doc/validators.md b/doc/validators.md
index f8869b8..0cfbe9c 100644
--- a/doc/validators.md
+++ b/doc/validators.md
@@ -80,3 +80,27 @@ You can use GroovyKeyword like below:
   }
 }
 ````
+
+### Override Email/UUID/DateTime Validator
+
+In this library, if the format keyword is "email", "uuid", "date", "date-time", default validator provided by the library will be used.
+
+If you want to override this behaivor, do as belows.
+
+```
+public JsonSchemaFactory mySchemaFactory() {
+    // base on JsonMetaSchema.V201909 copy code below
+    String URI = "https://json-schema.org/draft/2019-09/schema";
+    String ID = "$id";
+
+    JsonMetaSchema overrideEmailValidatorMetaSchema = new JsonMetaSchema.Builder(URI)
+            .idKeyword(ID)
+            // Override EmailValidator
+            .addFormat(new PatternFormat("email", "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"))
+            .build();
+
+    return new JsonSchemaFactory.Builder().defaultMetaSchemaURI(overrideEmailValidatorMetaSchema.getUri())
+            .addMetaSchema(overrideEmailValidatorMetaSchema)
+            .build();
+}
+```
\ No newline at end of file
diff --git a/src/main/java/com/networknt/schema/FormatKeyword.java b/src/main/java/com/networknt/schema/FormatKeyword.java
index 931da5e..f17d0e6 100644
--- a/src/main/java/com/networknt/schema/FormatKeyword.java
+++ b/src/main/java/com/networknt/schema/FormatKeyword.java
@@ -48,6 +48,10 @@ public class FormatKeyword implements Keyword {
         if (schemaNode != null && schemaNode.isTextual()) {
             String formatName = schemaNode.textValue();
             format = formats.get(formatName);
+            // if you set custom format, override default Email/DateTime/UUID Validator
+            if (format != null) {
+                return new FormatValidator(schemaPath, schemaNode, parentSchema, validationContext, format);
+            }
             // Validate date and time separately
             if (formatName.equals(DATE) || formatName.equals(DATE_TIME)) {
                 return new DateTimeValidator(schemaPath, schemaNode, parentSchema, validationContext, formatName);


Test patch:
diff --git a/src/test/java/com/networknt/schema/OverrideValidatorTest.java b/src/test/java/com/networknt/schema/OverrideValidatorTest.java
new file mode 100644
index 0000000..d128040
--- /dev/null
+++ b/src/test/java/com/networknt/schema/OverrideValidatorTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2016 Network New Technologies Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.networknt.schema;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+public class OverrideValidatorTest {
+
+    @Test
+    public void overrideDefaultValidator() throws JsonProcessingException, IOException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        final String URI = "https://github.com/networknt/json-schema-validator/tests/schemas/example01";
+        final String schema = "{\n" +
+        "  \"$schema\":\n" +
+        "    \"https://github.com/networknt/json-schema-validator/tests/schemas/example01\",\n" +
+        "  \"properties\": {\"mailaddress\": {\"type\": \"string\", \"format\": \"email\"}}\n" +
+        "}";
+        final JsonNode targetNode = objectMapper.readTree("{\"mailaddress\": \"a-zA-Z0-9.!#$%&'*+@a---a.a--a\"}");
+        // Use Default EmailValidator
+        final JsonMetaSchema validatorMetaSchema = JsonMetaSchema
+                .builder(URI, JsonMetaSchema.getV201909())
+                .build();
+
+        final JsonSchemaFactory validatorFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).addMetaSchema(validatorMetaSchema).build();
+        final JsonSchema validatorSchema = validatorFactory.getSchema(schema);
+
+        Set<ValidationMessage> messages = validatorSchema.validate(targetNode);
+        assertEquals(1, messages.size());
+
+        // Override EmailValidator
+        final JsonMetaSchema overrideValidatorMetaSchema = JsonMetaSchema
+                .builder(URI, JsonMetaSchema.getV201909())
+                .addFormat(new PatternFormat("email", "^[a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"))
+                .build();
+
+        final JsonSchemaFactory overrideValidatorFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).addMetaSchema(overrideValidatorMetaSchema).build();
+        final JsonSchema overrideValidatorSchema = overrideValidatorFactory.getSchema(schema);
+
+        messages = overrideValidatorSchema.validate(targetNode);
+        assertEquals(0, messages.size());
+
+
+    }
+}