Freemarker 中的继承/instanceof 检查

Inheritance / instanceof checks in Freemarker

我的用例是为用户提供在模板引擎的帮助下创建报告的可能性。因此,我提取了数据模型的相关部分并将 Freemarker 作为模板引擎集成。 到目前为止效果很好,但现在我的数据模型包含某些位置的继承 - 但 Freemarker 似乎不支持 instanceof 操作?如何处理这个问题?模型中是否还有其他支持继承的模板引擎?

虚构的例子:

我有 2 个 classes "Car" 和扩展 "Vehicle" 的 "Bike"。该模型包含一个 class "vehicle fleet",其中包含一个车辆列表。用户希望(在模板的帮助下)遍历列表并在汽车的情况下写入属性 "countSeats",在自行车的情况下写入属性 "frame size"。如何使用 Freemarker 实现这一点?可以在任何模板引擎中完成吗?

非常感谢!

// 编辑:不幸的是,由于车辆的顺序(在上面的示例中)在列表是必不可少的。

没有为此内置任何内容,但也不一定是。您可以编写自己的 TemplateMethodModelEx,或将简单的 Java 辅助对象放入数据模型中以执行几乎所有操作。或者,您可以将相关的 类 放入数据模型中,例如 root.put("Car", Car.class) 等,然后像这样使用 Class 的 Java API : <#if Car.isInstance(someObject)>

使用 TemplateMethodModelEx 的解决方案。

Class:

public class InstanceOfMethod implements TemplateMethodModelEx {

    @Override
    public Object exec(List list) throws TemplateModelException
    {
        if (list.size() != 2) {
            throw new TemplateModelException("Wrong arguments for method 'instanceOf'. Method has two required parameters: object and class");
        } else {
            Object object = ((WrapperTemplateModel) list.get(0)).getWrappedObject();
            Object p2 = ((WrapperTemplateModel) list.get(1)).getWrappedObject();
            if (!(p2 instanceof Class)) {
                throw new TemplateModelException("Wrong type of the second parameter. It should be Class. Found: " + p2.getClass());
            } else {
                Class c = (Class) p2;
                return c.isAssignableFrom(object.getClass());
            }
        }
    }
}

将 class 的实例和所有必需的 classes 放入模板的输入参数中:

parameters.put("instanceOf", new InstanceOfMethod());
parameters.put("Car", Car.class);
...

或者您可以将方法添加到共享变量:http://freemarker.org/docs/pgui_config_sharedvariables.html

所以你可以在FTL中使用方法如下:

<#if instanceOf(object, Car)>
       ...
</#if>

更丑陋的解决方案

            <#if yourObject.class.simpleName == "Simple class name like String">
                something
            </#if>                     

            <#if yourObject.class.simpleName == "other simple class name">
                do something else
            </#if>  `

我发现这比注册所有 classes 更容易,您可能会再次在 instanceof 中尝试这样做:

public class InstanceOfMethod implements TemplateMethodModelEx {

    @Override
    public Object exec(List arguments) throws TemplateModelException {
        if (arguments.size() != 2) {
            throw new TemplateModelException("Wrong arguments");
        }
        
        Object bean = DeepUnwrap.unwrap((TemplateModel) arguments.get(0));
        String className = ((TemplateModel) arguments.get(1)).toString();
        
        try {
            Class clazz = Class.forName(className);
        
            return clazz.isInstance(bean);
        }
        catch (ClassNotFoundException ex) {
            throw new TemplateModelException("Could not find the class '" + className + "'", ex);
        }
    }
    
}

然后您可以只使用带有 class 可用于 classloader 的任何 class FQN 的字符串:

<#if instanceOf(object, "mypackage.MyClass")>
...
</#if>