在Freemarker宏中模拟空参数

时间:2011-07-01 20:39:39

标签: freemarker

我正在使用Freemarker构建一个网站,并开始大量使用宏。我知道在Freemarker 2.3中,将一个空值作为参数传递给宏等同于根本不传递参数所以我创建了一个名为“null”的全局变量来模拟我的宏中的空检查:

<#assign null="NUL" />

现在在我的宏中我可以这样做:

<#maco doSomething param1=null>
  <#if param1 != null>
    <div>WIN!</div>
  </#if>
</#macro>

如果我想传递一个不是标量的参数,问题就出现了。例如,将List(在Freemarker中是SimpleSequence)传递给宏并检查我的null关键字会产生错误:

  

freemarker.template.TemplateException:   唯一的法律比较是介于两者之间   两个数字,两个字符串或两个   日期。左手操作数是一个   freemarker.template.SimpleSequence   右手操作数是一个   freemarker.template.SimpleScalar

我看了一下freemarker代码,我可以看到问题(ComparisonExpression.isTrue()):

if(ltm instanceof TemplateNumberModel && rtm instanceof TemplateNumberModel) { 
  ...
}
else if(ltm instanceof TemplateDateModel && rtm instanceof TemplateDateModel) {
  ...
}
else if(ltm instanceof TemplateScalarModel && rtm instanceof TemplateScalarModel) {
  ...
}
else if(ltm instanceof TemplateBooleanModel && rtm instanceof TemplateBooleanModel) {
  ...
}
// Here we handle compatibility issues
else if(env.isClassicCompatible()) {
  ...
}
else {
  throw new TemplateException("The only legal comparisons...", env);
}

所以我能想到的唯一解决方案是将isClassicCompatible设置为true,我认为它会在两个对象上调用toString()并比较结果。但是,文档明确指出应该重写任何依赖旧功能的内容。

我的问题是,是否有一个不依赖于弃用功能的解决方案?

5 个答案:

答案 0 :(得分:18)

null引用在FreeMarker中是设计错误。由于您提到的原因,定义自定义空值 - 这是一个字符串 - 不是一个好主意。应该使用以下结构:

  • 宏和函数参数可以有一个默认值,因此调用者可以省略它们
  • 要检查变量是否为null,您应使用??运算符:<#if (name??)>
  • 当您使用null变量时,应使用!运算符指定默认值:name!"No name"
  • 要检查序列(或字符串)是否为空,请使用内置的?has_content<#if (names?has_content)>

您可以在宏中指定一个空序列作为默认参数值,只需测试它是否为空。

答案 1 :(得分:9)

这就是我所做的,这似乎适用于大多数情况:

默认值应为空字符串,空值检查应为?has_content

<#macro someMacro optionalParam="" >
    <#if (optionalParam?has_content)>
        <#-- NOT NULL -->
    <#else>
        <#-- NULL -->
    </#if>
</#macro>

答案 2 :(得分:3)

同事建议空检查功能可能是最优雅的解决方案:

<#assign null="NUL" />

<#function is_null variable>
  <#if variable?is_string & variable == null>
    <#return true />
  <#else>
    <#return false />
  </#if>
</#function>

运行这个想法,我在mailing list中找到了另一个可能的解决方案,即通过扩展TemplateMethodModelEx在Java中创建一个空值检查函数。然后我可以将它直接插入到我的模型图中,并在我的所有模板中全局使用它。生成的Freemarker代码非常干净:

<#maco doSomething param1=null>
  <#if !is_null(param1)>
    <div>WIN!</div>
  </#if>
</#macro>

这似乎是模拟宏内部空参数的最佳方法。

答案 3 :(得分:1)

您可以先检查它是否不是标量:

<#if !param1?is_scalar || param1 != null)>

答案 4 :(得分:0)

实际上,我不明白为什么不允许使用null。我有一个Java函数,可以使用给定的可选/可空参数(如 height )来检索特定的图像URL。

我使用以下解决方法为其编写了一个宏:

Java函数

/**
 * Converts the given value to <code>null</code> if it equals the default
 * value, else returns it directly.
 *
 * @param value
 * @param defaultValue
 *
 * @return
 */
public static Object nullIfDefault(final Object value, final Object defaultValue)
{
    if (value != null && value.equals(defaultValue))
        return null;
    return value;
}

/**
 * Converts the given value to <code>null</code> if it equals an empty
 * string, else returns it directly.
 *
 * @param value
 *
 * @return
 */
public static Object nullIfEmpty(final Object value)
{
    return ObjectUtils.nullIfDefault(value, "");
}

导入静态功能

<#assign ObjectUtils=statics['com.example.ObjectUtils']/>

<#macro compEntityImage imageRetriever entity height="" width="">

    <img src="${imageRetriever.getMediaFileUrl(entity, 
        ObjectUtils.nullIfEmpty(height), 
        ObjectUtils.nullIfEmpty(width))}">

</#macro>

标记

<@compEntityImgage imageRetriever=imageRetriver entity=product height=500/>