macros not rendering in NVelocity

2019-08-11 07:42发布

问题:

I have simple set of velocity templates. When I am trying to merge using NVelocity, the macros from other templates are not executing. The template contents are as follows:

V1.vm

#parse("V2.vm")
#foreach( $customer in $customers)
    Hello $customer.Name!
    #set($a =$customer.getage())
    #age($a)
#end

V2.vm

#macro ( age $a )
    #if($a<18)
        Minor
    #else
        Major
    #end
#end

On merge, the output is:

Hello User1!

    #age(33)

Hello User2!

    #age(13)

回答1:

The macro doesn't work because NVelocity (and its ancestor Velocity) determine if #age is a directive or macro at parse time, while the #age macro gets discovered at runtime as it jumps into the other template, and so it is passed through as text.

To get around this you need to make the macro available to the parser before it parses your V1.vm's #foreach. You can obviously do this by putting the macro inline in that file, but I assume you intend to reuse it in other templates which is why you've got it separate now.

The other option is to put the macro in the macro library, either the one NVelocity will automatically load (VM_global_library.vm) or a custom one. If you create a template named VM_global_library.vm at the root of your templates directory NVelocity will automatically load this first before parsing anything, otherwise create your own macro template file and register it with the VelocityEngine with the velocimacro.library property. See the Velocity documentation for a more detailed explanation of the properties.

I've included a working example of using a custom macro library:

class Customer
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int GetAge() { return Age; }
}
class Program
{
    static void Main(string[] args)
    {
        VelocityEngine velocityEngine = new VelocityEngine();
        ExtendedProperties extendedProperties = new ExtendedProperties();
        extendedProperties.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, "Templates");
        extendedProperties.SetProperty(RuntimeConstants.VM_LIBRARY, "MyMacros.vm");
        velocityEngine.Init(extendedProperties);

        VelocityContext context = new VelocityContext();
        context.Put("customers", new Customer[] {
            new Customer { Name = "Jack", Age = 33 },
            new Customer { Name = "Jill", Age = 13 }
        });

        using (StringWriter sw = new StringWriter())
        {
            bool result = velocityEngine.MergeTemplate("V1.vm", context, sw);
            Console.WriteLine(sw.ToString());
        }
    }
}