Repository: joelittlejohn/jsonschema2pojo
Pull request #1475 (2023-02-04 17:56:17)

Add Email bean validation annotation for "format":"email"
Things to consider if are using the includeJsr303Annotations feature:

* If you have useJakartaValidation turned on, you'll now see `jakarta.validation.constraints.Email` added to a property when using `"format":"email"`.
* If don't have useJakartaValidation turned on, you'll now see [`javax.validation.constraints.Email`](https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/Email.html) added to a property when using `"format":"email"`.
  * The Email annotation is introduced in the bean validation spec 2.0, so you will need to upgrade to javax.validation:validation-api 2.x (2.0.1.Final is a good choice). Note, all old annoations are maintained (with the same fully-qualified name) so upgrading this should not break anything.
  * If email validation is still not happening, your validator might only be doing bean validation 1.0 or 1.1 validations. You would then need to upgrade your validator (e.g. with bval, [upgrade to 2.0.5 or later](https://bval.apache.org/downloads.html)).

joelittlejohn: If you receive compilation errors because javax.validation.constraints.Email cannot be found, upgrade to javax.validation/validation-api 2.0.1.Final.
unkish: > **If you receive compilation errors because javax.validation.constraints.Email cannot be found, upgrade to javax.validation/validation-api 2.0.1.Final.**

Note that given annotation can't be used when `targetVersion` is less that `1.8` as it has:

- `@Retention(RUNTIME)`
- `@Target` contains `TYPE_USE` which is available since `1.8`

Those who target older version might need to make sure that version of `javax.validation/validation-api` being used is compatible with `targetVersion`

Patch:
diff --git a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java
index f2095b95..9bb0ecb7 100644
--- a/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java
+++ b/jsonschema2pojo-core/src/main/java/org/jsonschema2pojo/rules/PropertyRule.java
@@ -16,7 +16,6 @@
 
 package org.jsonschema2pojo.rules;
 
-import org.apache.commons.lang.StringUtils;
 import org.jsonschema2pojo.GenerationConfig;
 import org.jsonschema2pojo.JsonPointerUtils;
 import org.jsonschema2pojo.Schema;
@@ -31,6 +30,7 @@ import com.sun.codemodel.JMethod;
 import com.sun.codemodel.JMod;
 import com.sun.codemodel.JType;
 import com.sun.codemodel.JVar;
+import org.apache.commons.lang.StringUtils;
 
 /**
  * Applies the schema rules that represent a property definition.
@@ -201,6 +201,12 @@ public class PropertyRule implements Rule<JDefinedClass, JDefinedClass> {
             ruleFactory.getAnnotator().dateField(field, clazz, node);
         } else if ("time".equalsIgnoreCase(format)) {
             ruleFactory.getAnnotator().timeField(field, clazz, node);
+        } else if ("email".equalsIgnoreCase(format) && ruleFactory.getGenerationConfig().isIncludeJsr303Annotations()) {
+            if (ruleFactory.getGenerationConfig().isUseJakartaValidation()) {
+                field.annotate(jakarta.validation.constraints.Email.class);
+            } else {
+                field.annotate(javax.validation.constraints.Email.class);
+            }
         }
     }
 
diff --git a/pom.xml b/pom.xml
index 07a5a959..7dae16b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -307,7 +307,7 @@
             <dependency>
                 <groupId>javax.validation</groupId>
                 <artifactId>validation-api</artifactId>
-                <version>1.1.0.Final</version>
+                <version>2.0.1.Final</version>
             </dependency>
             <dependency>
                 <groupId>jakarta.validation</groupId>
@@ -356,6 +356,12 @@
                 <artifactId>ant</artifactId>
                 <version>1.10.13</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.bval</groupId>
+                <artifactId>bval-jsr</artifactId>
+                <version>2.0.6</version>
+                <scope>test</scope>
+            </dependency>
             <dependency>
                 <groupId>org.apache.maven</groupId>
                 <artifactId>maven-plugin-api</artifactId>
@@ -386,7 +392,7 @@
             <dependency>
                 <groupId>org.hibernate.validator</groupId>
                 <artifactId>hibernate-validator</artifactId>
-                <version>7.0.1.Final</version>
+                <version>7.0.5.Final</version>
                 <scope>test</scope>
             </dependency>
             <dependency>


Test patch:
diff --git a/jsonschema2pojo-integration-tests/pom.xml b/jsonschema2pojo-integration-tests/pom.xml
index 39fad03f..603b9ac0 100644
--- a/jsonschema2pojo-integration-tests/pom.xml
+++ b/jsonschema2pojo-integration-tests/pom.xml
@@ -154,9 +154,7 @@
         <!-- javax.validation implementation dependencies: start -->
         <dependency>
             <groupId>org.apache.bval</groupId>
-            <artifactId>bval-jsr303</artifactId>
-            <version>0.5</version>
-            <scope>test</scope>
+            <artifactId>bval-jsr</artifactId>
         </dependency>
         <dependency>
             <groupId>javax.validation</groupId>
diff --git a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
index 49fe40f1..bce32ce5 100644
--- a/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
+++ b/jsonschema2pojo-integration-tests/src/test/java/org/jsonschema2pojo/integration/config/IncludeJsr303AnnotationsIT.java
@@ -21,6 +21,11 @@ import static org.hamcrest.Matchers.*;
 import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.*;
 import static org.junit.Assert.*;
 
+import org.jsonschema2pojo.integration.util.FileSearchMatcher;
+import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule;
+
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
 import java.beans.PropertyDescriptor;
 import java.io.File;
 import java.lang.reflect.InvocationTargetException;
@@ -32,19 +37,13 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
-
-import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr.ApacheValidationProvider;
 import org.hamcrest.Matcher;
-import org.jsonschema2pojo.integration.util.FileSearchMatcher;
-import org.jsonschema2pojo.integration.util.Jsonschema2PojoRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
-import jakarta.validation.Validation;
-import jakarta.validation.Validator;
-
 @SuppressWarnings("rawtypes")
 @RunWith(Parameterized.class)
 public class IncludeJsr303AnnotationsIT {
@@ -182,6 +181,23 @@ public class IncludeJsr303AnnotationsIT {
 
     }
 
+    @Test
+    public void jsr303EmailValidationIsAddedForFormatEmailSchemaRule() throws ClassNotFoundException {
+
+        ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/jsr303/email.json", "com.example",
+                config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation));
+
+        Class generatedType = resultsClassLoader.loadClass("com.example.Email");
+
+        Object validInstance = createInstanceWithPropertyValue(generatedType, "email", "user@example.com");
+
+        assertNumberOfConstraintViolationsOn(validInstance, is(0));
+
+        Object invalidInstance = createInstanceWithPropertyValue(generatedType, "email", "aaa");
+
+        assertNumberOfConstraintViolationsOn(invalidInstance, is(1));
+    }
+
     @Test
     public void jsr303PatternValidationIsAddedForSchemaRulePattern() throws ClassNotFoundException {
 
@@ -361,7 +377,7 @@ public class IncludeJsr303AnnotationsIT {
 
     @SuppressWarnings("unchecked")
     @Test
-    public void jar303AnnotationsValidatedForAdditionalProperties() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
+    public void jsr303AnnotationsValidatedForAdditionalProperties() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         ClassLoader resultsClassLoader = schemaRule.generateAndCompile("/schema/jsr303/validAdditionalProperties.json", "com.example",
                 config("includeJsr303Annotations", true, "useJakartaValidation", useJakartaValidation));
 
@@ -383,7 +399,8 @@ public class IncludeJsr303AnnotationsIT {
 
     private void assertNumberOfConstraintViolationsOn(Object instance, Matcher<Integer> matcher) {
         final Set<?> violationsForValidInstance = useJakartaValidation ? validator.validate(instance) : javaxValidator.validate(instance);
-        assertThat(violationsForValidInstance.size(), matcher);
+        final String validatorName = useJakartaValidation ? "jakarta/hibernate validator" : "javax/bval validator";
+        assertThat("Violations (" + validatorName + "): " + violationsForValidInstance.toString(), violationsForValidInstance.size(), matcher);
     }
 
     private static Object createInstanceWithPropertyValue(Class type, String propertyName, Object propertyValue) {
diff --git a/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/email.json b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/email.json
new file mode 100644
index 00000000..b369d628
--- /dev/null
+++ b/jsonschema2pojo-integration-tests/src/test/resources/schema/jsr303/email.json
@@ -0,0 +1,9 @@
+{
+  "type" : "object",
+  "properties" : {
+    "email" : {
+      "type" : "string",
+      "format": "email"
+    }
+  }
+}