Converter JSF para SelectOneMenu

La principal característica de JSF (o, al menos, la que más me gusta) es la facilidad para enlazar atributos del bean controlador desde la vista xhtml. No obstante existe una limitación importante: en el estándar HTTP las claves y valores siempre serán cadenas porque así es como se transmiten. Sí, se puede serializar el objeto en base64, pero en casi cualquier circunstancia se debe huir de una salvajada así.

¿Cómo hacer, pues, que el valor de un control se enlace directamente con un objeto? Pues JSF provee para ello los converters. Bueno, permite que tú los programes, claro. Tampoco PrimeFaces, que es mi librería de componentes de eleción, incluye estos conversores. Así pues, he programado uno pequeño para los SelectOneMenu, los menús desplegables asemejables a comboboxes. Lo dejo aquí para referencia mía y por si puede servirle de algo a alguien.

/**
 * The Class SelectOneMenuConverter.
 */
@FacesConverter("selectOneMenuConverter")
public class SelectOneMenuConverter implements Converter {

	@Override
	public Object getAsObject(final FacesContext arg0, final UIComponent arg1, final String objectString) {
		if (objectString == null) {
			return null;
		}

		return fromSelect(arg1, objectString);
	}

	/**
	 * Serialize.
	 *
	 * @param object
	 *            the object
	 * @return the string
	 */
	private String serialize(final Object object) {
		if (object == null) {
			return null;
		}
		return object.getClass() + "@" + object.hashCode();
	}

	/**
	 * From select.
	 *
	 * @param currentcomponent
	 *            the currentcomponent
	 * @param objectString
	 *            the object string
	 * @return the object
	 */
	private Object fromSelect(final UIComponent currentcomponent, final String objectString) {

		if (currentcomponent.getClass() == UISelectItem.class) {
			final UISelectItem item = (UISelectItem) currentcomponent;
			final Object value = item.getValue();
			if (objectString.equals(serialize(value))) {
				return value;
			}
		}

		if (currentcomponent.getClass() == UISelectItems.class) {
			final UISelectItems items = (UISelectItems) currentcomponent;
			final List<Object> elements = (List<Object>) items.getValue();
			for (final Object element : elements) {
				if (objectString.equals(serialize(element))) {
					return element;
				}
			}
		}


		if (!currentcomponent.getChildren().isEmpty()) {
			for (final UIComponent component : currentcomponent.getChildren()) {
				final Object result = fromSelect(component, objectString);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

	@Override
	public String getAsString(final FacesContext arg0, final UIComponent arg1, final Object object) {
		return serialize(object);
	}

}

Básicamente, convierte cualquier objeto en una cadena codificada con el nombre de la clase y el hash. A la hora de recuperarlo, accede a la colección enlazada al valor de nuestro desplegable para buscar el objeto adecuado y así devolverlo. Ciertamente depende de que el método hashCode esté bien codificado y devuelva (casi con toda seguridad) valores distintos para objetos distintos, pero es una forma rápida de hacerlo y que funcionaría en casi toda circunstancia. Usarlo es tan fácil como:

<p:selectOneMenu id="utiSelector" converter="selectOneMenuConverter" value="#{miBean.miObjeto}">
	<f:selectItem itemValue="#{null}" noSelectionOption="true" />
	<f:selectItems value="#{miBean.listaObjetos}" var="objeto" itemValue="#{objeto}" itemLabel="#{objeto.etiqueta}" />
</p:selectOneMenu>

Algo que suelo hacer en los DTO o entidades es que el hashcode sea la clave, o el hashcode de ésta si no es numérica. Pero eso ya es otra historia.

15 opiniones en “Converter JSF para SelectOneMenu”

  1. el UISelectItem.class a que hace referencia? osea para que sirve y de donde se saca, pues porque creo que es otro fragmento de tu codigo

    1. Si no recuerdo mal, es la clase de J2EE 6 que modela el combobox. Deberías tenerla si tienes la dependencia correcta.

  2. Muchísimas gracias por tu código, en realidad me ha servido muchísimo, llevo 48 horas buscando la solución y ésta me ha servido, argumentando claro que la clave es el override del hasCode().

    A mi me iba perfecto los convertes con los autoComplete que si te setea el objeto directamente, sin embargo nunca se me ocurrió, hasta hoy, hacer lo mismo para el selectOneMenu y claro, lo que funcionó para el converter no ha funcionado para el selectOneMenu.

    Muchas gracias nuevamente.

        1. Por demanda. Estoy especializado en backend, así que me pasé a microservicios, e hice frontends en backbone por ejemplo. Actualmente una de las aplicaciones que mantengo para mi cliente tiene la parte de cliente en SWT, mucho más antiguo y obsoleto que JSF. Vueltas que da la vida.

  3. Excelente tu código me he pasado 4 días tratando de resolver de muchas maneras sin lograrlo. Tu código está excelente mil gracias

  4. Muchas gracias, me fue de gran utilidad. Llevaba varias horas atorado, el tema es que trataba de pasar un objeto y no me marcaba ningún error, pero no hacia nada. Al leer tu explicación me di cuenta de que no se puede pasar un objeto, implemente el Converter y funcionó!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.