Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4ca2601

Browse files
With 'oneOf' the response schema contains an extra 'type: string. Fixes #3076
1 parent ea14088 commit 4ca2601

File tree

8 files changed

+250
-5
lines changed

8 files changed

+250
-5
lines changed

‎springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocUtils.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ public static boolean isComposedSchema(Schema referencedSchema) {
166166
*/
167167
public static void handleSchemaTypes(Schema<?> schema) {
168168
if (schema != null) {
169-
if (schema.getType() != null && CollectionUtils.isEmpty(schema.getTypes())) {
169+
if (schema.getType() == null && schema.getTypes() == null && schema.get$ref() == null && !isComposedSchema(schema)) {
170+
schema.addType("object");
171+
}
172+
else if (schema.getType() != null && CollectionUtils.isEmpty(schema.getTypes()) && !isComposedSchema(schema)) {
170173
schema.addType(schema.getType());
171174
}
172175
else if (schema.getItems() != null && schema.getItems().getType() != null
@@ -176,9 +179,6 @@ else if (schema.getItems() != null && schema.getItems().getType() != null
176179
if (schema.getProperties() != null) {
177180
schema.getProperties().forEach((key, value) -> handleSchemaTypes(value));
178181
}
179-
if (schema.getType() == null && schema.getTypes() == null && schema.get$ref() == null && !isComposedSchema(schema)) {
180-
schema.addType("object");
181-
}
182182
}
183183
}
184184

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * *
7+
* * * * * * Copyright 2019-2025 the original author or authors.
8+
* * * * * *
9+
* * * * * * Licensed under the Apache License, Version 2.0 (the "License");
10+
* * * * * * you may not use this file except in compliance with the License.
11+
* * * * * * You may obtain a copy of the License at
12+
* * * * * *
13+
* * * * * * https://www.apache.org/licenses/LICENSE-2.0
14+
* * * * * *
15+
* * * * * * Unless required by applicable law or agreed to in writing, software
16+
* * * * * * distributed under the License is distributed on an "AS IS" BASIS,
17+
* * * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* * * * * * See the License for the specific language governing permissions and
19+
* * * * * * limitations under the License.
20+
* * * * *
21+
* * * *
22+
* * *
23+
* *
24+
*
25+
*/
26+
27+
package test.org.springdoc.api.v31.app12;
28+
29+
import test.org.springdoc.api.v31.AbstractSpringDocTest;
30+
31+
import org.springframework.boot.autoconfigure.SpringBootApplication;
32+
33+
public class SpringDocApp12Test extends AbstractSpringDocTest {
34+
35+
@SpringBootApplication
36+
static class SpringDocTestApp {
37+
}
38+
39+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package test.org.springdoc.api.v31.app12.configuration;
2+
3+
import java.util.List;
4+
5+
import com.fasterxml.jackson.annotation.JsonInclude;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.http.MediaType;
11+
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
12+
13+
@Configuration
14+
public class WebMvcConfiguration {
15+
16+
@Bean
17+
MappingJackson2HttpMessageConverter getMappingJacksonHttpMessageConverter() {
18+
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
19+
converter.setSupportedMediaTypes(List.of(MediaType.APPLICATION_JSON));
20+
converter.setObjectMapper(new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL)
21+
);
22+
23+
return converter;
24+
}
25+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package test.org.springdoc.api.v31.app12.controllers;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.media.Content;
5+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
6+
import test.org.springdoc.api.v31.app12.model.Cat;
7+
8+
import org.springframework.hateoas.MediaTypes;
9+
import org.springframework.hateoas.RepresentationModel;
10+
import org.springframework.http.HttpStatus;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.RequestMapping;
13+
import org.springframework.web.bind.annotation.ResponseStatus;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@RestController
17+
@RequestMapping(path = "/")
18+
public class BasicController {
19+
20+
@GetMapping("/cat")
21+
@ResponseStatus(HttpStatus.OK)
22+
@Operation(summary = "get", description = "Provides an animal.")
23+
public String get(Cat cat) {
24+
return cat != null ? cat.getName() : "";
25+
}
26+
27+
@GetMapping("/test")
28+
@ResponseStatus(HttpStatus.OK)
29+
@Operation(summary = "get", description = "Provides a response.")
30+
@ApiResponse(content = @Content(mediaType = MediaTypes.HAL_JSON_VALUE,
31+
schema = @io.swagger.v3.oas.annotations.media.Schema(oneOf = {
32+
Integer.class
33+
})),
34+
responseCode = "200")
35+
public Object get() {
36+
return 1;
37+
}
38+
39+
// dummy
40+
public static class Response extends RepresentationModel {
41+
public Response(String v) {}
42+
}
43+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package test.org.springdoc.api.v31.app12.controllers;
2+
3+
import org.springdoc.core.customizers.SpringDocCustomizers;
4+
import org.springdoc.core.properties.SpringDocConfigProperties;
5+
import org.springdoc.core.providers.SpringDocProviders;
6+
import org.springdoc.core.service.AbstractRequestService;
7+
import org.springdoc.core.service.GenericResponseService;
8+
import org.springdoc.core.service.OpenAPIService;
9+
import org.springdoc.core.service.OperationService;
10+
import org.springdoc.webmvc.api.OpenApiWebMvcResource;
11+
12+
import org.springframework.beans.factory.ObjectFactory;
13+
import org.springframework.web.bind.annotation.RestController;
14+
15+
@RestController
16+
public class CustomOpenApiWebMvcResource extends OpenApiWebMvcResource {
17+
18+
public CustomOpenApiWebMvcResource(ObjectFactory<OpenAPIService> openAPIBuilderObjectFactory,
19+
AbstractRequestService requestBuilder,
20+
GenericResponseService responseBuilder,
21+
OperationService operationParser,
22+
SpringDocConfigProperties springDocConfigProperties,
23+
SpringDocProviders springDocProviders,
24+
SpringDocCustomizers springDocCustomizers) {
25+
super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, springDocConfigProperties, springDocProviders, springDocCustomizers);
26+
}
27+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package test.org.springdoc.api.v31.app12.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonUnwrapped;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
6+
@Schema(description = "Represents a Cat class.")
7+
public class Cat {
8+
9+
@JsonUnwrapped
10+
@Schema(description = "The name.", nullable = true)
11+
private String name;
12+
13+
public Cat(String name) {
14+
this.name = name;
15+
}
16+
17+
public String getName() {
18+
return name;
19+
}
20+
21+
public void setName(String name) {
22+
this.name = name;
23+
}
24+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
{
2+
"openapi": "3.1.0",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/test": {
15+
"get": {
16+
"tags": [
17+
"basic-controller"
18+
],
19+
"summary": "get",
20+
"description": "Provides a response.",
21+
"operationId": "get",
22+
"responses": {
23+
"200": {
24+
"description": "OK",
25+
"content": {
26+
"application/hal+json": {
27+
"schema": {
28+
"oneOf": [
29+
{
30+
"type": "integer",
31+
"format": "int32"
32+
}
33+
]
34+
}
35+
}
36+
}
37+
}
38+
}
39+
}
40+
},
41+
"/cat": {
42+
"get": {
43+
"tags": [
44+
"basic-controller"
45+
],
46+
"summary": "get",
47+
"description": "Provides an animal.",
48+
"operationId": "get_1",
49+
"parameters": [
50+
{
51+
"name": "cat",
52+
"in": "query",
53+
"required": true,
54+
"schema": {
55+
"$ref": "#/components/schemas/Cat"
56+
}
57+
}
58+
],
59+
"responses": {
60+
"200": {
61+
"description": "OK",
62+
"content": {
63+
"*/*": {
64+
"schema": {
65+
"type": "string"
66+
}
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
},
74+
"components": {
75+
"schemas": {
76+
"Cat": {
77+
"type": "object",
78+
"description": "Represents a Cat class.",
79+
"properties": {
80+
"name": {
81+
"type": "string",
82+
"description": "The name."
83+
}
84+
}
85+
}
86+
}
87+
}
88+
}

‎springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/3.1.0/app6.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"content": {
3232
"application/json": {
3333
"schema": {
34-
"type": "string",
3534
"oneOf": [
3635
{
3736
"type": "string"

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /