diff --git a/pom.xml b/pom.xml
index 5e40398c..19a5347f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,11 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene
jackson-databind
${version.jackson.core}
+
+ javax.validation
+ validation-api
+ 1.1.0.Final
+
diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java
new file mode 100644
index 00000000..cc756f2c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/customProperties/ValidationSchemaFactoryWrapper.java
@@ -0,0 +1,88 @@
+package com.fasterxml.jackson.module.jsonSchema.customProperties;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
+import com.fasterxml.jackson.module.jsonSchema.factories.*;
+import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
+import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.StringSchema;
+import com.fasterxml.jackson.module.jsonSchema.validation.AnnotationConstraintResolver;
+import com.fasterxml.jackson.module.jsonSchema.validation.ValidationConstraintResolver;
+
+/**
+ * @author cponomaryov
+ */
+public class ValidationSchemaFactoryWrapper extends SchemaFactoryWrapper {
+
+ private ValidationConstraintResolver constraintResolver;
+
+ private static class ValidationSchemaFactoryWrapperFactory extends WrapperFactory {
+ @Override
+ public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
+ SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper();
+ wrapper.setProvider(p);
+ return wrapper;
+ }
+
+ @Override
+ public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
+ SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper();
+ wrapper.setProvider(p);
+ wrapper.setVisitorContext(rvc);
+ return wrapper;
+ }
+ }
+
+ public ValidationSchemaFactoryWrapper() {
+ this(new AnnotationConstraintResolver());
+ }
+
+ public ValidationSchemaFactoryWrapper(ValidationConstraintResolver constraintResolver) {
+ super(new ValidationSchemaFactoryWrapperFactory());
+ this.constraintResolver = constraintResolver;
+ }
+
+ @Override
+ public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) {
+ return new ObjectVisitorDecorator((ObjectVisitor) super.expectObjectFormat(convertedType)) {
+ private JsonSchema getPropertySchema(BeanProperty writer) {
+ return ((ObjectSchema) getSchema()).getProperties().get(writer.getName());
+ }
+
+ @Override
+ public void optionalProperty(BeanProperty writer) throws JsonMappingException {
+ super.optionalProperty(writer);
+ addValidationConstraints(getPropertySchema(writer), writer);
+ }
+
+ @Override
+ public void property(BeanProperty writer) throws JsonMappingException {
+ super.property(writer);
+ addValidationConstraints(getPropertySchema(writer), writer);
+ }
+ };
+ }
+
+ private JsonSchema addValidationConstraints(JsonSchema schema, BeanProperty prop) {
+ if (schema.isArraySchema()) {
+ ArraySchema arraySchema = schema.asArraySchema();
+ arraySchema.setMaxItems(constraintResolver.getArrayMaxItems(prop));
+ arraySchema.setMinItems(constraintResolver.getArrayMinItems(prop));
+ } else if (schema.isNumberSchema()) {
+ NumberSchema numberSchema = schema.asNumberSchema();
+ numberSchema.setMaximum(constraintResolver.getNumberMaximum(prop));
+ numberSchema.setMinimum(constraintResolver.getNumberMinimum(prop));
+ } else if (schema.isStringSchema()) {
+ StringSchema stringSchema = schema.asStringSchema();
+ stringSchema.setMaxLength(constraintResolver.getStringMaxLength(prop));
+ stringSchema.setMinLength(constraintResolver.getStringMinLength(prop));
+ }
+ return schema;
+ }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitorDecorator.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitorDecorator.java
new file mode 100644
index 00000000..bed7c12b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/factories/ObjectVisitorDecorator.java
@@ -0,0 +1,57 @@
+package com.fasterxml.jackson.module.jsonSchema.factories;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
+import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
+import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
+
+/**
+ * @author cponomaryov
+ */
+public class ObjectVisitorDecorator implements JsonObjectFormatVisitor, JsonSchemaProducer {
+
+ protected ObjectVisitor objectVisitor;
+
+ public ObjectVisitorDecorator(ObjectVisitor objectVisitor) {
+ this.objectVisitor = objectVisitor;
+ }
+
+ @Override
+ public JsonSchema getSchema() {
+ return objectVisitor.getSchema();
+ }
+
+ @Override
+ public SerializerProvider getProvider() {
+ return objectVisitor.getProvider();
+ }
+
+ @Override
+ public void setProvider(SerializerProvider serializerProvider) {
+ objectVisitor.setProvider(serializerProvider);
+ }
+
+ @Override
+ public void optionalProperty(BeanProperty writer) throws JsonMappingException {
+ objectVisitor.optionalProperty(writer);
+ }
+
+ @Override
+ public void optionalProperty(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
+ objectVisitor.optionalProperty(name, handler, propertyTypeHint);
+ }
+
+ @Override
+ public void property(BeanProperty writer) throws JsonMappingException {
+ objectVisitor.property(writer);
+ }
+
+ @Override
+ public void property(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
+ objectVisitor.property(name, handler, propertyTypeHint);
+ }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java
new file mode 100644
index 00000000..a41df48e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/AnnotationConstraintResolver.java
@@ -0,0 +1,66 @@
+package com.fasterxml.jackson.module.jsonSchema.validation;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+
+/**
+ * @author cponomaryov
+ */
+public class AnnotationConstraintResolver implements ValidationConstraintResolver {
+
+ private Size getSizeAnnotation(BeanProperty prop) {
+ return prop.getAnnotation(Size.class);
+ }
+
+ private Integer getMaxSize(BeanProperty prop) {
+ Size sizeAnnotation = getSizeAnnotation(prop);
+ return sizeAnnotation != null && sizeAnnotation.max() != Integer.MAX_VALUE ? sizeAnnotation.max() : null;
+ }
+
+ private Integer getMinSize(BeanProperty prop) {
+ Size sizeAnnotation = getSizeAnnotation(prop);
+ return sizeAnnotation != null && sizeAnnotation.min() != 0 ? sizeAnnotation.min() : null;
+ }
+
+ @Override
+ public Integer getArrayMaxItems(BeanProperty prop) {
+ return getMaxSize(prop);
+ }
+
+ @Override
+ public Integer getArrayMinItems(BeanProperty prop) {
+ return getMinSize(prop);
+ }
+
+ @Override
+ public Double getNumberMaximum(BeanProperty prop) {
+ Max maxAnnotation = prop.getAnnotation(Max.class);
+ if (maxAnnotation != null) {
+ return (double) maxAnnotation.value();
+ }
+ DecimalMax decimalMaxAnnotation = prop.getAnnotation(DecimalMax.class);
+ return decimalMaxAnnotation != null ? new BigDecimal(decimalMaxAnnotation.value()).doubleValue() : null;
+ }
+
+ @Override
+ public Double getNumberMinimum(BeanProperty prop) {
+ Min minAnnotation = prop.getAnnotation(Min.class);
+ if (minAnnotation != null) {
+ return (double) minAnnotation.value();
+ }
+ DecimalMin decimalMinAnnotation = prop.getAnnotation(DecimalMin.class);
+ return decimalMinAnnotation != null ? new BigDecimal(decimalMinAnnotation.value()).doubleValue() : null;
+ }
+
+ @Override
+ public Integer getStringMaxLength(BeanProperty prop) {
+ return getMaxSize(prop);
+ }
+
+ @Override
+ public Integer getStringMinLength(BeanProperty prop) {
+ return getMinSize(prop);
+ }
+}
diff --git a/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java
new file mode 100644
index 00000000..0ada0e7d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/module/jsonSchema/validation/ValidationConstraintResolver.java
@@ -0,0 +1,22 @@
+package com.fasterxml.jackson.module.jsonSchema.validation;
+
+import com.fasterxml.jackson.databind.BeanProperty;
+
+/**
+ * @author cponomaryov
+ */
+public interface ValidationConstraintResolver {
+
+ Integer getArrayMaxItems(BeanProperty prop);
+
+ Integer getArrayMinItems(BeanProperty prop);
+
+ Double getNumberMaximum(BeanProperty prop);
+
+ Double getNumberMinimum(BeanProperty prop);
+
+ Integer getStringMaxLength(BeanProperty prop);
+
+ Integer getStringMinLength(BeanProperty prop);
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java b/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java
new file mode 100644
index 00000000..fc9fcb34
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/module/jsonSchema/ValidationSchemaFactoryWrapperTest.java
@@ -0,0 +1,302 @@
+package com.fasterxml.jackson.module.jsonSchema;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.module.jsonSchema.customProperties.ValidationSchemaFactoryWrapper;
+import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
+import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema;
+import com.fasterxml.jackson.module.jsonSchema.types.StringSchema;
+
+import javax.validation.constraints.*;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author cponomaryov
+ */
+public class ValidationSchemaFactoryWrapperTest extends SchemaTestBase {
+
+ public static class ValidationBean {
+
+ /*
+ /**********************************************************
+ /* Array fields
+ /**********************************************************
+ */
+
+ private List listWithoutConstraints;
+
+ @Size(min = 1)
+ private List listWithMinSize;
+
+ @Size(max = 2)
+ private List listWithMaxSize;
+
+ @Size(min = 3, max = 4)
+ private List listWithMinAndMaxSize;
+
+ /*
+ /**********************************************************
+ /* Number fields
+ /**********************************************************
+ */
+
+ private int numberWithoutConstraints;
+
+ @Min(5)
+ private int numberWithMin;
+
+ @DecimalMin("5.5")
+ private int numberWithDecimalMin;
+
+ @Max(6)
+ private int numberWithMax;
+
+ @DecimalMax("6.5")
+ private int numberWithDecimalMax;
+
+ @Min(7)
+ @Max(8)
+ private int numberWithMinAndMax;
+
+ @Min(9)
+ @DecimalMax("9.5")
+ private int numberWithMinAndDecimalMax;
+
+ @DecimalMin("10.5")
+ @Max(11)
+ private int numberWithDecimalMinAndMax;
+
+ @DecimalMin("11.5")
+ @DecimalMax("12.5")
+ private int numberWithDecimalMinAndDecimalMax;
+
+ /*
+ /**********************************************************
+ /* String fields
+ /**********************************************************
+ */
+
+ private String stringWithoutConstraints;
+
+ @Size(min = 13)
+ private String stringWithMinSize;
+
+ @Size(max = 14)
+ private String stringWithMaxSize;
+
+ @Size(min = 15, max = 16)
+ private String stringWithMinAndMaxSize;
+
+ public List getListWithoutConstraints() {
+ return listWithoutConstraints;
+ }
+
+ public void setListWithoutConstraints(List listWithoutConstraints) {
+ this.listWithoutConstraints = listWithoutConstraints;
+ }
+
+ public List getListWithMinSize() {
+ return listWithMinSize;
+ }
+
+ public void setListWithMinSize(List listWithMinSize) {
+ this.listWithMinSize = listWithMinSize;
+ }
+
+ public List getListWithMaxSize() {
+ return listWithMaxSize;
+ }
+
+ public void setListWithMaxSize(List listWithMaxSize) {
+ this.listWithMaxSize = listWithMaxSize;
+ }
+
+ public List getListWithMinAndMaxSize() {
+ return listWithMinAndMaxSize;
+ }
+
+ public void setListWithMinAndMaxSize(List listWithMinAndMaxSize) {
+ this.listWithMinAndMaxSize = listWithMinAndMaxSize;
+ }
+
+ public int getNumberWithoutConstraints() {
+ return numberWithoutConstraints;
+ }
+
+ public void setNumberWithoutConstraints(int numberWithoutConstraints) {
+ this.numberWithoutConstraints = numberWithoutConstraints;
+ }
+
+ public int getNumberWithMin() {
+ return numberWithMin;
+ }
+
+ public void setNumberWithMin(int numberWithMin) {
+ this.numberWithMin = numberWithMin;
+ }
+
+ public int getNumberWithDecimalMin() {
+ return numberWithDecimalMin;
+ }
+
+ public void setNumberWithDecimalMin(int numberWithDecimalMin) {
+ this.numberWithDecimalMin = numberWithDecimalMin;
+ }
+
+ public int getNumberWithMax() {
+ return numberWithMax;
+ }
+
+ public void setNumberWithMax(int numberWithMax) {
+ this.numberWithMax = numberWithMax;
+ }
+
+ public int getNumberWithDecimalMax() {
+ return numberWithDecimalMax;
+ }
+
+ public void setNumberWithDecimalMax(int numberWithDecimalMax) {
+ this.numberWithDecimalMax = numberWithDecimalMax;
+ }
+
+ public int getNumberWithMinAndMax() {
+ return numberWithMinAndMax;
+ }
+
+ public void setNumberWithMinAndMax(int numberWithMinAndMax) {
+ this.numberWithMinAndMax = numberWithMinAndMax;
+ }
+
+ public int getNumberWithMinAndDecimalMax() {
+ return numberWithMinAndDecimalMax;
+ }
+
+ public void setNumberWithMinAndDecimalMax(int numberWithMinAndDecimalMax) {
+ this.numberWithMinAndDecimalMax = numberWithMinAndDecimalMax;
+ }
+
+ public int getNumberWithDecimalMinAndMax() {
+ return numberWithDecimalMinAndMax;
+ }
+
+ public void setNumberWithDecimalMinAndMax(int numberWithDecimalMinAndMax) {
+ this.numberWithDecimalMinAndMax = numberWithDecimalMinAndMax;
+ }
+
+ public int getNumberWithDecimalMinAndDecimalMax() {
+ return numberWithDecimalMinAndDecimalMax;
+ }
+
+ public void setNumberWithDecimalMinAndDecimalMax(int numberWithDecimalMinAndDecimalMax) {
+ this.numberWithDecimalMinAndDecimalMax = numberWithDecimalMinAndDecimalMax;
+ }
+
+ public String getStringWithoutConstraints() {
+ return stringWithoutConstraints;
+ }
+
+ public void setStringWithoutConstraints(String stringWithoutConstraints) {
+ this.stringWithoutConstraints = stringWithoutConstraints;
+ }
+
+ public String getStringWithMinSize() {
+ return stringWithMinSize;
+ }
+
+ public void setStringWithMinSize(String stringWithMinSize) {
+ this.stringWithMinSize = stringWithMinSize;
+ }
+
+ public String getStringWithMaxSize() {
+ return stringWithMaxSize;
+ }
+
+ public void setStringWithMaxSize(String stringWithMaxSize) {
+ this.stringWithMaxSize = stringWithMaxSize;
+ }
+
+ public String getStringWithMinAndMaxSize() {
+ return stringWithMinAndMaxSize;
+ }
+
+ public void setStringWithMinAndMaxSize(String stringWithMinAndMaxSize) {
+ this.stringWithMinAndMaxSize = stringWithMinAndMaxSize;
+ }
+ }
+
+ /*
+ /**********************************************************
+ /* Unit tests, success
+ /**********************************************************
+ */
+
+ private final ObjectMapper MAPPER = new ObjectMapper();
+
+ private Object[][] listTestData() {
+ return new Object[][] {{"listWithoutConstraints", null, null},
+ {"listWithMinSize", 1, null},
+ {"listWithMaxSize", null, 2},
+ {"listWithMinAndMaxSize", 3, 4}};
+ }
+
+ private Object[][] numberTestData() {
+ return new Object[][] {{"numberWithoutConstraints", null, null},
+ {"numberWithMin", 5d, null},
+ {"numberWithDecimalMin", 5.5, null},
+ {"numberWithMax", null, 6d},
+ {"numberWithDecimalMax", null, 6.5},
+ {"numberWithMinAndMax", 7d, 8d},
+ {"numberWithMinAndDecimalMax", 9d, 9.5},
+ {"numberWithDecimalMinAndMax", 10.5, 11d},
+ {"numberWithDecimalMinAndDecimalMax", 11.5, 12.5}};
+ }
+
+ private Object[][] stringTestData() {
+ return new Object[][] {{"stringWithoutConstraints", null, null},
+ {"stringWithMinSize", 13, null},
+ {"stringWithMaxSize", null, 14},
+ {"stringWithMinAndMaxSize", 15, 16}};
+ }
+
+ /**
+ * Test set validation constraints
+ */
+ @SuppressWarnings("SuspiciousMethodCalls")
+ public void testAddingValidationConstraints() throws Exception {
+ ValidationSchemaFactoryWrapper visitor = new ValidationSchemaFactoryWrapper();
+ ObjectMapper mapper = new ObjectMapper();
+
+ mapper.acceptJsonFormatVisitor(ValidationBean.class, visitor);
+ JsonSchema jsonSchema = visitor.finalSchema();
+
+ assertNotNull("schema should not be null.", jsonSchema);
+ assertTrue("schema should be an objectSchema.", jsonSchema.isObjectSchema());
+ Map properties = jsonSchema.asObjectSchema().getProperties();
+ assertNotNull(properties);
+ for (Object[] testCase : listTestData()) {
+ JsonSchema propertySchema = properties.get(testCase[0]);
+ assertNotNull(propertySchema);
+ assertTrue(propertySchema.isArraySchema());
+ ArraySchema arraySchema = propertySchema.asArraySchema();
+ assertEquals(testCase[1], arraySchema.getMinItems());
+ assertEquals(testCase[2], arraySchema.getMaxItems());
+ }
+ for (Object[] testCase : numberTestData()) {
+ JsonSchema propertySchema = properties.get(testCase[0]);
+ assertNotNull(propertySchema);
+ assertTrue(propertySchema.isNumberSchema());
+ NumberSchema numberSchema = propertySchema.asNumberSchema();
+ assertEquals(testCase[1], numberSchema.getMinimum());
+ assertEquals(testCase[2], numberSchema.getMaximum());
+ }
+ for (Object[] testCase : stringTestData()) {
+ JsonSchema propertySchema = properties.get(testCase[0]);
+ assertNotNull(propertySchema);
+ assertTrue(propertySchema.isStringSchema());
+ StringSchema stringSchema = propertySchema.asStringSchema();
+ assertEquals(testCase[1], stringSchema.getMinLength());
+ assertEquals(testCase[2], stringSchema.getMaxLength());
+ }
+ }
+
+}