Mattan
Mattan

Reputation: 753

Swagger + Jersey Numeric Enum in Java

I have a field in my Rest API (Implemented in Jersey + Jackson + Bean Validation) which is a closed set of numeric values. For example, an Employee rank, which can be one of the following values: 1,2,5,7. And no, defining a matching String for each of the values is not an option.

I'm trying to get the generated Swagger docs to present a numeric enum for those value. I've tried the following:

  1. Defining an Integer with the property: @ApiModelProperty(allowableValues = "1,2,5,7") Integer employeeRank . Swagger-UI did not show any enum, only after changing the type of employeeRank to String. So unfortunately, allowableValues only works with with a String parameter, which I don't want my model to be. employeeRank must be an Integer.
  2. Defining an Enum:

    @JsonSerialize(using = BlockSize.OrderTypeSerializer.class)
    @JsonDeserialize(using = BlockSize.OrderTypeDeserializer.class)
    public enum EmployeeRank {
        ONW(1),
        TWO(2),
        FIVE(5),
        SEVEN(7)
    int rank;
    
    EmployeeRank(int rank) {
        this.rank = rank;
    }
    
    @JsonValue
    public int getRank() {
        return rank;
    }
    
    public class RankSerializer extends JsonSerializer<EmployeeRank> {
    
    
        @Override
        public void serialize(EmployeeRank rank, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            generator.writeStartObject();
            generator.writeFieldName("rank");
            generator.writeNumber(rank.getRank());
            generator.writeEndObject();
        }
    }
    
    public class RankDeserializer extends JsonDeserializer<EmployeeRank> {
    
        @Override
        public EmployeeRank deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            int val =jsonParser.getValueAsInt();
            if (val == 1) {
                return ONE;
            }
    
            if (val == 2) {
                return TWO;
            }
    
            if (val == 5) {
                return FIVE;
            }
    
            if (val == 7) {
                return SEVEN;
            }
    
            throw new IOException("Illiegal rank value " + val);
        }
    } 
    

With no luck. Swagger-UI presents the enum as the enum names (ONE,TWO,FIVE,SEVEN)

  1. Overriding toString on EmployeeRank to return the rank number as string, with or without JsonValue annotation on the method. Didn't have any effect.

This seems like a pretty trivial task to define a numeric set of values in Jersey with Swagger to understand it. Any idea?

Below is my ResourceConfig:

        resourceConfig.packages(true, ResponseWrapper.class.getPackage().getName());
        resourceConfig.property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
        resourceConfig.property(ServerProperties.BV_DISABLE_VALIDATE_ON_EXECUTABLE_OVERRIDE_CHECK, true);


            resourceConfig.register(ApiListingResource.class);
            resourceConfig.register(SwaggerSerializers.class);

        resourceConfig.register(EncodingFilter.class);
        resourceConfig.register(GZipEncoder.class);
        resourceConfig.register(DeflateEncoder.class);
        resourceConfig.register(JacksonFeature.class);
        resourceConfig.register(MultiPartFeature.class);
        resourceConfig.register(ValidationConfigurationContextResolver.class);

And My Swagger initializer:

 BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("3.0");
        beanConfig.setSchemes(new String[]{"https"});
        beanConfig.setHost("localhost");
        beanConfig.setBasePath("/api");
        beanConfig.setPrettyPrint(true);
        beanConfig.setResourcePackage(ApiLevel.class.getPackage().getName());
        beanConfig.setScan(true);

UPDATE

With the suggestion of @fehguy, I upgraded to Jackson 2.7. And there is some progress. Please consider the following enum:

public enum Status {
        @JsonProperty("hello")
        ENUM1,
        @JsonProperty("world")
        ENUM2;
    }

Running the following main application works:

public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        Status user = Status.ENUM1;

        System.out.println(mapper.writeValueAsString(user));
    }

Output is "hello", which is exactly what I needed.

However, Swagger-UI still shows the enum values. Maybe it doesn't process JsonProperty annotation on enum values?

Upvotes: 4

Views: 2232

Answers (2)

Eien
Eien

Reputation: 1167

If you want to customize enum values and generate swagger specification file with them (swagger-ui uses this file) you can do the following.

  1. Customize swagger ObjectMapper before you create an instance of BeanConfig:
import io.swagger.util.Json;

//...
Json.mapper().configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
BeanConfig beanConfig = new BeanConfig();
//...
  1. Override toString method in your enum and add methods annotated with @JsonCreator and @JsonValue:
public enum Status {
    ENUM1("hello"),
    ENUM2("world");

    private final String value;

    Status(final String value) {
        this.value = value;
    }

    @JsonCreator
    public static Rank fromString(final String value) {
        return EnumSet.allOf(Status.class).stream()
                .filter(s -> s.value == value)
                .findFirst().orElse(null);
    }

    @JsonValue
    public String getValue() {
        return value;
    }

    @Override
    public String toString() {
        return value;
    }

}

Model with enum values in Swagger UI

Request with enum values in Swagger UI

A simple code example can be found on GitHub.

Upvotes: -1

fehguy
fehguy

Reputation: 6824

This is a Jackson configuration problem. Take a look at using the @JsonProperty annotation in 2.6.2 or later, which will let you set the friendly name for the enum.

Upvotes: 2

Related Questions