Доступ к значению Enum с помощью EL с JSTL

104

У меня есть Enum под названием Status, определенный как таковой:

public enum Status { 

    VALID("valid"), OLD("old");

    private final String val;

    Status(String val) {
        this.val = val;
    }

    public String getStatus() {
        return val;
    }

}

Я хотел бы получить доступ к значению VALIDиз тега JSTL. В частности, testатрибут <c:when>тега. Например

<c:when test="${dp.status eq Status.VALID">

Я не уверен, возможно ли это.

IaCoder
источник

Ответы:

112

Работает простое сравнение со строкой:

<c:when test="${someModel.status == 'OLD'}">
Александр Васильев
источник
11
Для тех, кому требуется источник: это указано (например) в разделе 1.17 «Спецификации языка выражений, версия 2.2», которая является частью JSR-245 .
меритон 06
4
Спецификация JavaServer Pages ™ версии 2.0 в JSP.2.3.5.7 гласит: «• Если A или B являются String, приведите A и B к String, сравните лексически»
Роланд Иллиг
11
Но вы теряете преимущество наличия перечисления: это может привести к обременительным недоразумениям, если перечисление однажды изменится. Обычно, если я обнаруживаю, что меняю перечисление, я чувствую себя в полной безопасности, и, вероятно, я бы не вспомнил эту ссылку на строку-перечисление в этом представлении ...
действительно хорошо
1
Это сравнивается с toString перечисления? Поэтому, если вы переопределяете toString (например, вам нужно понятное отображаемое имя), вам нужно убедиться, что вы также изменили значение, с которым сравнивается.
Руперт Мэдден-Эбботт
1
FWIW, сегодня на моей Java 8 (IBM Java для WebSphere, если это важно), похоже, это не работает. Кажется, это работает только при сравнении со строковым значением, которое здесь будет строчным "старым"
dbreaux
54

При использовании Spring MVC может быть полезен язык выражений Spring (SpEL):

<spring:eval expression="dp.status == T(com.example.Status).VALID" var="isValid" />
<c:if test="${isValid}">
   isValid
</c:if>
Джеймс
источник
1
Кажется, это не работает для внутренних перечислений? Вызвано: org.springframework.expression.spel.SpelEvaluationException: EL1005E: (pos 0): Тип не найден 'my.package.model.EngagementRequest.EngagementStatus'
Эдди
4
Попробуйте использовать my.package.model.EngagementRequest $ EngagementStatus
Джеймс
Хорошая особенность этого решения заключается в том, что вы получаете сообщение об ошибке, если в вашем выражении есть ошибка, что не всегда происходит с <c:if>и <c:when>(они терпят неудачу).
vegemite4me
41

У вас есть 3 варианта, ни один из которых не идеален:

  1. Вы можете использовать скриптлет в testатрибуте:

    <c:when test="<%= dp.getStatus() == Status.VALID %>">

    Здесь используется перечисление, но также используется скриптлет, что не является «правильным» способом в JSP 2.0. Но самое главное, это не работает, если вы хотите добавить еще одно условие к тому же самому whenusing ${}. А это означает, что все переменные, которые вы хотите протестировать, должны быть объявлены в скриптлете или сохранены в запросе или сеансе ( pageContextпеременная недоступна в .tagфайлах).

  2. Вы можете сравнить со строкой:

    <c:when test="${dp.status == 'VALID'}">

    Это выглядит чистым, но вы вводите строку, которая дублирует значение перечисления и не может быть проверена компилятором. Поэтому, если вы удалите это значение из перечисления или переименуете его, вы не увидите, что эта часть кода больше не доступна. Вы в основном должны каждый раз выполнять поиск / замену кода.

  3. Вы можете добавить каждое из используемых значений перечисления в контекст страницы:

    <c:set var="VALID" value="<%=Status.VALID%>"/>

    а затем вы можете сделать это:

    <c:when test="${dp.status == VALID}">

