diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index 8812bb791d..65c5b58abf 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -327,10 +327,12 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context return context.resolve(aType); } - List> composedSchemaReferencedClasses = getComposedSchemaReferencedClasses(type.getRawClass(), annotatedType.getCtxAnnotations()); + List> composedSchemaReferencedClasses = getComposedSchemaReferencedClasses(type.getRawClass(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation); boolean isComposedSchema = composedSchemaReferencedClasses != null; if (type.isContainerType()) { + // TODO currently a MapSchema or ArraySchema don't also support composed schema props (oneOf,..) + isComposedSchema = false; JavaType keyType = type.getKeyType(); JavaType valueType = type.getContentType(); String pName = null; @@ -752,10 +754,16 @@ public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context .collect(Collectors.toList()); oneOfFiltered.forEach(c -> { Schema oneOfRef = context.resolve(new AnnotatedType().type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation())); - composedSchema.addOneOfItem(new Schema().$ref(oneOfRef.getName())); - // remove shared properties defined in the parent - if (isSubtype(beanDesc.getClassInfo(), c)) { - removeParentProperties(composedSchema, oneOfRef); + if (oneOfRef != null) { + if (StringUtils.isBlank(oneOfRef.getName())) { + composedSchema.addOneOfItem(oneOfRef); + } else { + composedSchema.addOneOfItem(new Schema().$ref(oneOfRef.getName())); + } + // remove shared properties defined in the parent + if (isSubtype(beanDesc.getClassInfo(), c)) { + removeParentProperties(composedSchema, oneOfRef); + } } }); @@ -844,7 +852,9 @@ protected boolean ignore(final Annotated member, final XmlAccessorType xmlAccess private void handleUnwrapped(List props, Schema innerModel, String prefix, String suffix) { if (StringUtils.isBlank(suffix) && StringUtils.isBlank(prefix)) { - props.addAll(innerModel.getProperties().values()); + if (innerModel.getProperties() != null) { + props.addAll(innerModel.getProperties().values()); + } } else { if (prefix == null) { prefix = ""; @@ -852,14 +862,16 @@ private void handleUnwrapped(List props, Schema innerModel, String prefi if (suffix == null) { suffix = ""; } - for (Schema prop : (Collection) innerModel.getProperties().values()) { - try { - Schema clonedProp = Json.mapper().readValue(Json.pretty(prop), Schema.class); - clonedProp.setName(prefix + prop.getName() + suffix); - props.add(clonedProp); - } catch (IOException e) { - LOGGER.error("Exception cloning property", e); - return; + if (innerModel.getProperties() != null) { + for (Schema prop : (Collection) innerModel.getProperties().values()) { + try { + Schema clonedProp = Json.mapper().readValue(Json.pretty(prop), Schema.class); + clonedProp.setName(prefix + prop.getName() + suffix); + props.add(clonedProp); + } catch (IOException e) { + LOGGER.error("Exception cloning property", e); + return; + } } } } @@ -1217,12 +1229,7 @@ private void removeParentProperties(Schema child, Schema parent) { } } - protected List> getComposedSchemaReferencedClasses(Class clazz, Annotation[] ctxAnnotations) { - - io.swagger.v3.oas.annotations.media.Schema schemaAnnotation = AnnotationsUtils.getSchemaAnnotation(ctxAnnotations); - if (schemaAnnotation == null) { - schemaAnnotation = AnnotationsUtils.getSchemaDeclaredAnnotation(clazz); - } + protected List> getComposedSchemaReferencedClasses(Class clazz, Annotation[] ctxAnnotations, io.swagger.v3.oas.annotations.media.Schema schemaAnnotation) { if (schemaAnnotation != null) { Class[] allOf = schemaAnnotation.allOf(); diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java index cbc46b2776..f43d9ba581 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/PrimitiveType.java @@ -108,6 +108,12 @@ public Schema createProperty() { return new NumberSchema(); } }, + NUMBER(Number.class, "number") { + @Override + public Schema createProperty() { + return new NumberSchema(); + } + }, DATE(DateStub.class, "date") { @Override public DateSchema createProperty() { @@ -185,6 +191,7 @@ public Schema createProperty() { addKeys(keyClasses, DOUBLE, Double.class, Double.TYPE); addKeys(keyClasses, INTEGER, java.math.BigInteger.class); addKeys(keyClasses, DECIMAL, java.math.BigDecimal.class); + addKeys(keyClasses, NUMBER, Number.class); addKeys(keyClasses, DATE, DateStub.class); addKeys(keyClasses, DATE_TIME, java.util.Date.class); addKeys(keyClasses, FILE, java.io.File.class); diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/ComposedSchemaTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/ComposedSchemaTest.java index 035dd47df0..c8cd5ba97c 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/ComposedSchemaTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/ComposedSchemaTest.java @@ -4,6 +4,8 @@ import io.swagger.v3.core.resolving.resources.TestObject2616; import io.swagger.v3.core.resolving.resources.TestObjectTicket2620; import io.swagger.v3.core.resolving.resources.TestObjectTicket2620Subtypes; +import io.swagger.v3.core.resolving.resources.TestObjectTicket2900; +import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.Schema; @@ -56,6 +58,31 @@ public void readBilateralComposedSchema_ticket2620() { Assert.assertTrue(((ComposedSchema)model).getOneOf().size() == 2); } + @Test(description = "read composed schem refs #2900") + public void readComposedSchema_ticket2900() { + Json.mapper().addMixIn(TestObjectTicket2900.GsonJsonPrimitive.class, TestObjectTicket2900.GsonJsonPrimitiveMixIn.class); + Map schemas = ModelConverters.getInstance().readAll(TestObjectTicket2900.class); + Schema model = schemas.get("SomeDTO"); + Assert.assertNotNull(model); + Map properties = model.getProperties(); + Assert.assertNotNull(properties.get("value")); + Assert.assertEquals(properties.get("value").get$ref(), "#/components/schemas/MyJsonPrimitive"); + Assert.assertEquals(properties.get("valueWithMixIn").get$ref(), "#/components/schemas/GsonJsonPrimitive"); + + model = schemas.get("MyJsonPrimitive"); + Assert.assertNotNull(model); + Assert.assertTrue(((ComposedSchema)model).getOneOf().size() == 2); + Assert.assertEquals(((ComposedSchema)model).getOneOf().get(0).getType(), "string"); + Assert.assertEquals(((ComposedSchema)model).getOneOf().get(1).getType(), "number"); + + model = schemas.get("GsonJsonPrimitive"); + Assert.assertNotNull(model); + Assert.assertTrue(((ComposedSchema)model).getOneOf().size() == 2); + Assert.assertEquals(((ComposedSchema)model).getOneOf().get(0).getType(), "string"); + Assert.assertEquals(((ComposedSchema)model).getOneOf().get(1).getType(), "number"); + Assert.assertNull(model.getProperties()); + } + @Test(description = "read composed schem refs #2616") public void readArrayComposedSchema_ticket2616() { Map schemas = ModelConverters.getInstance().readAll(TestObject2616.class); diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectTicket2900.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectTicket2900.java new file mode 100644 index 0000000000..0668ec9e59 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/resources/TestObjectTicket2900.java @@ -0,0 +1,36 @@ +package io.swagger.v3.core.resolving.resources; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; + + @Schema(name = "SomeDTO") + public class TestObjectTicket2900 { + + @Schema(implementation = MyJsonPrimitive.class) + public GsonJsonPrimitive value; + + @Schema( + description = "Description of value", + oneOf = { String.class, Number.class } + ) + public GsonJsonPrimitive valueWithMixIn; + + + public class GsonJsonPrimitive { + private String foo; + public String getFoo(){return foo;} + } + + @Schema( + description = "Description of value", + oneOf = { String.class, Number.class } + ) + public class MyJsonPrimitive { + } + + public abstract class GsonJsonPrimitiveMixIn { + @JsonIgnore + public abstract String getFoo(); + } + + } \ No newline at end of file