ojathelonius
ojathelonius

Reputation: 804

Null ApiResponse when migrating from springfox to springdoc

I'm trying to migrate from springfox-swagger2 (OpenAPI 2) to springdoc-openapi-ui (OpenAPI 3), to generate swagger documentation.

Here's a sample route :

@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 generation worked just fine with springfox-swagger2, however I get the following issue with 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"])

Indeed, OpenAPI tries to serialize the following object :

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
        }

As you can see, there's an empty ApiResponse object in ApiResponses that weirdly has a null key, and the serialization then fails in jackon's MapSerializer.serialize() :

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

No matter how many Swagger annotations I use (@Operation, adding @ApiResponse...), this null ApiResponse remains. I do not understand where this is coming from, especially suppressNull only affects values and not keys, as per MapSerializer.serializeOptionalFields().

How can I remove this null-keyed ApiResponse ?

Upvotes: 4

Views: 1971

Answers (2)

brianbro
brianbro

Reputation: 4789

It looks like the cause is that on the exception handler there is no @ResponseStatus on @ExceptionHandler.

The workaround is to add in order to display it in the swagger documentation.

There is already existing issue which is already fixed:

The fix will be available on v1.3.8.

Upvotes: 4

daman
daman

Reputation: 309

Using v1.3.4 seems to be working fine for me. I was on v1.3.3 and upgrade to latest v1.3.7 broke things.

I think the culprit is the fix for https://github.com/springdoc/springdoc-openapi/issues/597

Upvotes: 0

Related Questions