javajacksonswaggerlombokspringdoc

How can @JsonValue be used for Swagger enum values using springdoc-openapi with a Lombok getter


Given a Spring Boot project that uses the springdoc-openapi library to expose an OpenAPI (Swagger) endpoint documenting the Spring MVC controller endpoints of the project.

One of the enums in the project uses @JsonValue from Jackson on a field to change the JSON representation of the enum. This enum field is exposed as a getter using the @Getter annotation from Project Lombok:

@Getter
public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

    @JsonValue
    private final String name;

    Suit(String name) { this.name = name; }
}

However, despite the Jackson representation being based on the field, the enum representation returned by the OpenAPI endpoint uses the toString value of the enum instead:

"suit": {
  "type": "string",
  "enum": [
    "HEARTS",
    "DIAMONDS",
    "CLUBS",
    "SPADES"
  ]
}

Expected:

"suit": {
  "type": "string",
  "enum": [
    "Hearts",
    "Diamonds",
    "Clubs",
    "Spades"
  ]
}

Based on springdoc-openapi#1244 and swagger-core#3998, it's clear that the @JsonValue annotation needs to be applied to the method, and not the field. However, neither the above attempted approach, nor the following, work:

@Getter @JsonValue
public enum Suit {
    HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");

    private final String name;

    Suit(String name) { this.name = name; }
}

How can this enum be exposed with the proper values in Swagger, while still using Lombok to generate the getter?


Solution

  • The solution is to tell Lombok to use the annotation on the generated getter method, using @Getter(onMethod_ = @JsonValue) on the field.

    public enum Suit {
        HEARTS("Hearts"), DIAMONDS("Diamonds"), CLUBS("Clubs"), SPADES("Spades");
    
        @Getter(onMethod_ = @JsonValue)
        private final String name;
    
        Suit(String name) { this.name = name; }
    }
    

    The onMethod property is documented in the @Getter and @Setter and the onX documentation:

    To put annotations on the generated method, you can use onMethod=@__({@AnnotationsHere}). […] For more details see the documentation on the onX feature.

    The syntax is a little strange and depends on the javac you are using.
    On javac7, to use any of the 3 onX features, you must wrap the annotations to be applied to the constructor / method / parameter in @__(@AnnotationGoesHere). To apply multiple annotations, use @__({@Annotation1, @Annotation2}). The annotations can themselves obviously have parameters as well.
    On javac8 and up, you add an underscore after onMethod, onParam, or onConstructor.