Repository: springdoc/springdoc-openapi (java)

Issue:
Request body description not shown
I have created controller with optional RequestBody. I added RequestBody but description not shown in the generated Swagger UI.

Controller snippet:
```
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.responses.ApiResponse
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
import java.util.UUID

@RestController
class HelloController {

    @PostMapping("/hello")
    @Operation(summary = "Say hello",
        requestBody = io.swagger.v3.oas.annotations.parameters.RequestBody(
            description = "Map uid -> string)",
            required = false
        ),
        responses = [
            ApiResponse(responseCode = "200", description = "Ok"),
        ])
    fun hello(@RequestBody(required = false) body: Map<UUID, String>?): ResponseEntity<*> {
        return ResponseEntity.ok().build<Any>()
    }
    
}
```
How Swagger UI looks:
![image](https://github.com/user-attachments/assets/51c98278-359f-45e0-95c6-4ca1072851d0)

Actual OpenAPI Description json:
`hello":{"post":{"tags":["hello-controller"],"summary":"Say hello","operationId":"hello","requestBody":{"description":"Map uid -> string)"},"responses":{"200":{"description":"Ok","content":{"*/*":{"schema":{"type":"object"}}}}}}},`

Expected behavior:
Request body shown

Actual behavior:
Request body not shown

Spring boot: 3.2.2
Spring doc: 2.6.0

Pull request:
Display nullable request body with map type
Fixes #2703 

## Problem Overview
```java
@RestController
public class GreetController {
    @PostMapping("/greet")
    @Operation(summary = "Greet")
    public String greet(
            @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Some description", required = false)
            @RequestBody(required = false) Map<String, String> body) {
        return body.getOrDefault("greet", "Hello");
    }
}
```
In the example above, the request body is not displayed because it is marked as a parameter to ignore. 
This behavior stems from the fact that the request body is both optional and of type `java.util.Map`.
The relevant code can be found here:
https://github.com/springdoc/springdoc-openapi/blob/03349cff6a2480555c746cf560f7d14272c93331/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java#L451-L457

https://github.com/springdoc/springdoc-openapi/blob/03349cff6a2480555c746cf560f7d14272c93331/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java#L141

Although this logic makes sense in some cases, I believe that using a `Map` as a request body is fairly common and should be supported, even when the request body is not required. As such, I’ve made an adjustment that ensures the request body will still be displayed when its type is `Map`.

I would love to hear your thoughts or feedback on this approach and whether you think it aligns with the project’s goals.

Patch:
diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
index 77b125edb..53aa8a480 100644
--- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
+++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/AbstractRequestService.java
@@ -3,7 +3,7 @@
  *  *
  *  *  *
  *  *  *  *
- *  *  *  *  * Copyright 2019-2022 the original author or authors.
+ *  *  *  *  * Copyright 2019-2024 the original author or authors.
  *  *  *  *  *
  *  *  *  *  * Licensed under the Apache License, Version 2.0 (the "License");
  *  *  *  *  * you may not use this file except in compliance with the License.
@@ -25,7 +25,6 @@
 package org.springdoc.core.service;
 
 import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.util.ArrayList;
@@ -89,7 +88,6 @@ import org.springframework.web.bind.support.SessionStatus;
 import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.context.request.WebRequest;
 import org.springframework.web.method.HandlerMethod;
-import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.util.UriComponentsBuilder;
 
 import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
@@ -453,6 +451,8 @@ public abstract class AbstractRequestService {
 			return true;
 		if (isRequiredAnnotation(parameter))
 			return false;
+		if (isRequestBodyWithMapType(parameter))
+			return false;
 		return isRequestTypeToIgnore(parameter.getParameterType());
 	}
 
@@ -471,6 +471,21 @@ public abstract class AbstractRequestService {
 				|| (requestBody != null && requestBody.required());
 	}
 
+	/**
+	 * Is request body with map type
+	 *
+	 * @param parameter the parameter
+	 * @return the boolean
+	 */
+	private boolean isRequestBodyWithMapType(MethodParameter parameter) {
+		org.springframework.web.bind.annotation.RequestBody requestBody = parameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class);
+		if (requestBody == null) {
+			return false;
+		}
+		Class<?> parameterType = parameter.getParameterType();
+		return parameterType == java.util.Map.class;
+	}
+
 	/**
 	 * Sets params.
 	 *


Test patch:
diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/GreetController.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/GreetController.java
new file mode 100644
index 000000000..c3b62b1b7
--- /dev/null
+++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/GreetController.java
@@ -0,0 +1,37 @@
+/*
+ *
+ *  * Copyright 2019-2024 the original author or authors.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *      https://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package test.org.springdoc.api.v31.app10;
+
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Map;
+
+@RestController
+public class GreetController {
+    @PostMapping("/greet")
+    @Operation(summary = "Greet")
+    public String greet(
+            @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Some description", required = false)
+            @RequestBody(required = false) Map<String, String> body) {
+        return body.getOrDefault("greet", "Hello");
+    }
+}
diff --git a/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/SpringDocApp10Test.java b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/SpringDocApp10Test.java
new file mode 100644
index 000000000..6f416b859
--- /dev/null
+++ b/springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app10/SpringDocApp10Test.java
@@ -0,0 +1,30 @@
+/*
+ *
+ *  * Copyright 2019-2024 the original author or authors.
+ *  *
+ *  * Licensed under the Apache License, Version 2.0 (the "License");
+ *  * you may not use this file except in compliance with the License.
+ *  * You may obtain a copy of the License at
+ *  *
+ *  *      https://www.apache.org/licenses/LICENSE-2.0
+ *  *
+ *  * Unless required by applicable law or agreed to in writing, software
+ *  * distributed under the License is distributed on an "AS IS" BASIS,
+ *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  * See the License for the specific language governing permissions and
+ *  * limitations under the License.
+ *
+ */
+
+package test.org.springdoc.api.v31.app10;
+
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import test.org.springdoc.api.v31.AbstractSpringDocV31Test;
+
+public class SpringDocApp10Test extends AbstractSpringDocV31Test {
+
+    @SpringBootApplication
+    static class SpringDocTestApp {
+
+    }
+}
diff --git a/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app10.json b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app10.json
new file mode 100644
index 000000000..c20537fa9
--- /dev/null
+++ b/springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.1.0/app10.json
@@ -0,0 +1,52 @@
+{
+  "openapi": "3.1.0",
+  "info": {
+    "title": "OpenAPI definition",
+    "version": "v0"
+  },
+  "servers": [
+    {
+      "url": "http://localhost",
+      "description": "Generated server url"
+    }
+  ],
+  "paths": {
+    "/greet": {
+      "post": {
+        "tags": [
+          "greet-controller"
+        ],
+        "summary": "Greet",
+        "operationId": "greet",
+        "requestBody": {
+          "description": "Some description",
+          "content": {
+            "application/json": {
+              "schema": {
+                "type": "object",
+                "additionalProperties": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "OK",
+            "content": {
+              "*/*": {
+                "schema": {
+                  "type": "string"
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  },
+  "components": {
+
+  }
+}
\ No newline at end of file