diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java index c111a3af2fc..a74e652bb6d 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConfig.java @@ -109,6 +109,10 @@ public interface CodegenConfig { Map postProcessSupportingFileData(Map objs); + void postProcessModelProperty(CodegenModel model, CodegenProperty property); + + void postProcessParameter(CodegenParameter parameter); + String apiFilename(String templateName, String tag); boolean shouldOverwrite(String filename); diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java index 9c2711f8cad..06d7e801176 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java @@ -34,6 +34,9 @@ public class CodegenConstants { public static final String SERIALIZABLE_MODEL = "serializableModel"; public static final String SERIALIZABLE_MODEL_DESC = "boolean - toggle \"implements Serializable\" for generated models"; + public static final String SERIALIZE_BIG_DECIMAL_AS_STRING = "bigDecimalAsString"; + public static final String SERIALIZE_BIG_DECIMAL_AS_STRING_DESC = "boolean - treat BigDecimal values as Strings to avoid precision loss. Default: false"; + public static final String LIBRARY = "library"; public static final String LIBRARY_DESC = "library template (sub-template)"; diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java index 415fd729de9..c8362d1b19d 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultCodegen.java @@ -120,6 +120,12 @@ public class DefaultCodegen { return objs; } + // override to post-process any model properties + public void postProcessModelProperty(CodegenModel model, CodegenProperty property){} + + // override to post-process any parameters + public void postProcessParameter(CodegenParameter parameter){} + //override with any special handling of the entire swagger spec public void preprocessSwagger(Swagger swagger) { } @@ -620,7 +626,9 @@ public class DefaultCodegen { **/ public String getSwaggerType(Property p) { String datatype = null; - if (p instanceof StringProperty) { + if (p instanceof StringProperty && "number".equals(p.getFormat())) { + datatype = "BigDecimal"; + } else if (p instanceof StringProperty) { datatype = "string"; } else if (p instanceof ByteArrayProperty) { datatype = "ByteArray"; @@ -843,6 +851,12 @@ public class DefaultCodegen { } addVars(m, impl.getProperties(), impl.getRequired()); } + + if(m.vars != null) { + for(CodegenProperty prop : m.vars) { + postProcessModelProperty(m, prop); + } + } return m; } @@ -1595,6 +1609,8 @@ public class DefaultCodegen { } p.paramName = toParamName(bp.getName()); } + + postProcessParameter(p); return p; } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java index 08a28b81bf1..f23b719d679 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/JavaClientCodegen.java @@ -1,45 +1,21 @@ package io.swagger.codegen.languages; import com.google.common.base.Strings; -import io.swagger.codegen.CliOption; -import io.swagger.codegen.CodegenConfig; -import io.swagger.codegen.CodegenConstants; -import io.swagger.codegen.CodegenModel; -import io.swagger.codegen.CodegenOperation; -import io.swagger.codegen.CodegenProperty; -import io.swagger.codegen.CodegenType; -import io.swagger.codegen.DefaultCodegen; -import io.swagger.codegen.SupportingFile; +import io.swagger.codegen.*; import io.swagger.models.Model; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.Swagger; -import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.FormParameter; import io.swagger.models.parameters.Parameter; -import io.swagger.models.properties.ArrayProperty; -import io.swagger.models.properties.BooleanProperty; -import io.swagger.models.properties.DoubleProperty; -import io.swagger.models.properties.FloatProperty; -import io.swagger.models.properties.IntegerProperty; -import io.swagger.models.properties.LongProperty; -import io.swagger.models.properties.MapProperty; -import io.swagger.models.properties.Property; -import io.swagger.models.properties.StringProperty; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - +import io.swagger.models.properties.*; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.*; + public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { private static final Logger LOGGER = LoggerFactory.getLogger(JavaClientCodegen.class); public static final String FULL_JAVA_UTIL = "fullJavaUtil"; @@ -55,6 +31,7 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { protected boolean fullJavaUtil = false; protected String javaUtilPrefix = ""; protected Boolean serializableModel = false; + protected boolean serializeBigDecimalAsString = false; public JavaClientCodegen() { super(); @@ -101,6 +78,8 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { cliOptions.add(new CliOption(CodegenConstants.LOCAL_VARIABLE_PREFIX, CodegenConstants.LOCAL_VARIABLE_PREFIX_DESC)); cliOptions.add(new CliOption(CodegenConstants.SERIALIZABLE_MODEL, CodegenConstants.SERIALIZABLE_MODEL_DESC, BooleanProperty.TYPE)); + cliOptions.add(new CliOption(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING, CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING_DESC, + BooleanProperty.TYPE)); cliOptions.add(new CliOption(FULL_JAVA_UTIL, "whether to use fully qualified name for classes under java.util", BooleanProperty.TYPE).defaultValue(Boolean.FALSE.toString())); @@ -181,6 +160,10 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { this.setLibrary((String) additionalProperties.get(CodegenConstants.LIBRARY)); } + if(additionalProperties.containsKey(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING)) { + this.setSerializeBigDecimalAsString(Boolean.valueOf(additionalProperties.get(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING).toString())); + } + // need to put back serializableModel (boolean) into additionalProperties as value in additionalProperties is string additionalProperties.put(CodegenConstants.SERIALIZABLE_MODEL, serializableModel); @@ -212,6 +195,11 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { this.sanitizeConfig(); + + // optional jackson mappings for BigDecimal support + importMapping.put("ToStringSerializer", "com.fasterxml.jackson.databind.ser.std.ToStringSerializer"); + importMapping.put("JsonSerialize", "com.fasterxml.jackson.databind.annotation.JsonSerialize"); + final String invokerFolder = (sourceFolder + '/' + invokerPackage).replace(".", "/"); supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml")); supportingFiles.add(new SupportingFile("README.mustache", "", "README.md")); @@ -471,6 +459,26 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { return codegenModel; } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + if(serializeBigDecimalAsString) { + if (property.baseType.equals("BigDecimal")) { + // we serialize BigDecimal as `string` to avoid precision loss + property.vendorExtensions.put("extraAnnotation", "@JsonSerialize(using = ToStringSerializer.class)"); + + // this requires some more imports to be added for this model... + model.imports.add("ToStringSerializer"); + model.imports.add("JsonSerialize"); + } + } + return; + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + return; + } + @Override public Map postProcessModels(Map objs) { List models = (List) objs.get("models"); @@ -702,6 +710,9 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig { this.localVariablePrefix = localVariablePrefix; } + public void setSerializeBigDecimalAsString(boolean s) { + this.serializeBigDecimalAsString = s; + } public Boolean getSerializableModel() { return serializableModel; diff --git a/modules/swagger-codegen/src/main/resources/Java/pojo.mustache b/modules/swagger-codegen/src/main/resources/Java/pojo.mustache index 8d92141fa7f..6d65b5a888d 100644 --- a/modules/swagger-codegen/src/main/resources/Java/pojo.mustache +++ b/modules/swagger-codegen/src/main/resources/Java/pojo.mustache @@ -14,6 +14,7 @@ public class {{classname}} {{#parent}}extends {{{parent}}}{{/parent}} {{#seriali * minimum: {{minimum}}{{/minimum}}{{#maximum}} * maximum: {{maximum}}{{/maximum}} **/ + {{#vendorExtensions.extraAnnotation}}{{vendorExtensions.extraAnnotation}}{{/vendorExtensions.extraAnnotation}} @ApiModelProperty({{#required}}required = {{required}}, {{/required}}value = "{{{description}}}") @JsonProperty("{{baseName}}") public {{{datatypeWithEnum}}} {{getter}}() { diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/inflector/JavaInflectorServerOptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/inflector/JavaInflectorServerOptionsTest.java index d7046dda9b2..8c54a382993 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/inflector/JavaInflectorServerOptionsTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/inflector/JavaInflectorServerOptionsTest.java @@ -4,8 +4,6 @@ import io.swagger.codegen.CodegenConfig; import io.swagger.codegen.java.JavaClientOptionsTest; import io.swagger.codegen.languages.JavaInflectorServerCodegen; import io.swagger.codegen.options.JavaInflectorServerOptionsProvider; -import io.swagger.codegen.options.JavaOptionsProvider; - import mockit.Expectations; import mockit.Tested; @@ -50,6 +48,8 @@ public class JavaInflectorServerOptionsTest extends JavaClientOptionsTest { times = 1; clientCodegen.setFullJavaUtil(Boolean.valueOf(JavaInflectorServerOptionsProvider.FULL_JAVA_UTIL_VALUE)); times = 1; + clientCodegen.setSerializeBigDecimalAsString(true); + times = 1; }}; } } diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaOptionsProvider.java index 8ea7ee31452..7e5fd36793a 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaOptionsProvider.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/JavaOptionsProvider.java @@ -38,6 +38,7 @@ public class JavaOptionsProvider implements OptionsProvider { .put(CodegenConstants.SERIALIZABLE_MODEL, SERIALIZABLE_MODEL_VALUE) .put(JavaClientCodegen.FULL_JAVA_UTIL, FULL_JAVA_UTIL_VALUE) .put(CodegenConstants.LIBRARY, LIBRARY_VALUE) + .put(CodegenConstants.SERIALIZE_BIG_DECIMAL_AS_STRING, "true") .build(); }