I want to use enum values in a <h:selectManyCheckbox>
. The checkboxes get populated correctly, however, when selecting some values and submitting them, their runtime type is String
, and not enum. My code:
<h:selectManyCheckbox value="#{userController.roles}" layout="pageDirection">
<f:selectItems value="#{userController.rolesSelectMany}" />
</h:selectManyCheckbox>
UserController class (SecurityRole is an enum type):
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);
}
When JSF calls the setRoles method, it contains a list of type String, and not the enum type. Any ideas? Thanks!
In some cases the List could just as well be an array SomeType[], and in this case no explicit converter is needed.
Generic erasure was a clever way of putting generics into the language without breaking the old stuff, but now we live forever with the consequences of that decision...
This problem is not specifically related to enums. You would have the same problem with other
List
types for which JSF has builtin converters, e.g.List<Integer>
,List<Double>
, etcetera.The problem is that EL operates runtime and that generic type information is lost during runtime. So in essence, JSF/EL doesn't know anything about the parameterized type of the
List
and defaults toString
unless otherwise specified by an explicitConverter
. In theory, it would have been possible using nasty reflection hacks with help ofParameterizedType#getActualTypeArguments()
, but the JSF/EL developers may have their reasons for not doing this.You really need to explicitly define a converter for this. Since JSF already ships with a builtin
EnumConverter
(which isn't useable standalone in this particular case because you have to specify the enum type during runtime), you could just extend it as follows:And use it as follows:
or
A bit more generic (and hacky) solution would be to storing the enum type as component attribute.
It's useable on all kinds of
List<Enum>
using converter IDgenericEnumConverter
. ForList<Double>
,List<Integer>
, etc one would have used the builtin convertersjavax.faces.Double
,javax.faces.Integer
and so on. The builtin Enum converter is by the way unsuitable due to the inability to specify the target enum type (aClass<Enum>
) from the view side on. The JSF utility library OmniFaces offers exactly this converter out the box.Note that for a normal
Enum
property, the builtinEnumConverter
already suffices. JSF will instantiate it automagically with the right target enum type.