Skip to content

Instantly share code, notes, and snippets.

@silas
Last active January 10, 2019 13:27
Show Gist options
  • Save silas/3b41e6ed4031c11f919767a74829ffea to your computer and use it in GitHub Desktop.
Save silas/3b41e6ed4031c11f919767a74829ffea to your computer and use it in GitHub Desktop.
Swagger snake case
import io.swagger.converter.ModelConverters;

ModelConverters.getInstance().addConverter(new SnakeCaseConverter());

BeanConfig config = new BeanConfig();
config.setTitle("Swagger sample app");
config.setVersion("1.0.0");
config.setResourcePackage("io.swagger.sample.resource");
config.setScan(true);
package example;
import io.swagger.converter.ModelConverter;
import io.swagger.converter.ModelConverterContext;
import io.swagger.models.Model;
import io.swagger.models.properties.Property;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
// based on code taken from SnakeCaseConverterTest in swagger-core
public class SnakeCaseConverter implements ModelConverter {
@Override
public Property resolveProperty(Type type, ModelConverterContext context, Annotation[] annotations, Iterator<ModelConverter> chain) {
if (chain.hasNext()) {
final ModelConverter converter = chain.next();
return converter.resolveProperty(type, context, annotations, chain);
}
return null;
}
@Override
public Model resolve(Type type, ModelConverterContext context, Iterator<ModelConverter> chain) {
if (chain.hasNext()) {
final ModelConverter converter = chain.next();
final Model model = converter.resolve(type, context, chain);
if (model != null) {
final Map<String, Property> properties = model.getProperties();
final Map<String, Property> newProperties = new LinkedHashMap<>();
for (String key : properties.keySet()) {
newProperties.put(toSnakeCase(key), properties.get(key));
}
model.getProperties().clear();
model.setProperties(newProperties);
return model;
}
}
return null;
}
// taken from jackson-databind PropertyNamingStrategy
private static String toSnakeCase(String input) {
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++) {
char c = input.charAt(i);
if (i > 0 || c != '_') // skip first starting underscore
{
if (Character.isUpperCase(c)) {
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_') {
result.append('_');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
} else {
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
}
@bric3
Copy link

bric3 commented Oct 13, 2017

Here's a java 8 improved version, using Jackson PropertyNamingStrategy directly

public class JacksonBasedPropertyConverter implements ModelConverter {

    private PropertyNamingStrategyBase propertyNamingStrategy;

    public JacksonBasedPropertyConverter(PropertyNamingStrategyBase propertyNamingStrategy) {
        this.propertyNamingStrategy = propertyNamingStrategy;
    }

    @Override
    public Property resolveProperty(Type type, ModelConverterContext context, Annotation[] annotations, Iterator<ModelConverter> chain) {
        if (!chain.hasNext()) {
            return null;
        }
        return chain.next().resolveProperty(type, context, annotations, chain);
    }

    @Override
    public Model resolve(Type type, ModelConverterContext context, Iterator<ModelConverter> chain) {
        if (!chain.hasNext()) {
            return null;
        }

        Model model = chain.next().resolve(type, context, chain);
        if (model == null) {
            return null;
        }

        Map<String, Property> newProperties = model.getProperties()
                                                   .entrySet()
                                                   .stream()
                                                   .collect(toMap(entry -> toSnakeCase(entry.getKey()),
                                                                  Map.Entry::getValue,
                                                                  (prop1, prop2) -> prop1));
        model.getProperties().clear(); // needed because setProperties actually merges the map
        model.setProperties(newProperties);
        return model;
    }

    private String translate(String input) {
        return propertyNamingStrategy.translate(input);
    }

    public static JacksonBasedPropertyConverter from(PropertyNamingStrategy strategy) {
        if(strategy instanceof PropertyNamingStrategyBase) {
            return new JacksonBasedPropertyConverter((PropertyNamingStrategyBase) strategy);
        }
        throw new IllegalArgumentException(strategy.getClass() + " cannot be cast as " + PropertyNamingStrategyBase.class);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment