代码之家  ›  专栏  ›  技术社区  ›  Kevin Pauli

使用单个FreeMarker模板显示任意POJO的表

  •  6
  • Kevin Pauli  · 技术社区  · 16 年前

    注意高级自由标记大师:

    我希望使用单个FreeMarker模板能够输出任意POJO的表,其中要显示的列与数据分开定义。问题是,我不知道如何在运行时获取POJO上函数的句柄,然后让FreeMarker调用该函数(lambda样式)。从浏览文档来看,FreeMarker似乎支持函数式编程,但我似乎无法放弃正确的咒语。

    我想出了一个简单具体的例子。假设我有两个列表:一个是名字和姓氏的人列表,一个是品牌和型号的汽车列表。希望输出这两个表:

    <table>
      <tr>
        <th>firstName</th>
        <th>lastName</th>
      </tr>
      <tr>
        <td>Joe</td>
        <td>Blow</d>
      </tr>
      <tr>
        <td>Mary</td>
        <td>Jane</d>
      </tr>
    </table>
    

    和

    <table>
      <tr>
        <th>make</th>
        <th>model</th>
      </tr>
      <tr>
        <td>Toyota</td>
        <td>Tundra</d>
      </tr>
      <tr>
        <td>Honda</td>
        <td>Odyssey</d>
      </tr>
    </table>
    

    但是我想使用相同的模板,因为这是一个框架的一部分,必须处理几十种不同的POJO类型。

    给出以下代码:

    public class FreemarkerTest {
    
      public static class Table {
        private final List<Column> columns = new ArrayList<Column>();
    
        public Table(Column[] columns) {
          this.columns.addAll(Arrays.asList(columns));
        }
    
        public List<Column> getColumns() {
          return columns;
        }
    
      }
    
      public static class Column {
        private final String name;
    
        public Column(String name) {
          this.name = name;
        }
    
        public String getName() {
          return name;
        }
      }
    
      public static class Person {
        private final String firstName;
        private final String lastName;
    
        public Person(String firstName, String lastName) {
          this.firstName = firstName;
          this.lastName = lastName;
        }
    
        public String getFirstName() {
          return firstName;
        }
    
        public String getLastName() {
          return lastName;
        }
      }
    
      public static class Car {
        String make;
        String model;
    
        public Car(String make, String model) {
          this.make = make;
          this.model = model;
        }
    
        public String getMake() {
          return make;
        }
    
        public String getModel() {
          return model;
        }
      }
    
      public static void main(String[] args) throws Exception {
        final Table personTableDefinition = new Table(new Column[] { new Column("firstName"), new Column("lastName") });
        final List<Person> people = Arrays.asList(new Person[] { new Person("Joe", "Blow"), new Person("Mary", "Jane") });
        final Table carTable = new Table(new Column[] { new Column("make"), new Column("model") });
        final List<Car> cars = Arrays.asList(new Car[] { new Car("Toyota", "Tundra"), new Car("Honda", "Odyssey") });
    
        final Configuration cfg = new Configuration();
        cfg.setClassForTemplateLoading(FreemarkerTest.class, "");
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        final Template template = cfg.getTemplate("test.ftl");
    
        process(template, personTableDefinition, people);
        process(template, carTable, cars);
      }
    
      private static void process(Template template, Table tableDefinition, List<? extends Object> data) throws Exception {
        final Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("tableDefinition", tableDefinition);
        dataMap.put("data", data);
        final Writer out = new OutputStreamWriter(System.out);
        template.process(dataMap, out);
        out.flush();
      }
    
    }
    

    以上都是针对这个问题给出的。这是我一直在破解的模板。注意我遇到麻烦的地方的评论。

    <table>
      <tr>
    <#list tableDefinition.columns as col>
        <th>${col.name}</th>
    </#list>
      </tr>
    <#list data as pojo>
      <tr>
    <#list tableDefinition.columns as col>
        <td><#-- what goes here? --></td>    
    </#list>  
      </tr>
    </#list>
    </table>
    

    因此col.name具有要从pojo访问的属性的名称。我试过一些东西,比如

    pojo.col.name
    

    和

    <#assign property = col.name/>
    ${pojo.property}
    

    但这些当然不起作用,我只是把它们包括进来帮助表达我的意图。我正在寻找一种方法来获取函数的句柄并让FreeMarker调用它,或者某种“评估”特性,它可以将任意表达式作为字符串并在运行时对其进行评估。

    2 回复  |  直到 15 年前
        1
  •  5
  •   Chaquotay    15 年前

    ?eval (几乎?)总是一个坏主意,因为它经常伴随着性能缺陷(例如很多解析)和安全问题(例如“ftl注入”)。

    更好的方法是使用方括号语法:

    如果我们想用一个表达式来指定子变量名,可以使用另一种语法:book[“title”]。在方括号中,只要表达式的计算结果是字符串,就可以给出任何表达式。

    (从 FreeMarker documentation about retrieving data from a hash )

    在你的情况下,我推荐一些像 ${pojo[col.name]} .

        2
  •  2
  •   Kevin Pauli    16 年前

    找到了答案。

    ${("pojo." + col.name)?eval}
    
    推荐文章