Skip to content
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

Allow filtering bean properties and transforming JSON schema during generation process #52

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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.jsonFormatVisitors.JsonObjectFormatVisitor;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
import com.fasterxml.jackson.module.jsonSchema.customProperties.filter.BeanPropertyFilter;
import com.fasterxml.jackson.module.jsonSchema.customProperties.transformer.JsonSchemaTransformer;
import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitor;
import com.fasterxml.jackson.module.jsonSchema.factories.ObjectVisitorDecorator;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.types.ObjectSchema;

import java.util.Iterator;
import java.util.List;

/**
* This subtype of {@link com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper} allows
* you to filter out {@link com.fasterxml.jackson.databind.BeanProperty} from generating schema by applying
* to each property {@link com.fasterxml.jackson.module.jsonSchema.customProperties.filter.BeanPropertyFilter}.
*
* BeanProperty will be excluded if at least one filter excludes it.
*
* This wrapper also uses {@link com.fasterxml.jackson.module.jsonSchema.customProperties.transformer.JsonSchemaTransformer}
* transformers to apply some additional transformation of {@link com.fasterxml.jackson.module.jsonSchema.JsonSchema}
*
* @author wololock
*/
public class FilterChainSchemaFactoryWrapper extends SchemaFactoryWrapper {

private final List<BeanPropertyFilter> filters;

private final List<JsonSchemaTransformer> transformers;

public FilterChainSchemaFactoryWrapper(FilterChainSchemaFactoryWrapperFactory wrapperFactory) {
super(wrapperFactory);
this.filters = wrapperFactory.getFilters();
this.transformers = wrapperFactory.getTransformers();
}

@Override
public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) {
return new ObjectVisitorDecorator((ObjectVisitor) super.expectObjectFormat(convertedType)) {
@Override
public void optionalProperty(BeanProperty writer) throws JsonMappingException {
boolean allowed = applyFilters(writer);
if (allowed) {
super.optionalProperty(writer);
applyTransformations(writer);
}
}

@Override
public void property(BeanProperty writer) throws JsonMappingException {
boolean allowed = applyFilters(writer);
if (allowed) {
super.property(writer);
applyTransformations(writer);
}
}

private boolean applyFilters(BeanProperty writer) {
boolean allowed = true;
Iterator<BeanPropertyFilter> iterator = filters.iterator();
while (iterator.hasNext() && allowed) {
allowed = iterator.next().test(writer);
}
return allowed;
}

private void applyTransformations(BeanProperty beanProperty) {
if (!transformers.isEmpty()) {
JsonSchema jsonSchema = getPropertySchema(beanProperty);
for (JsonSchemaTransformer transformer : transformers) {
jsonSchema = transformer.transform(jsonSchema, beanProperty);
}
}
}

private JsonSchema getPropertySchema(BeanProperty beanProperty) {
return ((ObjectSchema) getSchema()).getProperties().get(beanProperty.getName());
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.fasterxml.jackson.module.jsonSchema.customProperties;

import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.module.jsonSchema.customProperties.filter.BeanPropertyFilter;
import com.fasterxml.jackson.module.jsonSchema.customProperties.transformer.JsonSchemaTransformer;
import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
import com.fasterxml.jackson.module.jsonSchema.factories.VisitorContext;
import com.fasterxml.jackson.module.jsonSchema.factories.WrapperFactory;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
* Creates {@link FilterChainSchemaFactoryWrapper} with
* injected list of {@link com.fasterxml.jackson.module.jsonSchema.customProperties.filter.BeanPropertyFilter} filters
* and additional list of {@link com.fasterxml.jackson.module.jsonSchema.customProperties.transformer.JsonSchemaTransformer}
* transformers.
*
* This class is thread-safe.
*
* @author wololock
*/
public class FilterChainSchemaFactoryWrapperFactory extends WrapperFactory {

/**
* Chain of filters
*
* Only properties that match all filters will be included in final
* JSON schema.
*/
private final List<BeanPropertyFilter> filters;

/**
* Additional transformations that have to be applied to filtered
* bean properties.
*/
private final List<JsonSchemaTransformer> transformers;

public FilterChainSchemaFactoryWrapperFactory(List<BeanPropertyFilter> filters, List<JsonSchemaTransformer> transformers) {
this.filters = Collections.unmodifiableList(filters != null ? filters : new LinkedList<BeanPropertyFilter>());
this.transformers = Collections.unmodifiableList(transformers != null ? transformers : new LinkedList<JsonSchemaTransformer>());
}

public List<BeanPropertyFilter> getFilters() {
return filters;
}

public List<JsonSchemaTransformer> getTransformers() {
return transformers;
}

@Override
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
SchemaFactoryWrapper wrapper = new FilterChainSchemaFactoryWrapper(this);
wrapper.setProvider(p);
return wrapper;
}

@Override
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
SchemaFactoryWrapper wrapper = new FilterChainSchemaFactoryWrapper(this);
wrapper.setProvider(p);
wrapper.setVisitorContext(rvc);
return wrapper;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.fasterxml.jackson.module.jsonSchema.customProperties.filter;

import com.fasterxml.jackson.databind.BeanProperty;

/**
* Checks if given {@link com.fasterxml.jackson.databind.BeanProperty}
* follows filtering rule.
*
* @author wololock
*/
public interface BeanPropertyFilter {
boolean test(BeanProperty property);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.fasterxml.jackson.module.jsonSchema.customProperties.filter;

import com.fasterxml.jackson.databind.BeanProperty;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
* Rejects {@link com.fasterxml.jackson.databind.BeanProperty} if it's
* annotated with at least one given annotation.
*
* @author wololock
*/
public class RuntimeAnnotatedBeanPropertyFilter implements BeanPropertyFilter {

private final List<Class<? extends Annotation>> rejectedAnnotations;

public RuntimeAnnotatedBeanPropertyFilter(List<Class<? extends Annotation>> rejectedAnnotations) {
this.rejectedAnnotations = Collections.unmodifiableList(rejectedAnnotations);
}

public RuntimeAnnotatedBeanPropertyFilter(Class<? extends Annotation> ...classes) {
this(Arrays.asList(classes));
}

@Override
public boolean test(BeanProperty property) {
boolean accept = true;
if (hasAnnotations(property)) {
Iterator<Class<? extends Annotation>> iterator = rejectedAnnotations.iterator();
while (accept && iterator.hasNext()) {
accept = !property.getMember().hasAnnotation(iterator.next());
}
}
return accept;
}

private boolean hasAnnotations(BeanProperty property) {
return property.getMember().annotations().iterator().hasNext();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.fasterxml.jackson.module.jsonSchema.customProperties.transformer;

import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.module.jsonSchema.JsonSchema;

/**
* JsonSchemaTransformer defines additional {@link com.fasterxml.jackson.module.jsonSchema.JsonSchema}
* transformation.
*
* @author wololock
*/
public interface JsonSchemaTransformer {
JsonSchema transform(JsonSchema jsonSchema, BeanProperty beanProperty);
}
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);
}

}
Loading