Apache Freemarker 的一个小坑

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

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

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

1
2
3
4
5
6
7
package xpu.tim.freemarker_bug;

@Data
public class Person {
private String name;
private Long age;
}

一个简单的 Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package xpu.tim.freemarker_bug;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
@RequestMapping ("/")
public class IndexController {
@GetMapping
public ModelAndView index(Map<String, Object> map){
Person person = new Person("AAA", 1001L);
map.put ("person", person);
return new ModelAndView("index", map);
}
}

一个 index.ftl

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

在 person.age <= 999 的时候一切正常,person.age > 999 了就无法在输入框显示,通过查看页面源代码发现如果数字是 1000 的话:源代码内容是 1,000 而不是 1000,根据解决办法修改 index.ftl 为:
1
2
<input value="${person.age?c}" type="number">
<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} 这种形式!