Grails связывает параметры запроса с перечислением

Приложение My Grails имеет большое количество перечислений, которые выглядят следующим образом:

public enum Rating {
    BEST("be"), GOOD("go"), AVERAGE("av"), BAD("ba"), WORST("wo")
    final String id

    private RateType(String id) {
        this.id = id
    }

    static public RateType getEnumFromId(String value) {
        values().find {it.id == value }
    }   
}

Если у меня есть объект команды, например:

class MyCommand {
    Rating rating
}

Я хотел бы (например) автоматически преобразовать параметр запроса со значением "wo" в Rating.WORST.

Процедура определения пользовательских преобразователей описана здесь (в контексте преобразования строк в даты). Хотя эта процедура работает нормально, я не хочу создавать класс, реализующий PropertyEditorSupport для каждого из моих перечислений. Есть ли лучшая альтернатива?

Ответ 1

Я нашел решение, которым я очень доволен.

Шаг 1: Создайте реализацию PropertyEditorSupport для преобразования текста в/из соответствующего Enum

public class EnumEditor extends PropertyEditorSupport {

    private Class<? extends Enum<?>> clazz

    public EnumEditor(Class<? extends Enum<?>> clazz) {
        this.clazz = clazz
    }

    public String getAsText() {
        return value?.id
    }

    public void setAsText(String text) {
        value = clazz.getEnumFromId(text)
    }
}

Шаг 2: Определите класс, который регистрирует EnumEditor в качестве конвертера для различных классов перечисления. Чтобы изменить список классов перечисления, которые привязаны по id, просто измените BINDABLE_ENUMS

public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {

    private static final String REQUIRED_METHOD_NAME = 'getEnumFromId'

    // Add any enums that you want to bind to by ID into this list
    private static final BINDABLE_ENUMS = [Rating, SomeOtherEnum, SomeOtherEnum2]

    public void registerCustomEditors(PropertyEditorRegistry registry) {            

        BINDABLE_ENUMS.each {enumClass ->
            registerEnum(registry, enumClass)
        }
    }

    /**
     * Register an enum to be bound by ID from a request parameter
     * @param registry Registry of types eligible for data binding
     * @param enumClass Class of the enum
     */
    private registerEnum(PropertyEditorRegistry registry, Class<? extends Enum<?>> enumClass) {

        boolean hasRequiredMethod = enumClass.metaClass.methods.any {MetaMethod method ->
            method.isStatic() && method.name == REQUIRED_METHOD_NAME && method.parameterTypes.size() == 1
        }

        if (!hasRequiredMethod) {
            throw new MissingMethodException(REQUIRED_METHOD_NAME, enumClass, [String].toArray())
        }
        registry.registerCustomEditor(enumClass, new EnumEditor(enumClass))
    }
}

Шаг 3: Сообщите Spring о реестре выше, указав следующий Spring bean в grails-app/conf/spring/resources.grooovy

customPropertyEditorRegistrar(CustomPropertyEditorRegistrar)

Ответ 2

Таким образом, привязка по умолчанию привязывается к имени Enum, а не к отдельно заданному свойству Enum. Вы можете создать свой собственный PropertyEditor, как вы упомянули, или выполните аналогичную работу:

class MyCommand {
 String ratingId
    Rating getRating() {
     return Rating.getEnumFromId(this.ratingId)
    }
    static constraints = {
     ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
    }
}