Skip to content

Use Bean Validation (JSR 303) annotations for resolving JSON schema validation constraints #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 19, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ JSON Schema (http://tools.ietf.org/html/draft-zyp-json-schema-03) version 3 gene
<artifactId>jackson-databind</artifactId>
<version>${version.jackson.core}</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>

</dependencies>
<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);

}
Loading