Search code examples
resteasyquarkus

Using an Enum in @BeanParam


I am using a @BeanParam like this:

@GET
public Response listAllPaged(@BeanParam PagedRequest pagedRequest) {
    // Do stuff...
}

The bean itself:

public class PagedRequest {

    @QueryParam("sortOrder")
    @DefaultValue("0")
    public int sortOrder;    
}

Now I would like to change the type of sortOrder to the following enum:

public enum SortOrder {
    ASC("asc"),
    DESC("desc");

    public final String sortOrder;

    SortOrder(String sortOrder) {
        this.sortOrder = sortOrder;
    }
}

But as soon as I do this:

public class PagedRequest {

    @QueryParam("sortOrder")
    @DefaultValue("asc")
    public SortOrder sortOrder;
}

My REST Endpoint cannot match the signature anymore and returns a 404. Why is that? I thought that the presence of a constructor accepting a single String should allow JAX-RS to do the conversion.

What am I doing wrong?

UPDATE

I managed to make it work like this, but it does not really answer my initial question...

public enum SortOrder {
    ASC,
    DESC;

    public static SortOrder fromString(String param) {
        String toUpper = param.toUpperCase();
        try {
            return valueOf(toUpper);
        } catch (Exception e) {
            return null;
        }
    }
}

Solution

  • The Enum.valueOf(String) method is used to resolve the value. Since your SorterOrder enums are uppercase you'd be required to send the parameter in uppercase.

    If you want to pass the value in lowercase only you could change the enum names to lower case, e.g. SortOrder.asc.

    If you don't know or don't want to care about the case the parameter is sent in you could use a ParamConverter.

    public class SortOrderParamConverter implements ParamConverter<SortOrder> {
        @Override
        public SortOrder fromString(final String value) {
            if (value != null) {
                return SortOrder.valueOf(value.toUpperCase(Locale.ROOT));
            }
            return SortOrder.ASC;
        }
    
        @Override
        public String toString(final SortOrder value) {
            return value.name();
        }
    }
    

    If you want a more generic approach you could create a ParamConverter or all enums.

    @Provider
    public class EnumParamConverterProvider implements ParamConverterProvider {
        @Override
        public <T> ParamConverter<T> getConverter(final Class<T> rawType, final Type genericType,
                                                  final Annotation[] annotations) {
            if (!rawType.isEnum()) {
                return null;
            }
            final Enum<?>[] constants = (Enum<?>[]) rawType.getEnumConstants();
            return new ParamConverter<T>() {
                @Override
                @SuppressWarnings("unchecked")
                public T fromString(final String value) {
                    if (value == null || value.isEmpty()) {
                        return null;
                    }
                    for (Enum<?> e : constants) {
                        if (e.name().equalsIgnoreCase(value)) {
                            return (T) e;
                        }
                    }
                    // No match, check toString()
                    for (Enum<?> e : constants) {
                        if (e.toString().equalsIgnoreCase(value)) {
                            return (T) e;
                        }
                    }
                    return null;
                }
    
                @Override
                public String toString(final T value) {
                    return value != null ? value.toString() : null;
                }
            };
        }
    }