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" ] }
Reproducible Example
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
Advertisement
Answer
Due to bug #3998 in Swagger Core, @JsonValue
is handled properly when on a public method, but not when on a field. Therefore, adding a public accessor method will work as desired:
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; } }