Apache Freemarker的一个小坑

xxx.png 前段时间用到了Apache Freemarker来做前端页面,发现一个问题,就是JavaBean的Long类型和Integer类型向模板中注入的时候,只要是注入input类型,那么大 于999的数字则不会被显示,这个问题我也问了Apache官方的论坛:https://issues.apache.org/jira/browse/FREEMARKER-132 ,果然是官方站点呀,反馈还是很快的,系统还自动发了邮件!

给出的回复是在需要加上一个 ? c 于是我试了一下:

代码是这样的,非常简单的一个JavaBean

1package xpu.tim.freemarker_bug;
2
3@Data
4public class Person {
5    private String name;
6    private Long age;
7}

一个简单的Controller

 1package xpu.tim.freemarker_bug;
 2
 3import org.springframework.stereotype.Controller;
 4import org.springframework.web.bind.annotation.GetMapping;
 5import org.springframework.web.bind.annotation.RequestMapping;
 6import org.springframework.web.servlet.ModelAndView;
 7
 8import java.util.Map;
 9
10@Controller
11@RequestMapping("/")
12public class IndexController {
13    @GetMapping
14    public ModelAndView index(Map<String, Object> map){
15        Person person = new Person("AAA", 1001L);
16        map.put("person", person);
17        return new ModelAndView("index", map);
18    }
19}

一个index.ftl

1<input value="${person.age}" type="number">
2<input value="${person.name}" type="text">

在person.age <= 999 的时候一切正常,person.age > 999了就无法在输入框显示,通过查看页面源代码发现如果数字是1000的话:源代码内容是1,000 而不是1000,根据解决办法修改index.ftl为:

1<input value="${person.age?c}" type="number">
2<input value="${person.name}" type="text">

果然修改后一切正常,最后查阅官方文档得到如下结论: https://freemarker.apache.org/docs/ref_builtins_number.html#ref_builtin_c

This built-in converts a number to string for a “computer language” as opposed to for human audience. That is, it formats with the rules that programming languages used to use, which is independent of all the locale and number format settings of FreeMarker. It always uses dot as decimal separator, and it never uses grouping separators (like 3,000,000), nor exponential form (like 5E20), nor superfluous leading or trailing 0-s (like 03 or 1.0), nor + sign (like +1). It will print at most 16 digits after the decimal dot, and thus numbers whose absolute value is less than 1E-16 will be shown as 0. This built-in is crucial because be default (like with ${x}) numbers are converted to strings with the locale (language, country) specific number formatting, which is for human readers (like 3000000 is possibly printed as 3,000,000). When the number is printed not for human audience (e.g., for a database record ID used as the part of an URL, or as invisible field value in a HTML form, or for printing CSS/JavaScript numerical literals) this built-in must be used to print the number (i.e., use ${x?c} instead of ${x}), or else the output will be possibly broken depending on the current number formatting settings and locale (like the decimal point is not dot, but comma in many countries) and the value of the number (like big numbers are possibly “damaged” by grouping separators). If the incompatible_improvements FreeMarker configuration setting is set to 2.3.24 or higher (also if it’s set to 2.3.20 or higher and you are outside a string literal), this built-in will return “INF”, “-INF” and “NaN” for positive/negative infinity and IEEE floating point Not-a-Number, respectively. These are the XML Schema compatible representations of these special values. (Earlier it has returned what java.text.DecimalFormat did with US locale, none of which is understood by any (common) computer language.)

即在默认情况下,比如${x}数字被转换成具有地区语言、国家特定数字格式的字符串,这是针对我们人来说的,比如3000000可能打印为 3,000,000 ,但是显示成这样input标签就无法解析,所以还是需要显示成 3000000 就必须使用 ${x?c} 这种形式!