Skip to content

Commit b333f23

Browse files
committed
Merge pull request #32 from cponomaryov/master
Use Bean Validation (JSR 303) annotations for resolving JSON schema validation constraints
2 parents 5019c16 + 18adedc commit b333f23

File tree

6 files changed

+540
-0
lines changed

6 files changed

+540
-0
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene
5555
<artifactId>jackson-databind</artifactId>
5656
<version>${version.jackson.core}</version>
5757
</dependency>
58+
<dependency>
59+
<groupId>javax.validation</groupId>
60+
<artifactId>validation-api</artifactId>
61+
<version>1.1.0.Final</version>
62+
</dependency>
5863

5964
</dependencies>
6065
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.fasterxml.jackson.module.jsonSchema.customProperties;
2+
3+
import com.fasterxml.jackson.databind.BeanProperty;
4+
import com.fasterxml.jackson.databind.JavaType;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
8+
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
9+
import com.fasterxml.jackson.module.jsonSchema.factories.*;
10+
import com.fasterxml.jackson.module.jsonSchema.types.ArraySchema;
11+
import com.fasterxml.jackson.module.jsonSchema.types.NumberSchema;
12+
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;
13+
import com.fasterxml.jackson.module.jsonSchema.types.StringSchema;
14+
import com.fasterxml.jackson.module.jsonSchema.validation.AnnotationConstraintResolver;
15+
import com.fasterxml.jackson.module.jsonSchema.validation.ValidationConstraintResolver;
16+
17+
/**
18+
* @author cponomaryov
19+
*/
20+
public class ValidationSchemaFactoryWrapper extends SchemaFactoryWrapper {
21+
22+
private ValidationConstraintResolver constraintResolver;
23+
24+
private static class ValidationSchemaFactoryWrapperFactory extends WrapperFactory {
25+
@Override
26+
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
27+
SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper();
28+
wrapper.setProvider(p);
29+
return wrapper;
30+
}
31+
32+
@Override
33+
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
34+
SchemaFactoryWrapper wrapper = new ValidationSchemaFactoryWrapper();
35+
wrapper.setProvider(p);
36+
wrapper.setVisitorContext(rvc);
37+
return wrapper;
38+
}
39+
}
40+
41+
public ValidationSchemaFactoryWrapper() {
42+
this(new AnnotationConstraintResolver());
43+
}
44+
45+
public ValidationSchemaFactoryWrapper(ValidationConstraintResolver constraintResolver) {
46+
super(new ValidationSchemaFactoryWrapperFactory());
47+
this.constraintResolver = constraintResolver;
48+
}
49+
50+
@Override
51+
public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) {
52+
return new ObjectVisitorDecorator((ObjectVisitor) super.expectObjectFormat(convertedType)) {
53+
private JsonSchema getPropertySchema(BeanProperty writer) {
54+
return ((ObjectSchema) getSchema()).getProperties().get(writer.getName());
55+
}
56+
57+
@Override
58+
public void optionalProperty(BeanProperty writer) throws JsonMappingException {
59+
super.optionalProperty(writer);
60+
addValidationConstraints(getPropertySchema(writer), writer);
61+
}
62+
63+
@Override
64+
public void property(BeanProperty writer) throws JsonMappingException {
65+
super.property(writer);
66+
addValidationConstraints(getPropertySchema(writer), writer);
67+
}
68+
};
69+
}
70+
71+
private JsonSchema addValidationConstraints(JsonSchema schema, BeanProperty prop) {
72+
if (schema.isArraySchema()) {
73+
ArraySchema arraySchema = schema.asArraySchema();
74+
arraySchema.setMaxItems(constraintResolver.getArrayMaxItems(prop));
75+
arraySchema.setMinItems(constraintResolver.getArrayMinItems(prop));
76+
} else if (schema.isNumberSchema()) {
77+
NumberSchema numberSchema = schema.asNumberSchema();
78+
numberSchema.setMaximum(constraintResolver.getNumberMaximum(prop));
79+
numberSchema.setMinimum(constraintResolver.getNumberMinimum(prop));
80+
} else if (schema.isStringSchema()) {
81+
StringSchema stringSchema = schema.asStringSchema();
82+
stringSchema.setMaxLength(constraintResolver.getStringMaxLength(prop));
83+
stringSchema.setMinLength(constraintResolver.getStringMinLength(prop));
84+
}
85+
return schema;
86+
}
87+
88+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.fasterxml.jackson.module.jsonSchema.factories;
2+
3+
import com.fasterxml.jackson.databind.BeanProperty;
4+
import com.fasterxml.jackson.databind.JavaType;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
6+
import com.fasterxml.jackson.databind.SerializerProvider;
7+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
8+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
9+
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
10+
11+
/**
12+
* @author cponomaryov
13+
*/
14+
public class ObjectVisitorDecorator implements JsonObjectFormatVisitor, JsonSchemaProducer {
15+
16+
protected ObjectVisitor objectVisitor;
17+
18+
public ObjectVisitorDecorator(ObjectVisitor objectVisitor) {
19+
this.objectVisitor = objectVisitor;
20+
}
21+
22+
@Override
23+
public JsonSchema getSchema() {
24+
return objectVisitor.getSchema();
25+
}
26+
27+
@Override
28+
public SerializerProvider getProvider() {
29+
return objectVisitor.getProvider();
30+
}
31+
32+
@Override
33+
public void setProvider(SerializerProvider serializerProvider) {
34+
objectVisitor.setProvider(serializerProvider);
35+
}
36+
37+
@Override
38+
public void optionalProperty(BeanProperty writer) throws JsonMappingException {
39+
objectVisitor.optionalProperty(writer);
40+
}
41+
42+
@Override
43+
public void optionalProperty(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
44+
objectVisitor.optionalProperty(name, handler, propertyTypeHint);
45+
}
46+
47+
@Override
48+
public void property(BeanProperty writer) throws JsonMappingException {
49+
objectVisitor.property(writer);
50+
}
51+
52+
@Override
53+
public void property(String name, JsonFormatVisitable handler, JavaType propertyTypeHint) throws JsonMappingException {
54+
objectVisitor.property(name, handler, propertyTypeHint);
55+
}
56+
57+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package com.fasterxml.jackson.module.jsonSchema.validation;
2+
3+
import com.fasterxml.jackson.databind.BeanProperty;
4+
5+
import javax.validation.constraints.*;
6+
import java.math.BigDecimal;
7+
8+
/**
9+
* @author cponomaryov
10+
*/
11+
public class AnnotationConstraintResolver implements ValidationConstraintResolver {
12+
13+
private Size getSizeAnnotation(BeanProperty prop) {
14+
return prop.getAnnotation(Size.class);
15+
}
16+
17+
private Integer getMaxSize(BeanProperty prop) {
18+
Size sizeAnnotation = getSizeAnnotation(prop);
19+
return sizeAnnotation != null && sizeAnnotation.max() != Integer.MAX_VALUE ? sizeAnnotation.max() : null;
20+
}
21+
22+
private Integer getMinSize(BeanProperty prop) {
23+
Size sizeAnnotation = getSizeAnnotation(prop);
24+
return sizeAnnotation != null && sizeAnnotation.min() != 0 ? sizeAnnotation.min() : null;
25+
}
26+
27+
@Override
28+
public Integer getArrayMaxItems(BeanProperty prop) {
29+
return getMaxSize(prop);
30+
}
31+
32+
@Override
33+
public Integer getArrayMinItems(BeanProperty prop) {
34+
return getMinSize(prop);
35+
}
36+
37+
@Override
38+
public Double getNumberMaximum(BeanProperty prop) {
39+
Max maxAnnotation = prop.getAnnotation(Max.class);
40+
if (maxAnnotation != null) {
41+
return (double) maxAnnotation.value();
42+
}
43+
DecimalMax decimalMaxAnnotation = prop.getAnnotation(DecimalMax.class);
44+
return decimalMaxAnnotation != null ? new BigDecimal(decimalMaxAnnotation.value()).doubleValue() : null;
45+
}
46+
47+
@Override
48+
public Double getNumberMinimum(BeanProperty prop) {
49+
Min minAnnotation = prop.getAnnotation(Min.class);
50+
if (minAnnotation != null) {
51+
return (double) minAnnotation.value();
52+
}
53+
DecimalMin decimalMinAnnotation = prop.getAnnotation(DecimalMin.class);
54+
return decimalMinAnnotation != null ? new BigDecimal(decimalMinAnnotation.value()).doubleValue() : null;
55+
}
56+
57+
@Override
58+
public Integer getStringMaxLength(BeanProperty prop) {
59+
return getMaxSize(prop);
60+
}
61+
62+
@Override
63+
public Integer getStringMinLength(BeanProperty prop) {
64+
return getMinSize(prop);
65+
}
66+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.fasterxml.jackson.module.jsonSchema.validation;
2+
3+
import com.fasterxml.jackson.databind.BeanProperty;
4+
5+
/**
6+
* @author cponomaryov
7+
*/
8+
public interface ValidationConstraintResolver {
9+
10+
Integer getArrayMaxItems(BeanProperty prop);
11+
12+
Integer getArrayMinItems(BeanProperty prop);
13+
14+
Double getNumberMaximum(BeanProperty prop);
15+
16+
Double getNumberMinimum(BeanProperty prop);
17+
18+
Integer getStringMaxLength(BeanProperty prop);
19+
20+
Integer getStringMinLength(BeanProperty prop);
21+
22+
}

0 commit comments

Comments
 (0)