How do I write a converter for a list of items of class A in JSF2? I have written a converter for class A, but the items show up using the default toString() function: “A@hashcode”.
I need to use a converter rather than a backing bean method so that validation can take place (Hibernate Validator).
more info
This is how I use the list:
<h:inputText id="destinations" value="#{rule.destinations}" converter="gr.panayk.vinyls.Destination"/>
Where #{rule.destinations} is of List<Destination> type. I am expecting a comma separated list of converted Destinations.
solution
I attach the List converter that BalusC proposed.
@FacesConverter(value="gr.panayk.vinyls.converter.DestinationList")
public class DestinationListConverter implements Converter
{
@Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String values)
{
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
final List<Destination> result = new ArrayList<Destination>();
for (String value : values.split(",", -1))
{
final String trimmedValue = value.trim();
final Set<ConstraintViolation<Destination>> violations = validator.validateValue(Destination.class, "data", trimmedValue);
if (!violations.isEmpty())
{
throw new ConverterException(new FacesMessage(violations.iterator().next().getMessage()));
}
result.add(new Destination(trimmedValue));
}
final Set<ConstraintViolation<Rule>> violations = validator.validateValue(Rule.class, "destinations", result);
if (!violations.isEmpty())
{
throw new ConverterException(new FacesMessage(violations.iterator().next().getMessage()));
}
return result;
}
@Override
public String getAsString(final FacesContext context, final UIComponent component, final Object value)
{
if (value instanceof List<?>)
{
final StringBuffer result = new StringBuffer();
final List<?> list = (List<?>) value;
for (int i = 0; i < list.size()-1; i++)
{
if (list.get(i) instanceof Destination)
{
result.append(((Destination) list.get(i)).getData());
result.append(", ");
}
else
{
throw new IllegalArgumentException( "Cannot convert " + value + " object to Destination in DestinationConverter." );
}
}
if (!list.isEmpty())
{
if (list.get(list.size()-1) instanceof Destination)
{
result.append(((Destination) list.get(list.size()-1)).getData());
}
else
{
throw new IllegalArgumentException( "Cannot convert " + value + " object to Destination in DestinationConverter." );
}
}
return result.toString();
}
else
{
throw new IllegalArgumentException( "Cannot convert " + value + " object to List in DestinationConverter." );
}
}
}
That can happen if you didn’t explicitly declare the converter on the component. In for example
<h:selectManyCheckbox>and<h:selectManyListbox>explicitly declaring the converter is mandatory as all JSF/EL knows is that the value is of typeList, notList<A>(generic types are lost during runtime). If you don’t declare a converter, then the values will be treated asString(as that’s what HTML output and HTTP request parameter values default to).E.g.
with
Explicitly declaring the above converter is not necessary when you’re using single-item inputs like
<h:selectOneMenu>as theforClasswould match it anyway.