【问题标题】:Replace null or empty strings with a specified value while outputting using a JSF converter使用 JSF 转换器输出时用指定值替换空字符串或空字符串
【发布时间】:2016-05-29 04:09:40
【问题描述】:

以下是一个转换器,主要用于修剪前导和尾随空格,并用单个空格替换句子或文本中单词之间的多个空格。转换器现已修改为将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+", " ");
    }
}

由于未调用转换器,当模型值为 null 基于 the previous question 时,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));
    }
}

following 条件测试已被移除,因此即使遇到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>

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

在整个应用程序的任何地方都像#{empty bean.value ? 'Not available' : bean.value} 这样在EL 中进行条件测试是很疯狂的。有什么建议吗?

这是 Mojarra 2.2.12。


更新:

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

javax.​faces.​convert.​Converter.getAsString(FacesContext context, UIComponent component, Object value)

在那个方法getFormattedValue().

因此,following

if(currentValue == null) {
    return "";
}

需要替换为,

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

(需要建议)。

【问题讨论】:

  • 您在哪个组件中尝试此操作(因为您将其创建为通用 forClass =String.class)如果只是在 outputText 中,我会使用 simpleCustom 组件并搜索/替换默认的 jsf 或 primeFaces有了这个。
  • 基本上是&lt;h:outputText&gt;。可以为选定的可选字段创建自定义组件,但需要仔细记住哪些字段可能包含空值或空值。
  • 好吧,你隐含地声明(通过使用'forClass = String.class')它适用于每个字符串?因此,无需记住事情。始终使用自定义 &lt;tiny:outputText&gt; ;-)
  • 到处使用自定义组件?我将&lt;h:outputText&gt; 组件与&lt;my:convertBigDecimal percent="true" groupingUsed="true" locale="#{localeBean.locale}"/&gt;&lt;my:convertBigDecimal currency="#{currencyRateBean.currency}" groupingUsed="true" locale="#{localeBean.locale}"/&gt; 之类的转换器一起使用。因此,它需要根据目标&lt;h:outputText&gt; 组件所指的模型值类型来创建不同的自定义组件。

标签: string jsf null converter


【解决方案1】:

首先,Converter 绝不打算强制执行“默认值”。

不管是什么问题,无论你在getAsString() 中做什么,你都应该保证当你通过getAsObject() 传回时,得到的String 可以转换回原来的Object。你的转换器不这样做。即使您不太可能使用它,但从技术上讲,需要修改转换器以将确切的字符串 "Not available" 转换回 null。换句话说,您的转换器必须设计成getAsObject()getAsString() 可以在无限循环中成功传递彼此的结果,并且每次都返回相同的结果。

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

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

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

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

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

<h:outputText value="#{bean.potentiallyNullValue}" converter="stringTrimmer" />

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

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

但是,当我在这里自己尝试时,它仍然失败。结果是,当值是String 的实例时,将完全跳过转换器类。只有当转换器在组件上显式注册时,它仍然有效。这很可能是在为 JSF 1.2 实施 spec issue 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);
}

请注意,您不需要检查UIInput,因为您已在javax.faces.Output/javax.faces.Text 组件系列/类型上明确注册了您的自定义渲染器(即它只会在&lt;h:outputText&gt; 组件上运行) .

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

<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}" />

【讨论】:

  • currentValue里面getFormattedValue()的值随时可以是null。因此,currentValue.getClass() 将在渲染器本身中抛出 java.lang.NullPointerException,如果 currentValuenull
  • 如果您也覆盖 getValue() 则不会。我可能不够清楚,我澄清了答案。
  • 在另一种类型的转换器中还有另一个后果。我有一个BigDecimal 转换器,如previous question。如果数据库中BigDecimal 类型的值是null,则在getAsString() 中检索到的modelValue 是一个空字符串-System.out.println("modelValue : " + ((String)modelValue).isEmpty());getAsString() 中返回true。这会达到throw new ConverterException("Message");,如getAsString() 方法内的if-else-if-else 阶梯中指定的那样。 (modelValue 应该是 null,如果它在 DB 中是 null
  • 我明白了。这可以通过在 getFormattedValue() 中从空字符串返回到 null 来欺骗。我改进了答案。但归根结底,我希望您了解转换器并不是完全适合此目的的工具。
  • 在 JSF 2.3 / Java EE 8 中是否有机会使用默认值调用转换器?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-12
  • 2022-12-09
  • 2022-01-03
  • 2014-11-12
  • 2012-08-09
  • 1970-01-01
相关资源
最近更新 更多