testerJeff
testerJeff

Reputation: 5

Freemarker (FTL) - How to check if String can be converted to a number using ?number

How can I check if a string can be converted to a number?

For example this will not work unless SelfAge is a valid String to convert:

<#if SelfAge?? && (SelfAge?number > 15)>

Test Data:

SelfAge = "October 30, 1913 // "Can't convert this string to number: "October 30, 1913"
SelfAge = "User-submitted-comment" //  Can't convert this string to number: "User-submitted-comment"
SelfAge = "1/12" // Can't convert this string to number: "1/12"

Upvotes: 0

Views: 3104

Answers (2)

T.Okrajni
T.Okrajni

Reputation: 91

This works for non-negative integers:

[#if ageString?matches("^\\d+$")]
    [#assign ageNumber = ageString?number]
[/#if]

Upvotes: 2

Cortex
Cortex

Reputation: 684

There are 2 appraoches:

Freemarker attempt, recover block

Assuming the input can be one of the following

<#assign SelfAge = "October 30, 1913" >
<#assign SelfAge = "1/12" >
<#assign SelfAge = "101" >
<#assign SelfAge = "User-submitted-comment" >

You can do this (starting from 2.3.3) using attempt, recover:

<#attempt>
    <#-- try -->
    <#if SelfAge?? && (SelfAge?number > 15)>
     Will do something with the number
    </#if>

<#recover>
    <#-- catch -->
    Can't convert this string to number: ${SelfAge}
</#attempt>

However, this solution has limitations:

  • Will throw an exception which will appear in log/console as big error trace.
  • Does not support all number formats, will support only pure digits string.

This leads us to the second and better approach.

Freemarker TemplateHashModel Container

You can use TemplateHashModel Hash to create a model of a utility class of your own that you can use to access more customized funcaitonalities that Freemarker doesn't provide off the bat.

Our goal is to create a method named isNumber inside a class in your project called MyUtils then we will use this method to check the string and get eaither true or false. In this manner:

<#if Utils.isNumber(SelfAge)>

First, create your MyUtils class:

package example;
public class MyUtils {

        public static boolean isNumber(String s){
            try {
                Integer.parseInt(s);
                return true;
            }
            catch (NumberFormatException e){
                return false;
            }
        }

    }

Second, implement a bean wrapper to create static models container and add your class to it (make sure package path is correct):

BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
TemplateHashModel staticModels = wrapper.getStaticModels();
TemplateHashModel myUtilsWrapper = (TemplateHashModel) staticModels.get( "example.MyUtils" );

If your application is Spring app, the wrapper can be added to the controller redirecting to your page, here is my controller class:

import freemarker.ext.beans.BeansWrapper;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModelException;

    @RestController
    public class HelloController {

            @RequestMapping("/")
            public ModelAndView hello() throws TemplateModelException {
                ModelAndView mainView = new ModelAndView();
                mainView.setViewName("index");

                BeansWrapper wrapper = BeansWrapper.getDefaultInstance();
                TemplateHashModel staticModels = wrapper.getStaticModels();
                TemplateHashModel myUtilsWrapper = (TemplateHashModel) staticModels.get( "example.MyUtils" );

                mainView.getModel().put("Utils", myUtilsWrapper);

                return mainView;
            }

        }

Notice how we named our wrapper: Utils <-- this is the name we will use to access method from Freemarker template.

Now in Freemarker do this:

<#if Utils.isNumber(SelfAge)>
    <div>Will do something with the number</div>
<#else>
    <div>Can't convert this string to number: ${SelfAge}</div>
</#if>

It will produce same output as first approach, with better code encapsulation.

Bonus

I'd go one further step and implement the checker method using Apache Commons NumberUtils.isCreatable()

This way nulls, negative/postive signs, and all number formats will be covered!

public static boolean isNumeric(String s){
    return NumberUtils.isCreatable(s);
}

Then in Freemarker:

<#if Utils.isNumeric(SelfAge)>
    <div>Will do something with the number</div>
<#else>
    <div>Can't convert this string to number: ${SelfAge}</div>
</#if>

Example: This case <#assign SelfAge = "-101.1" > will fail in first and second appraoch but will work in the Bonus suggestion:

Will do something with the number

Upvotes: 2

Related Questions