Reputation: 12416
Within our controllers we want use enums as method parameters:
@GetMapping("/feed")
public void feed(@RequestParam myEnum: MyEnum)
This works until we try to use strings which doesn't match by case. For that I found a similar question:
This however requires setting handler for each enum. I would rather like to implement my custom handler for all enums. Something like:
val ordinal = param.toString().toIntOrNull()
// Reflection on built-in Kotlin types is not yet fully supported... so ...
return if (null == ordinal)
try {
type.jvmErasure.java.methods.find { it.name == "valueOf" }!!.invoke(null, param.toString())
} catch (e: Exception) {
type.jvmErasure.java.methods.find { it.name == "valueOf" }!!.invoke(null, param.toString().toUpperCase())
}
else
type.jvmErasure.java.enumConstants[ordinal]
Sorry this is written in Kotlin, but shouldn't be hard to understand. It basically tries valueOf on the original string, then on uppercase version and if it is number, it uses ordinal value of the enum.
I am looking for a general solution, not handler for single enum.
Upvotes: 0
Views: 1246
Reputation: 8297
Inspired by StringToEnumConverterFactory.
I will use Java to give you a sample solution.
Following code uppercases strings are to be converted to Enum
and then invokes valueOf
, but you can put any logic to the convert method.
@SuppressWarnings({"unchecked", "rawtypes"})
public class CaseInsensitiveStringToEnumConverterFactory implements ConverterFactory<String, Enum> {
@Override
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new CaseInsensitiveStringToEnumConverter(getEnumType(targetType));
}
// ConversionUtils are package private. The method is copy-pasted
private static Class<?> getEnumType(Class<?> targetType) {
Class<?> enumType = targetType;
while (enumType != null && !enumType.isEnum()) {
enumType = enumType.getSuperclass();
}
Assert.notNull(enumType, () -> "The target type " + targetType.getName() + " does not refer to an enum");
return enumType;
}
@SuppressWarnings("unchecked")
private class CaseInsensitiveStringToEnumConverter<T extends Enum> implements Converter<String, T> {
private final Class<T> enumClass;
CaseInsensitiveStringToEnumConverter(Class<T> enumClass) {
this.enumClass = enumClass;
}
@Override
public T convert(String source) {
if (source.isEmpty()) {
return null;
}
source = source.toUpperCase().trim();
return (T)Enum.valueOf(enumClass, source);
}
}
}
MvcConfig:
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new CaseInsensitiveStringToEnumConverterFactory());
}
}
Sample enum:
public enum Color {
RED,
BLUE,
GREEN
}
SampleContoller:
@RestController
public class MyController {
@RequestMapping("/")
public Map<String, Object> testMethod(@RequestParam("color") Color color) {
Map<String, Object> map = new HashMap<>();
map.put("color", color);
return map;
}
}
Testing:
$ curl 'localhost:8080/?color=red'
Output:
{"color":"RED"}
Upvotes: 1