diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java index e331732722..82ce4e5c20 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/AnnotationsUtils.java @@ -765,16 +765,24 @@ public static Optional getSchemaFromAnnotation( schemaObject.setDefault(schema.defaultValue()); } if (StringUtils.isNotBlank(schema.example())) { - try { - if (openapi31) { - schemaObject.setExample(Json31.mapper().readTree(schema.example())); - } else { - schemaObject.setExample(Json.mapper().readTree(schema.example())); + String exampleValue = schema.example().trim(); + + // Fix: Prevent numeric-starting example strings (e.g. "5 lacs per annum") from being parsed as numbers + if (exampleValue.matches("^[0-9].*") && !exampleValue.startsWith("\"")) { + schemaObject.setExample(exampleValue); + } else { + try { + if (openapi31) { + schemaObject.setExample(Json31.mapper().readTree(exampleValue)); + } else { + schemaObject.setExample(Json.mapper().readTree(exampleValue)); + } + } catch (IOException e) { + schemaObject.setExample(exampleValue); } - } catch (IOException e) { - schemaObject.setExample(schema.example()); } } + if (StringUtils.isNotBlank(schema.format())) { schemaObject.setFormat(schema.format()); } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/util/AnnotationsUtilsTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/AnnotationsUtilsTest.java index 3b717062fc..6d17ed5f94 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/util/AnnotationsUtilsTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/util/AnnotationsUtilsTest.java @@ -7,6 +7,9 @@ import io.swagger.v3.oas.models.media.Schema; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.lang.annotation.Annotation; +import static org.testng.Assert.assertNotNull; + import java.io.File; import java.io.Serializable; @@ -59,6 +62,7 @@ public void resolveSchemaFromType(Class aClass, Map expected) assertEquals(schema.get$ref(), expected.get("$ref")); } + @DataProvider private Object[][] expectedSchemaFromTypeAndFormat() { return new Object[][]{ @@ -96,6 +100,108 @@ private void emailType() { @ApiResponse(content = @Content(schema = @io.swagger.v3.oas.annotations.media.Schema(implementation = DummyClass.class))) private void dummyType() { } +@Test + public void testExampleStartingWithNumberShouldBeString() throws Exception { + io.swagger.v3.oas.annotations.media.Schema annotation = new io.swagger.v3.oas.annotations.media.Schema() { + @Override + public Class annotationType() { + return io.swagger.v3.oas.annotations.media.Schema.class; + } + + @Override public String name() { return ""; } + @Override public String title() { return ""; } + @Override public String description() { return ""; } + @Override public String format() { return ""; } + @Override public String ref() { return ""; } + @Override public boolean nullable() { return false; } + @Override public boolean required() { return false; } + @Override public io.swagger.v3.oas.annotations.media.Schema.AccessMode accessMode() { return io.swagger.v3.oas.annotations.media.Schema.AccessMode.AUTO; } + @Override public io.swagger.v3.oas.annotations.media.Schema.RequiredMode requiredMode() { return io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO; } + @Override public String example() { return "5 lacs per annum"; } + @Override public Class implementation() { return java.lang.Void.class; } + @Override public Class not() { return java.lang.Void.class; } + @Override public Class[] oneOf() { return new Class[0]; } + @Override public Class[] anyOf() { return new Class[0]; } + @Override public Class[] allOf() { return new Class[0]; } + + @Override public double multipleOf() { return 0; } + @Override public String maximum() { return ""; } + @Override public boolean exclusiveMaximum() { return false; } + @Override public String minimum() { return ""; } + @Override public boolean exclusiveMinimum() { return false; } + @Override public int maxLength() { return Integer.MAX_VALUE; } + @Override public int minLength() { return 0; } + @Override public String pattern() { return ""; } + @Override public int maxProperties() { return 0; } + @Override public int minProperties() { return 0; } + + @Override public boolean hidden() { return false; } + @Override public boolean enumAsRef() { return false; } + @Override public boolean deprecated() { return false; } + @Override public boolean readOnly() { return false; } + @Override public boolean writeOnly() { return false; } + @Override public String type() { return ""; } + @Override public String defaultValue() { return ""; } + @Override public String discriminatorProperty() { return ""; } + @Override public String[] allowableValues() { return new String[0]; } + + @Override public String[] requiredProperties() { return new String[0]; } + + @Override public io.swagger.v3.oas.annotations.media.Schema.SchemaResolution schemaResolution() { return io.swagger.v3.oas.annotations.media.Schema.SchemaResolution.DEFAULT; } + @Override public String _const() { return ""; } + @Override public String[] examples() { return new String[0]; } + @Override public Class additionalPropertiesSchema() { return java.lang.Void.class; } + @Override public Class unevaluatedProperties() { return java.lang.Void.class; } + @Override public io.swagger.v3.oas.annotations.StringToClassMapItem[] properties() { return new io.swagger.v3.oas.annotations.StringToClassMapItem[0]; } + @Override public io.swagger.v3.oas.annotations.StringToClassMapItem[] patternProperties() { return new io.swagger.v3.oas.annotations.StringToClassMapItem[0]; } + @Override public io.swagger.v3.oas.annotations.StringToClassMapItem[] dependentSchemas() { return new io.swagger.v3.oas.annotations.StringToClassMapItem[0]; } + @Override public io.swagger.v3.oas.annotations.media.DependentRequired[] dependentRequiredMap() { return new io.swagger.v3.oas.annotations.media.DependentRequired[0]; } + @Override public io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue additionalProperties() { return io.swagger.v3.oas.annotations.media.Schema.AdditionalPropertiesValue.FALSE; } + @Override public Class[] exampleClasses() { return new Class[0]; } + @Override public String $comment() { return ""; } + @Override public Class then() { return java.lang.Void.class; } + @Override public Class _else() { return java.lang.Void.class; } + @Override public Class _if() { return java.lang.Void.class; } + @Override public Class unevaluatedItems() { return java.lang.Void.class; } + @Override public Class additionalItems() { return java.lang.Void.class; } + @Override public int minContains() { return 0; } + @Override public int maxContains() { return 0; } + @Override public Class propertyNames() { return java.lang.Void.class; } + @Override public Class contentSchema() { return java.lang.Void.class; } + @Override public String contentMediaType() { return ""; } + @Override public String contentEncoding() { return ""; } + @Override public String $dynamicRef() { return ""; } + @Override public String $dynamicAnchor() { return ""; } + @Override public String $vocabulary() { return ""; } + @Override public String $anchor() { return ""; } + @Override public String $schema() { return ""; } + @Override public String $id() { return ""; } + @Override public Class contains() { return java.lang.Void.class; } + @Override public int exclusiveMinimumValue() { return 0; } + @Override public int exclusiveMaximumValue() { return 0; } + @Override public String[] types() { return new String[0]; } + @Override public Class[] prefixItems() { return new Class[0]; } + @Override public io.swagger.v3.oas.annotations.extensions.Extension[] extensions() { return new io.swagger.v3.oas.annotations.extensions.Extension[0]; } + @Override public Class[] subTypes() { return new Class[0]; } + @Override public io.swagger.v3.oas.annotations.media.DiscriminatorMapping[] discriminatorMapping() { return new io.swagger.v3.oas.annotations.media.DiscriminatorMapping[0]; } + + @Override + public io.swagger.v3.oas.annotations.ExternalDocumentation externalDocs() { + return new io.swagger.v3.oas.annotations.ExternalDocumentation() { + @Override public Class annotationType() { return io.swagger.v3.oas.annotations.ExternalDocumentation.class; } + @Override public String description() { return ""; } + @Override public String url() { return ""; } + @Override public io.swagger.v3.oas.annotations.extensions.Extension[] extensions() { return new io.swagger.v3.oas.annotations.extensions.Extension[0]; } + }; + } + }; + + io.swagger.v3.oas.models.media.Schema schema = + AnnotationsUtils.getSchemaFromAnnotation(annotation, null, null, false, null).get(); + + assertNotNull(schema); + assertEquals(schema.getExample(), "5 lacs per annum"); + } class DummyClass implements Serializable {}