Я предпочитаю последний вариант (3), хотя он также использует скриптлет. Это потому, что он использует его только тогда, когда вы устанавливаете значение. Позже вы можете использовать его в более сложных выражениях EL вместе с другими условиями EL. В варианте (1) нельзя использовать скриптлет и выражение EL в testатрибуте одного whenтега.

Мэтт
источник
1
Что касается варианта 2, компилятор не может его проверить, но во время выполнения строка будет преобразована в перечисление, с помощью Enum.valueOf(Class<T> enumType, String name)которого будет срабатывать, ELExceptionесли перечисление не имеет константы с таким именем. Выражение не всегда будет ложным.
Перезагрузка
23

Итак, чтобы полностью решить мою проблему, мне нужно было сделать следующее:

<% pageContext.setAttribute("old", Status.OLD); %>

Тогда я смог:

<c:when test="${someModel.status == old}"/>...</c:when>

который работал, как ожидалось.

IaCoder
источник
12
использование скриптлетов - плохой стиль.
Евгений Ретунский
13

Вот еще две возможности:

Константы JSP EL 3.0

Если вы используете EL версии не ниже 3.0, вы можете импортировать константы на свою страницу следующим образом:

<%@ page import="org.example.Status" %>
<c:when test="${dp.status eq Status.VALID}">

Однако некоторые IDE этого еще не понимают (например, IntelliJ ), поэтому вы не получите никаких предупреждений, если сделаете опечатку, до времени выполнения.

Это был бы мой предпочтительный метод, когда он получит надлежащую поддержку IDE.

Вспомогательные методы

Вы можете просто добавить геттеры в свое перечисление.

public enum Status { 
  VALID("valid"), OLD("old");

  private final String val;

  Status(String val) {
    this.val = val;
  }

  public String getStatus() {
    return val;
  }

  public boolean isValid() {
    return this == VALID;
  }

  public boolean isOld() {
    return this == OLD;
  }
}

Затем в вашем JSP:

<c:when test="${dp.status.valid}">

Это поддерживается во всех IDE и также будет работать, если вы еще не можете использовать EL 3.0. Это то, что я делаю сейчас, потому что это сохраняет всю логику в моем enum.

Также будьте осторожны, если переменная, хранящая перечисление, может иметь значение null. Вам нужно сначала проверить это, если ваш код не гарантирует, что он не равен нулю:

<c:when test="${not empty db.status and dp.status.valid}">

Я думаю, что этот метод превосходит те, в которых вы устанавливаете промежуточное значение в JSP, потому что вам нужно делать это на каждой странице, где вам нужно использовать перечисление. Однако с этим решением вам нужно только один раз объявить получатель.

Руперт Мэдден-Эбботт
источник
2
Часть «Константы JSP EL 3.0» должна быть принятым ответом, поскольку это стандартный способ достижения требуемого результата с использованием встроенных функций. Кстати, InteliJ IDEA (по крайней мере, Ultimate версии 2017.2.4) поддерживает его прямо из коробки и показывает список доступных констант при ${MyEnum.}вводе, ставит курсор сразу после точки и нажимает, Ctrl+Spaceчтобы отобразить предложения.
izogfif
[ ОБНОВЛЕНИЕ ] Похоже, IntelliJ IDEA уже устранила проблему, упомянутую в этом ответе.
informatik01
10

Для этого делаю следующее:

<c:set var="abc">
    <%=Status.OLD.getStatus()%>
</c:set>

<c:if test="${someVariable == abc}">
    ....
</c:if>

Выглядит некрасиво, но работает!

Экстремальный байкер
источник
3

У меня нет ответа на вопрос Корнеля, но есть замечание по поводу других примеров скриптов. Большинство выражений неявно доверяют свойству toString(), но Enum.valueOf()ожидает, что значение, полученное из / соответствует Enum.name()свойству. Поэтому следует использовать, например:

