With SpringDoc OpenAPI, Double and other non-primitives numbers are not marked as nullable

Viewed 973

We are using SpringDoc OpenAPI with SpingBoot 2.3 and WebFlux and have Swagger UI on top of it (more precisely springdoc-openapi-webflux-ui 1.4.8).

Lately, we noticed that there is no way to differentiate a nullable / optional number (Double) from a primitive (double).

For example, the following class

@With
@Value
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CurrencyBalanceDto {
    double balance;
    Double hardCapping;
}

Results in the following OpenAPI Schema

"CurrencyBalanceDto": {
    "type": "object",
    "properties": {
        "balance": {
            "type": "number",
            "format": "double"
        },
        "hardCapping": {
            "type": "number",
            "format": "double"
        }
    }
}

Based on https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#schemaNullable, I would expect the hardCapping property to have "nullable": true

2 Answers

The quote from the documentation from the provided link:

nullable boolean Allows sending a null value for the defined schema. Default value is false.

So by default, all fields are not nullable. You need explicitly set a field as nullable either by using the annotations like @Null, @Nullable or with @Schema(nullable = true)

The springdoc-openapi library does not mark values as nullable by default; it will only do so if something specific tells it to do so (e.g. @Schema(nullable = true)). This isn't ideal in my personal opinion, as it introduces a discrepancy between what the OpenAPI description claims is allowable and what actually is allowable.

That being said, even if it did, arguably it should automatically mark balance as nullable in this case. If the CurrencyBalanceDto type were only used as a response object, then yes, it would be guaranteed to never be null. However, Jackson by default allows allows deserialization of primitive values from null:, in this case the default for the given primitive type will be used instead of null (0 for int, 0.0 for double, false for boolean, etc.).

Because Jackson allows nulls for primitive values, it is in fact therefore valid by default for a null value to be passed as a primitive field of a request object . Without additional context, it would be impossible for springdoc-openapi to determine that such a field should be nullable.

Example

In your specific case, a null balance could be successfully passed to e.g. a POST endpoint that took a CurrencyBalanceDto:

POST /currency/balance HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 2

{}

This object would be received by the Spring controller as a CurrencyBalanceDto with balance of 0.0 and hardCapping of null.

Related