I have a Spring Boot application using springdoc-openapi to generate Swagger API documentation for my controllers. One of the enums used in the JSON request/response has a different JSON representation than its value
/toString()
. This is achieved using the Jackson @JsonValue
annotation:
public enum Suit {
HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
@JsonValue
private final String jsonValue;
Suit(String jsonValue) { this.jsonValue = jsonValue; }
}
However, the generated Swagger API docs use the enum value (specifically, the value of toString()
) rather than the JSON representation (per @JsonValue
) when listing the enum values:
{
"openapi": "3.0.1",
"info": { "title": "OpenAPI definition", "version": "v0" },
"servers": [
{ "url": "http://localhost:8080", "description": "Generated server url" }
],
"paths": { ... },
"components": {
"schemas": {
"PlayingCard": {
"type": "object",
"properties": {
"suit": {
"type": "string",
"enum": [ "Hearts", "Diamonds", "Clubs", "Spades" ]
},
"value": { "type": "integer", "format": "int32" }
}
}
}
}
}
There is closed issue #1101 in the springdoc-openapi project which requests allowing @JsonValue
to affect the enum serialization. However, that issue was closed since no PR was submitted for it.
How can I get the enum list to match the actual JSON type accepted/returned by the REST endpoint, and not the toString()
values?
My first thought to solving this issue was to use the @Schema(allowableValues = {...}]
annotation from Swagger Core. However, whether by bug or by design, this adds to the list of values, rather than replacing it:
@Schema(allowableValues = {"Hearts", "Diamonds", "Clubs", "Spades"})
public enum Suit {
HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
// ...
}
"suit": {
"type": "string",
"enum": [
"HEARTS",
"DIAMONDS",
"CLUBS",
"SPADES",
"Hearts",
"Diamonds",
"Clubs",
"Spades"
]
}
plugins {
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
implementation 'io.swagger.core.v3:swagger-annotations:2.1.10'
implementation 'org.springdoc:springdoc-openapi-ui:1.5.10'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
package com.example.springdoc;
import com.fasterxml.jackson.annotation.JsonValue;
public class PlayingCard {
private Suit suit;
private Integer value;
public Suit getSuit() { return suit; }
public void setSuit(Suit suit) { this.suit = suit; }
public Integer getValue() { return value; }
public void setValue(Integer value) { this.value = value; }
public enum Suit {
HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
@JsonValue
private final String jsonValue;
Suit(String jsonValue) { this.jsonValue = jsonValue; }
}
}
package com.example.springdoc;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/playingCard")
public class PlayingCardController {
@PostMapping
public PlayingCard echo(@RequestBody PlayingCard card) {
return card;
}
}
Swagger URL: http://localhost:8080/v3/api-docs
This was due to bug #3998 in versions of Swagger Core prior to 2.2.5. In those versions of the library, @JsonValue
is handled properly when on a public method, but not when on a field. Upgrading to version 2.2.5 of Swagger Core or later will cause the example to work as desired without modification.
Alternatively, for versions of Swagger Core before 2.2.5, adding a @JsonValue
-annotated public accessor method will have the desired effect:
public enum Suit {
HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
private final String jsonValue;
Suit(String jsonValue) { this.jsonValue = jsonValue; }
@JsonValue
public String getJsonValue() {
return jsonValue;
}
}