Closed beeondev closed 11 months ago
say the same shortly :
// processor-spring impl
Mode.fromValue(source);
is not the same as
Mode.valueOf(source);
yes, this is annoying.
the question is how to improve it.
generate lowercase enums
provide/generate converter
StringToEnumConverter implements Converter<String, MyEnum>
any other idea?
I would like specify what is annoying :
Second point is far more annoying, because each enum must have its converter registered. Having a special interface on the enum i.e like "Supplier<String>" would allow :
If breaking backward compatibily is an option, then perhaphs, because initial openapi spec declare a string type, we can just keep it as a string. enum declaration in spec could be interpreted a special validator constraint with something like :
public Mono<String> someEndpoint(@RequestBody @DictionaryValue(allowedValues = {"a", "b"}) String mode)
with :
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = DictionaryValueValidator.class)
@Documented
public @interface DictionaryValue {
String message() default "Invalid value. Please choose from the dictionary.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] allowedValues() default {};
}
and
public class DictionaryValueValidator implements ConstraintValidator<DictionaryValue, String> {
private String[] allowedValues;
@Override
public void initialize(DictionaryValue constraintAnnotation) {
this.allowedValues = constraintAnnotation.allowedValues();
}
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value != null && Arrays.asList(allowedValues).contains(value);
}
}
This is only suggestions. Best regards
I have successfully created a prototype ConverterFactory
that creates enum converters for generated enums that implement Supplier<>
. Not much code which is nice :-)
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.stereotype.Component;
import java.util.EnumSet;
import java.util.function.Supplier;
public class StringToEnumConverterFactory<T extends Enum<T> & Supplier<String>> implements ConverterFactory<String, T> {
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public <E extends T> Converter<String, E> getConverter(Class<E> targetType) {
return new StringToEnumConverter(targetType);
}
static class StringToEnumConverter<T extends Enum<T> & Supplier<String>> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
String sourceValue = source.trim();
for (T e : EnumSet.allOf(enumType)) {
if (e.get().equals(sourceValue)) {
return e;
}
}
throw new IllegalArgumentException(
String.format("No enum constant of %s has the value %s", enumType.getCanonicalName(), sourceValue));
}
}
}
It requires a one time registration using Web[Mvc|Flux]Configurer.addFormatters()
.
Your String
version is also something I'm considering. I'm thinking about a global setting to switch between enum
and String
. enum
would also generate the converter factory and it can be registered (or not if it is not needed).
Thanks for your input :-)
improved in 2023.6, added enum-type
mapping option default
(like before), string
(just use string) and framework
(enum with spring converter factory)
I have a legacy API that takes an enumeration as argument, here is a yaml fragment of OpenAPI spec :
Such enumeration should be used as string in API, but is translated to Enum with processor (why not, this is not the problem) as :
Internally spring use StringToEnumConverterFactory to convert the string to enumeration:
and it fail, because Enum.valueOf(Mode.class, "a") delegates to internal enumConstantDirectory which contains only uppercase values "A" and "B" !
So the only workaround is to inject custom converter, but this is not desirable.