あなたが経験している動作は完全に予期されたものです。さらに、HTTP の仕組みと同じように Java ジェネリックに関連しています。
問題
HTTP部分
問題は、HTTP の仕組みを完全には理解していないことです。送信ボタンを押してデータを送信すると、チェックボックス<h:selectManyCheckbox>
の束として生成された JSF タグのパラメーターが文字列として送信され、最終的にも文字列として取得されます。もちろん、JSF はモデル オブジェクト クラスを構築する方法を知りません。<input type="checkbox" name="..." value="userRoleAsString">
request.getParameter("checkboxName");
Role
ジェネリック部分
Java が下位互換性を提供するためにジェネリックの型消去を選択したという事実により、ジェネリック型に関する情報は基本的にコンパイル型のアーティファクトであり、実行時に失われます。そのため、実行時にList<Role>
消去をプレーンで古き良きものにしList
ます。また、EL が Java Reflection API を使用して式を処理したりメソッドを呼び出したりするランタイム言語である限り、実行時にそのような情報は利用できません。HTTP の部分を考慮して、JSF は最善を尽くして文字列オブジェクトをリストに割り当てます。これは暗黙的にできることのすべてです。JSF に別の方法を指示する場合は、明示的に行う必要があります。つまり、コンバーターを指定して、HTTP リクエストで期待されるオブジェクトのタイプを知る必要があります。
JSF の部分: 余波
JSFにはjavax.faces.Enum
コンバーターが用意されており、ELがリストのコンパイル時のジェネリック型を知っていれば、実際に機能しますRole
。しかし、それは知りません。複数選択がオブジェクトに対して行われる場合、またはにバインドされた値で のRole[] userRoles
ように一意の選択を使用した場合、コンバーターを提供する必要はありません。これらの例では、組み込みの enum コンバーターが自動的に呼び出されます。<h:selectOneMenu>
Role userRole
したがって、期待どおりに機能させるには、このリストが保持する値のタイプと、 からへ、またはその逆Converter
の変換を行う方法を JSF に「説明」するを提供する必要があります。Role
String
List<...>
これは、複数選択 JSF コンポーネント内のバインドされた値の場合に当てはまることに注意してください。
Stack Overflow の参照ポイント
問題を調査して解決した後、過去に誰も問題に直面したことがないのではないかと考え、ここで以前の回答を検索しました。当然のことながら、以前にも質問されましたが、もちろん問題は BalusC によって解決されました。以下に、最も価値のある 2 つの参照ポイントを示します。
テスト ケースと動作するコンバーターの 2 つの例
以下に、完全に理解できるようにテスト ケースを示します。3 番目の<h:selectManyCheckbox>
コンポーネントを除いて、すべてが期待どおりに動作します。問題を完全に排除するためにそれを完全に追跡するのはあなた次第です。
景色:
<h:form>
Many with enum converter
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles}" converter="roleEnumConverter">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Many with plain converter
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles2}" converter="roleConverter">
<f:selectItems value="#{q16433250Bean.allRoles2}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Without any converter
<!-- will NOT be mapped correctly with Role object, but with a default String instead -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles3}">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
Without any converter + array
<!-- will be mapped correctly with Role object -->
<h:selectManyCheckbox value="#{q16433250Bean.userRoles4}">
<f:selectItems value="#{q16433250Bean.allRoles}" var="role" itemLabel="#{role.name}" />
</h:selectManyCheckbox>
<br/>
<h:commandButton value="Submit" action="#{q16433250Bean.action}"/>
</h:form>
豆:
@ManagedBean
@RequestScoped
public class Q16433250Bean {
private List<Role> userRoles = new ArrayList<Role>();//getter + setter
private List<Role> userRoles2 = new ArrayList<Role>();//getter + setter
private List<Role> userRoles3 = new ArrayList<Role>();//getter + setter
private Role[] userRoles4;//getter + setter
public enum Role {
ADMIN("Admin"),
SUPER_USER("Super user"),
USER("User");
private final String name;
private Role(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public Role[] getAllRoles() {
return Role.values();
}
public String action() {
return null;
}
}
コンバーター:
@FacesConverter("roleEnumConverter")
public class RoleEnumConverter extends EnumConverter {
public RoleEnumConverter() {
super(Role.class);
}
}
と
@FacesConverter("roleConverter")
public class RoleConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if(value == null || value.equals("")) {
return null;
}
Role role = Role.valueOf(value);
return role;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (!(value instanceof Role) || (value == null)) {
return null;
}
return ((Role)value).toString();
}
}