<% pageContext.setAttribute("Status_OLD", Status.OLD.name()); %>
...
<c:when test="${someModel.status == Status_OLD}"/>...</c:when>
Eremmel
источник
2

Добавьте в перечисление метод, например:

public String getString() {
    return this.name();
}

Например

public enum MyEnum {
    VALUE_1,
    VALUE_2;
    public String getString() {
        return this.name();
    }
}

Тогда вы можете использовать:

<c:if test="${myObject.myEnumProperty.string eq 'VALUE_2'}">...</c:if>
Декан
источник
1

При использовании инфраструктуры MVC я помещаю в свой контроллер следующее.

request.setAttribute(RequestParameterNamesEnum.INBOX_ACTION.name(), RequestParameterNamesEnum.INBOX_ACTION.name());

Это позволяет мне использовать на моей странице JSP следующее.

<script> var url = 'http://www.nowhere.com/?${INBOX_ACTION}=' + someValue;</script>

Его также можно использовать при сравнении

<c:when test="${someModel.action == INBOX_ACTION}">

Я предпочитаю вводить строковый литерал.

Электроника
источник
1
<%@ page import="com.example.Status" %>

1. ${dp.status eq Title.VALID.getStatus()}
2. ${dp.status eq Title.VALID}
3. ${dp.status eq Title.VALID.toString()}
  • Поместите импорт вверху в заголовок страницы JSP
  • Если вы хотите работать с методом getStatus , используйте # 1
  • Если вы хотите работать с самим элементом перечисления , используйте # 2 или # 3
  • Вы можете использовать == вместо eq
Мехди
источник
-1

Я вообще считаю плохой практикой смешивать код java с файлами jsps / tag. Использование 'eq' должно помочь:

<c:if test="${dp.Status eq 'OLD'}">
  ...
</c:if>
Eclatante
источник
3
Так что это плохая практика - использовать ==вместо eq? Они оба делают одно и то же, так что здесь нет никакого «трюка».
BalusC
Конечно, я не делал заявления относительно использования eq vs ==. Многие ответы на этот вопрос включают в себя вставку кода Java в файлы jsp или тегов, что может быть костылем. Я предпочитаю хранить бизнес-логику в java-коде (где ее можно легко и тщательно тестировать) отдельно от логики отображения в JSP.
Eclatante
7
Мне кажется такой же плохой практикой вставлять магические строки в ваш JSP, которые не могут быть проверены компилятором, когда вы хотите реорганизовать свои перечисления. Похоже, ни с одной стороны нет хорошего решения этой проблемы.
Lyle
-1

Я так делаю, когда есть много точек ...

public enum Status { 

    VALID("valid"), OLD("old");

    private final String val;

    Status(String val) {
        this.val = val;
    }

    public String getStatus() {
        return val;
    }

    public static void setRequestAttributes(HttpServletRequest request) {
        Map<String,String> vals = new HashMap<String,String>();
        for (Status val : Status.values()) {
            vals.put(val.name(), val.value);
        }
        request.setAttribute("Status", vals);
    }

}

JSP

<%@ page import="...Status" %>
<% Status.setRequestAttributes(request) %>

<c:when test="${dp.status eq Status.VALID}">
...
HS Шин
источник
-2

В классе Java:

    public class EnumTest{
    //Other property link
    private String name;
    ....

        public enum Status {
                ACTIVE,NEWLINK, BROADCASTED, PENDING, CLICKED, VERIFIED, AWARDED, INACTIVE, EXPIRED, DELETED_BY_ADMIN;
            }

        private Status statusobj ;

    //Getter and Setters
}

Итак, теперь созданы POJO и enum obj. Теперь EnumTest вы установите в объект сеанса, используя класс сервлета или контроллера session.setAttribute ("enumTest", EnumTest);

На странице JSP

<c:if test="${enumTest.statusobj == 'ACTIVE'}">

//TRUE??? THEN PROCESS SOME LOGIC
Паван
источник