使用JSF转换器输出时,使用指定值替换null或空字符串

Tin*_*iny 12 string null jsf converter

以下是一个转换器,主要用于修剪前导和尾随空格,并用一个空格替换句子中的单词或文本之间的多个空格.现在修改转换器以null使用"不可用" 替换或清空字符串(如果需要,可以动态本地化).

@FacesConverter(forClass = String.class)
public class StringTrimmer implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        return Boolean.TRUE.equals(component.getAttributes().get("skipConverter")) ? value : value == null ? null : value.trim().replaceAll("\\s+", " ");
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return Boolean.TRUE.equals(component.getAttributes().get("skipConverter")) ? value == null ? null : value.toString() : value == null || ((String) value).trim().length() == 0 ? "Not available" : ((String) value).trim().replaceAll("\\s+", " ");
    }
}
Run Code Online (Sandbox Code Playgroud)

由于未调用转换器,因此当模型值null基于前一个问题时,com.sun.faces.renderkit.html_basic.TextRenderer已扩展以调用转换器,当关联模型中的属性值为时null.

public final class HtmlBasicRenderer extends TextRenderer {

    @Override
    public String getCurrentValue(FacesContext context, UIComponent component) {

        if (component instanceof UIInput) {
            Object submittedValue = ((UIInput) component).getSubmittedValue();

            if (submittedValue != null) {
                return submittedValue.toString();
            }
        }

        return getFormattedValue(context, component, getValue(component));
    }
}
Run Code Online (Sandbox Code Playgroud)

以下条件测试已被移除,以使getFormattedValue()方法可以被调用,即使null遇到值.

Object currentObj = getValue(component);

if (currentObj != null) {
    currentValue = getFormattedValue(context, component, currentObj);
}
Run Code Online (Sandbox Code Playgroud)

这已经注册faces-config.xml如下.

<render-kit>
    <renderer>
        <component-family>javax.faces.Output</component-family>
        <renderer-type>javax.faces.Text</renderer-type>
        <renderer-class>com.example.renderer.HtmlBasicRenderer</renderer-class>
    </renderer>
</render-kit>
Run Code Online (Sandbox Code Playgroud)

当目标模型中的属性值返回时,StringTrimmer仍未调用转换器(getAsString())null.

#{empty bean.value ? 'Not available' : bean.value}在整个应用程序中随处可见的有条件测试都是疯狂的.有什么建议吗?

这是Mojarra 2.2.12.


更新:

转换的值可用,当getFormattedValue()方法中返回空字符串的其中一个返回语句""(如果currentValuenull)被修改为在调用时返回转换后的值

javax.?faces.?convert.?Converter.getAsString(FacesContext context, UIComponent component, Object value)
Run Code Online (Sandbox Code Playgroud)

在那个方法里面getFormattedValue().

因此,以下,

if(currentValue == null) {
    return "";
}
Run Code Online (Sandbox Code Playgroud)

需要更换,

if (currentValue == null) {
    converter = Util.getConverterForClass("".getClass(), context);
    return converter == null ? "" : converter.getAsString(context, component, currentValue);
}
Run Code Online (Sandbox Code Playgroud)

(需要建议).

Bal*_*usC 5

首先,aConverter从来没有意图强制执行“默认值”。

无论什么问题,无论您在 中做什么getAsString(),您都应该保证当您将结果传递回 时,String可以将结果转换回原始值。你的转换器不会这样做。尽管您不太可能使用它,但从技术上讲,需要修改转换器以将确切的字符串转换回. 换句话说,您的转换器必须以这样的方式设计:和可以在无限循环中成功地传递彼此的结果,并每次都返回相同的结果。ObjectgetAsObject()"Not available"nullgetAsObject()getAsString()

至于强制执行默认值的具体功能要求,Converter您应该在模型或视图中执行此操作,而不是在 a 中,具体取决于实际默认值的来源。在您的特定情况下,当没有这样的值时,您只想在用户界面中有一个默认的占位符文本/标签。这属于视图。

#{empty bean.value ? 'Not available' : bean.value}像整个应用程序中的任何地方一样在 EL 中进行条件测试是疯狂的。

我完全同意,如果您已经完成了应用程序开发的一半并且到处都有数百个应用程序,那么这是疯狂的。然而,如果你从一开始就考虑到了这一点,那么这并不是疯狂。如果你没有,那么好吧,吸取教训,咬紧牙关并定期相应地修复代码。不错的 IDE 具有基于正则表达式的查找和替换,这可以对此有所帮助。每个人都曾经历过这种疯狂的事,甚至包括我自己。要减少样板文件,请将其包装在 EL 函数或标记文件中。

至于Converter模型值为 时不被调用的具体问题null,我个人完全同意这是意外行为,这曾经被报告为Mojarra issues 630。这后来被关闭为 WONTFIX,因为它导致测试用例失败,并且毕竟最好报告为 JSF 规范问题。这是在JSF 规范第 475 期中完成的(已经在 J​​SF 1.2 期间)。我对此检查了 MyFaces 2.2.9,它也不会触发转换器,因此暴露了相同的规范问题。

然而,技术问题是可以理解的。值null没有合理的getClass(),因此无法通过这种方式通过值的类查找转换器。仅当转换器在组件上显式注册时它才起作用。

<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />
Run Code Online (Sandbox Code Playgroud)

当值为空字符串时它应该可以工作,这在扩展 Mojarra 的自定义渲染器中""实现相对简单。getValue()TextRenderer

@Override
protected Object getValue(UIComponent component) {
    Object value = super.getValue(component);
    return (value != null) ? value : "";
}
Run Code Online (Sandbox Code Playgroud)

然而,当我自己在这里尝试时,仍然失败。结果是,当该值是 的实例时,按类转换器将被完全跳过String。仅当转换器在组件上显式注册时,它仍然有效。这很可能是在实现JSF 1.2规范问题 131期间的疏忽(在此版本之前,String.class根本不支持 的转换器,并且此问题仅针对解码修复了它,而不是针对编码)。

这可以在同一个自定义渲染器中被覆盖(使用上面的getValue()覆盖),并使用下面的覆盖getFormattedValue()来显式查找转换器。

@Override
protected String getFormattedValue(FacesContext context, UIComponent component, Object currentValue) throws ConverterException {
    Converter converter = ((UIOutput) component).getConverter();

    if (converter == null) {
        converter = context.getApplication().createConverter(currentValue.getClass());
    }

    return super.getFormattedValue(context, component, "".equals(currentValue) ? null : currentValue, converter);
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要检查,因为您已仅在/组件系列/类型UIInput上显式注册了自定义渲染器(即它仅在组件上运行)。javax.faces.Outputjavax.faces.Text<h:outputText>

尽管如此,最好的解决方案仍然是为此创建一个 EL 函数或标记文件。

<h:outputText value="#{empty bean.value ? 'Not available' : bean.value}" />
Run Code Online (Sandbox Code Playgroud)
<h:outputText value="#{of:coalesce(bean.value, 'Not Available')}" />
Run Code Online (Sandbox Code Playgroud)
<h:outputText value="#{of:coalesce(bean.value, i18n.na)}" />
Run Code Online (Sandbox Code Playgroud)
<h:outputText value="#{your:value(bean.value)}" />
Run Code Online (Sandbox Code Playgroud)
<your:text value="#{bean.value}" />
Run Code Online (Sandbox Code Playgroud)