h:selectManyCheckbox で enum を使用する
-
26-09-2019 - |
質問
enum値を使用したい <h:selectManyCheckbox>
. 。チェックボックスには正しく値が入力されますが、いくつかの値を選択して送信すると、そのランタイム タイプは次のようになります。 String
, 、列挙型ではありません。私のコード:
<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
UserController クラス (SecurityRole は列挙型):
public SelectItem[] getRolesSelectMany() {
SelectItem[] items = new SelectItem[SecurityRole.values().length];
int i = 0;
for (SecurityRole role : SecurityRole.values()) {
items[i++] = new SelectItem(role, role.toString());
}
return items;
}
public List<SecurityRole> getRoles() {
getCurrent().getRoles();
}
public void setRoles(List<SecurityRole> roles) {
getCurrent().setRoles(roles);
}
JSF が setRoles メソッドを呼び出すと、それには enum 型ではなく String 型のリストが含まれます。何か案は?ありがとう!
解決
この問題は特に列挙型に関連するものではありません。他の人でも同じ問題が発生するでしょう List
JSF にコンバーターが組み込まれている型 (例: List<Integer>
, List<Double>
, 、など。
問題は、EL が実行時に動作し、ジェネリック型情報が実行時に失われることです。したがって、本質的に、JSF/EL はパラメータ化された型について何も知りません。 List
そしてデフォルトは String
明示的に指定されていない限り、 Converter
. 。理論的には、次の助けを借りて厄介なリフレクション ハックを使用することは可能でした。 ParameterizedType#getActualTypeArguments()
, しかし、JSF/EL 開発者にはこれを行わない理由があるかもしれません。
これにはコンバータを明示的に定義する必要があります。JSF にはすでにビルトインが同梱されているため、 EnumConverter
(実行時に列挙型を指定する必要があるため、この特定のケースではスタンドアロンでは使用できません)、次のように拡張することもできます。
package com.example;
import javax.faces.convert.EnumConverter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="securityRoleConverter")
public class SecurityRoleConverter extends EnumConverter {
public SecurityRoleConverter() {
super(SecurityRole.class);
}
}
そして、次のように使用します。
<h:selectManyCheckbox value="#{userController.roles}" converter="securityRoleConverter">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
または
<h:selectManyCheckbox value="#{userController.roles}">
<f:converter converterId="securityRoleConverter" />
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
もう少し一般的な (そして巧妙な) 解決策は、列挙型をコンポーネント属性として保存することです。
package com.example;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
@FacesConverter(value="genericEnumConverter")
public class GenericEnumConverter implements Converter {
private static final String ATTRIBUTE_ENUM_TYPE = "GenericEnumConverter.enumType";
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value instanceof Enum) {
component.getAttributes().put(ATTRIBUTE_ENUM_TYPE, value.getClass());
return ((Enum<?>) value).name();
} else {
throw new ConverterException(new FacesMessage("Value is not an enum: " + value.getClass()));
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Object getAsObject(FacesContext context, UIComponent component, String value) {
Class<Enum> enumType = (Class<Enum>) component.getAttributes().get(ATTRIBUTE_ENUM_TYPE);
try {
return Enum.valueOf(enumType, value);
} catch (IllegalArgumentException e) {
throw new ConverterException(new FacesMessage("Value is not an enum of type: " + enumType));
}
}
}
あらゆる種類で使えます List<Enum>
コンバーター ID を使用する genericEnumConverter
. 。のために List<Double>
, List<Integer>
, など、組み込みコンバータを使用することになるでしょう。 javax.faces.Double
, javax.faces.Integer
等々。ちなみに、組み込みの Enum コンバータは、ターゲットの enum 型を指定できないため、適切ではありません ( Class<Enum>
)ビュー側から。JSFユーティリティライブラリ オムニフェイス まさにこのコンバーターを提供します 箱から出して.
通常の場合は、 Enum
プロパティ、ビルトイン EnumConverter
もう十分です。JSF は、適切なターゲット列挙型を使用して自動的にそれをインスタンス化します。
他のヒント
いくつかの場合にはの一覧するはちょうど同様に、アレイの SomeType [] をすることができ、この場合、明示的な変換は必要とされない。
ジェネリック消去古いものを壊すことなく、言語にジェネリックを置くの巧妙な方法でしたが、今私たちはその決定の結果で永遠に生きる...