简体   繁体   中英

JSF: selectOneRadio converter problem

I'm having trouble using a selectOneRadio component. I get a NullPointerException in my Converter's getAsString. That exception is thrown before I even get to see that component.

This is how it looks like:

            <h:selectOneRadio id="bookA"
                value="#{bookHandler.compareBookA}">
                 <f:converter converterId="bookConverter" />
                <f:selectItems value="#{bookHandler.selectedBooks}"
                    var="book" itemLabel="#{book.shortname}" itemValue="#{book}" />
            </h:selectOneRadio>

The property compareBookA is an object of type Book.

This is the method that throws the NPE:

@Override
public String getAsString(FacesContext fc, UIComponent uic, Object value)
        throws ConverterException {
    Book book = (Book) value;
    if (book == null ) {
        throw new ConverterException("NPE...");
    }
    return book.getShortname();
}

I have also overwritten toString().

For some reason the component is rendered if I change it to selectManyCheckbox (and leave the rest like it is).

I'm using JSF 2 (MyFaces implementation) with Tomahawk on Tomcat.

Bonus question: Why do I need a converter in the first place? If I leave the converter away, the component is rendered, but I want to pass the chosen book to some action method and it won't be a Book then.

Any ideas? Thanks!

Apparently #{bookHandler.compareBookA} is just null .

You shouldn't throw ConverterException when Book is null , but just let it go.

return (book != null) ? book.getShortname() : null;

As to the bonus question, you need converters because the HTML response is basically one large string . All Java objects needs to be converted to string to get embedded in the HTML response. Also, the HTTP request parameters are by default strings. The request.getParameter() returns String . HTML/HTTP has totally no notion of how to handle and pass fullworthy Java objects.

See also:

A Converter provides a mechanism to convert a POJO into a String (HTML) representation and then convert that String back into an instance of that same POJO. In your example you don't want to throw an Exception but rather just return null when Book is null.

One thing that I often do in my Application is create a Generic converter that works for all my selectItems. This is easy if all of your Entities implement a common Interface with for example an id property.

public interface Entity extends Serializable {

    /**
     * Return the primary key for this entity
     * 
     * @return id
     */
    public BigInteger getId();

}

Then you can create a convention for how all Entitys should be converted to a String and back to an Object. I usually concatenate the classname and id and then Base64 encode them to convert the object to a String and then to reverse the transformation:

/*
 * (non-Javadoc)
 * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
 */
@SuppressWarnings("unchecked")
public Object getAsObject(FacesContext context, UIComponent component, String value) {

    Object objectValue = null;

    try {            
        BigInteger id = null;

        value = this.unhash(value);            
        String[] array = value.split(CLASS_KEY_SEPARATOR);

        if (array.length == 2) {
            id = new BigInteger(array[1]);
        }

        Collection<UIComponent> childComponents = component.getChildren();

        for (UIComponent childComponent : childComponents) {

            if (childComponent.getClass().equals(UISelectItems.class)) {

                Collection<Entity> values = 
                    (Collection<Entity>) childComponent.getValueExpression("value").getValue(FacesContext.getCurrentInstance().getELContext());

                for (Entity selectItemValue : values) {
                    if (id.equals(selectItemValue.getId())) {
                        return selectItemValue;
                    }
                }
            }
        }            

    } catch (Exception ex) {
        LOGGER.error(ex.getMessage(), ex);
    }

    return objectValue;
}

Your Entity structure may vary from mine but if you follow the basic pattern you have a converter that works for all the POJOs in your app represented as SelectItems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM