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

String 使用JSF转换器输出时,用指定值替换空字符串或空字符串,string,jsf,null,converter,String,Jsf,Null,Converter,下面是一个转换器,主要用于修剪前导和尾随空格,并用单个空格替换句子或文本中单词之间的多个空格。转换器现在被修改为将null或空字符串替换为“不可用”(如果需要,可以动态本地化) 由于未调用转换器,因此当模型值基于com.sun.faces.renderkit.html_basic.textrender时,当关联模型中的属性值为null时,扩展了com.sun.faces.renderkit.html\u basic.textrender以调用转换器 public final class Html

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

由于未调用转换器,因此当模型值基于
com.sun.faces.renderkit.html_basic.textrender
时,当关联模型中的属性值为
null
时,扩展了
com.sun.faces.renderkit.html\u basic.textrender
以调用转换器

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));
    }
}
已删除条件测试,因此即使遇到
null
值,也可以调用
getFormattedValue()
方法

Object currentObj = getValue(component);

if (currentObj != null) {
    currentValue = getFormattedValue(context, component, currentObj);
}
这已在
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>
在该方法中
getFormattedValue()

因此,

需要用,

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

(需要建议)。

首先,
转换器从来不会强制执行“默认值”

不管问题是什么,无论您在
getAsString()
中执行什么操作,您都应该保证当您通过
getAsObject()
将结果
字符串传回时,可以将其转换回原始
对象。你的转换器不能做到这一点。尽管您不太可能使用它,但从技术上讲,转换器需要修改,以便将确切的字符串
“notavailable”
转换回
null
。换句话说,转换器的设计必须确保
getAsObject()
getastring()
能够在无限循环中成功地传递彼此的结果,并每次返回相同的结果

至于强制默认值的具体功能要求,您应该在模型或视图中执行,而不是在
转换器中执行,这取决于实际默认值来自何处。在您的特定情况下,您只需要在用户界面中没有默认占位符文本/标签的情况下就可以了。这属于视图

在整个应用程序中的任何地方都使用EL-like
{empty bean.value?'Not available':bean.value}
进行条件测试都是愚蠢的

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

至于当模型值为
null
时,
转换器
未被调用的具体问题,我个人完全同意这是意外行为,这曾被报道为。由于它导致了测试用例失败,所以它后来被命名为WONTFIX,毕竟最好作为JSF规范问题报告。这是按照(已经在JSF1.2中)完成的。我在上面检查了MyFaces2.2.9,它也不会触发转换器,因此暴露了相同的规范问题

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

<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />
然而,当我自己在这里尝试时,还是失败了。结果是,当值是
String
的实例时,将完全跳过converter by class。仅当转换器在组件上显式注册时,它仍然有效。这很可能是JSF 1.2实现过程中的一个疏忽(在此版本之前,
String.class
的转换器根本不受支持,此问题只针对解码而非编码进行了修复)

这可以在相同的自定义渲染器中被覆盖(使用上面的
getValue()
override)和下面的
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);
}
请注意,您不需要检查
UIInput
,因为您已经在
javax.faces.Output
/
javax.faces.Text
组件族/类型上明确注册了自定义呈现程序(即,它将仅在
组件上运行)

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

<h:outputText value="#{empty bean.value ? 'Not available' : bean.value}" />






如果只是在outputText中,您在哪个组件中尝试此操作(因为您将其创建为通用的
forClass=String.class
),我会使用simpleCustom组件并用它搜索/替换默认的jsf或primeFaces组件。它基本上是
。可以为选定的可选字段创建自定义组件,但需要仔细记住哪些字段可能包含null或空值。好吧,您可以隐式地声明(通过使用'forClass=String.class')每根绳子都是这样?那就不需要记忆了。始终使用自定义的
;-)到处使用自定义组件?我使用带有转换器的
组件,如
。因此,它需要根据目标
组件所引用的模型值类型创建不同的自定义组件。
getFormattedValue()
中的
currentValue
值可以在任何时候为
null
。因此,如果
currentValue
null
,则
currentValue.getClass()
将在渲染器本身中抛出
java.lang.NullPointerException
。如果重写
getValue,则不会抛出(
<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />
@Override
protected Object getValue(UIComponent component) {
    Object value = super.getValue(component);
    return (value != null) ? value : "";
}
@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);
}
<h:outputText value="#{empty bean.value ? 'Not available' : bean.value}" />
<h:outputText value="#{of:coalesce(bean.value, 'Not Available')}" />
<h:outputText value="#{of:coalesce(bean.value, i18n.na)}" />
<h:outputText value="#{your:value(bean.value)}" />
<your:text value="#{bean.value}" />