web-dev-qa-db-ja.com

SpringfoxからSpringDocへの移行時のNull ApiResponse

Swaggerドキュメントを生成するために、springfox-swagger2(OpenAPI 2)からspringdoc-openapi-ui(OpenAPI 3)に移行しようとしています。

ここにサンプルのルートがあります:

@RequestMapping("/api/object/")
public interface IObjectController {

    @RequestMapping(path = "v1/{param}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseStatus(value = HttpStatus.OK)
    ObjectDto getObjectByParamV1(@PathVariable("param") String code);
}

Swagger生成はspringfox-swagger2でうまく機能しましたが、springdocで次の問題が発生しました。

Null key for a Map not allowed in JSON (use a converting NullKeySerializer?)
(through reference chain: io.swagger.v3.oas.models.OpenAPI["paths"]->
io.swagger.v3.oas.models.Paths["/api/object/v1/{param}"]->io.swagger.v3.oas.models.PathItem["get"]->
io.swagger.v3.oas.models.Operation["responses"]->io.swagger.v3.oas.models.responses.ApiResponses["null"])

実際、OpenAPIは次のオブジェクトをシリアル化しようとします。

responses: class ApiResponses {
            {null=class ApiResponse {
                description: default response
                headers: null
                content: class Content {
                    {*/*=class MediaType {
                        schema: class ComposedSchema {
                            class Schema {
                                type: null
                                format: null
                                $ref: null
                                description: null
                                title: null
                                multipleOf: null
                                maximum: null
                                exclusiveMaximum: null
                                minimum: null
                                exclusiveMinimum: null
                                maxLength: null
                                minLength: null
                                pattern: null
                                maxItems: null
                                minItems: null
                                uniqueItems: null
                                maxProperties: null
                                minProperties: null
                                required: null
                                not: null
                                properties: null
                                additionalProperties: null
                                nullable: null
                                readOnly: null
                                writeOnly: null
                                example: null
                                externalDocs: null
                                deprecated: null
                                discriminator: null
                                xml: null
                            }
                            allOf: null
                            anyOf: null
                            oneOf: [class Schema {
                                type: object
                                format: null
                                $ref: null
                                description: null
                                title: null
                                multipleOf: null
                                maximum: null
                                exclusiveMaximum: null
                                minimum: null
                                exclusiveMinimum: null
                                maxLength: null
                                minLength: null
                                pattern: null
                                maxItems: null
                                minItems: null
                                uniqueItems: null
                                maxProperties: null
                                minProperties: null
                                required: null
                                not: null
                                properties: null
                                additionalProperties: null
                                nullable: null
                                readOnly: null
                                writeOnly: null
                                example: null
                                externalDocs: null
                                deprecated: null
                                discriminator: null
                                xml: null
                            }, class StringSchema {
                                class Schema {
                                    type: string
                                    format: null
                                    $ref: null
                                    description: null
                                    title: null
                                    multipleOf: null
                                    maximum: null
                                    exclusiveMaximum: null
                                    minimum: null
                                    exclusiveMinimum: null
                                    maxLength: null
                                    minLength: null
                                    pattern: null
                                    maxItems: null
                                    minItems: null
                                    uniqueItems: null
                                    maxProperties: null
                                    minProperties: null
                                    required: null
                                    not: null
                                    properties: null
                                    additionalProperties: null
                                    nullable: null
                                    readOnly: null
                                    writeOnly: null
                                    example: null
                                    externalDocs: null
                                    deprecated: null
                                    discriminator: null
                                    xml: null
                                }
                            }]
                        }
                        examples: null
                        example: null
                        encoding: null
                    }}
                }
                links: null
                extensions: null
                $ref: null
            }, 200=class ApiResponse {
                description: OK
                headers: null
                content: class Content {
                    {application/json=class MediaType {
                        schema: class Schema {
                            type: null
                            format: null
                            $ref: #/components/schemas/ObjectDto
                            description: null
                            title: null
                            multipleOf: null
                            maximum: null
                            exclusiveMaximum: null
                            minimum: null
                            exclusiveMinimum: null
                            maxLength: null
                            minLength: null
                            pattern: null
                            maxItems: null
                            minItems: null
                            uniqueItems: null
                            maxProperties: null
                            minProperties: null
                            required: null
                            not: null
                            properties: null
                            additionalProperties: null
                            nullable: null
                            readOnly: null
                            writeOnly: null
                            example: null
                            externalDocs: null
                            deprecated: null
                            discriminator: null
                            xml: null
                        }
                        examples: null
                        example: null
                        encoding: null
                    }}
                }
                links: null
                extensions: null
                $ref: null
            }}
            extensions: null
        }

ご覧のとおり、奇妙なことにApiResponseキーを持つApiResponsesに空のnullオブジェクトがあり、シリアル化はjackonのMapSerializer.serialize()で失敗します。

// What is this _suppressNulls ?!
// _suppressableValue IS null, but is not suppressed
if ((_suppressableValue != null) || _suppressNulls) {
    serializeOptionalFields(value, gen, provider, _suppressableValue);
}

Swaggerアノテーションをいくつ使用しても(@Operation@ApiResponse...を追加)、このnull ApiResponseは残ります。 MapSerializer.serializeOptionalFields()のように、特にsuppressNullvaluesにのみ影響し、keysには影響しません。

このnull- keyed ApiResponseを削除するにはどうすればよいですか?

3
ojathelonius

原因は、例外ハンドラで@ExceptionHandlerに@ResponseStatusがないことが原因のようです。

回避策は、Swaggerドキュメントに表示するために追加することです。

既に修正されている既存の問題があります:

修正はv1.3.8で利用可能になります。

5
brianbro