【问题标题】:Unwanted quotes in substituted freemarker template fields替换的 freemarker 模板字段中不需要的引号
【发布时间】:2014-09-29 03:36:53
【问题描述】:

我正在使用 Freemarker 模板生成内容,但我在替换字段中得到了引用值。

对于 JSON 对象

{
  "name" : "Pepster"
}

在模板中:

Hi ${name}!

我明白了

Hi "Pepster"!

当我想要的时候

Hi Pepster!

我提供给它的对象是一个 JsonNode-tree,它是通过使用 Jackson 注释映射我的对象获得的:

class Name {
    @JsonProperty("name")
    public String mName;
}

处理器:

final ObjectMapper mapper = new ObjectMapper();
JsonNode jsonDocument = mapper.valueToTree(nameObject);
//...
template.process(jsonDocument, writer);

我感觉我缺少某种配置?

【问题讨论】:

    标签: java json javabeans freemarker


    【解决方案1】:

    FreeMarker 不会添加引号,JsonNodetoString() 方法肯定会。您需要使用自定义(化)ObjectWrapper,它知道它应该调用getTextValue()(或类似的东西)来提取String 值。对于 JSON 编号,您将遇到同样的问题,其中 ObjectWrapper 应该调用 getNumberValue()。或者,如果您不想投资自定义 ObjectWrapper${name.textValue} 应该可以,但如果您有很多模板,那就有点尴尬了。

    【讨论】:

      【解决方案2】:

      这是一个自定义 ObjectWrapper 的示例实现,可以正确处理JsonNode subclasses

      package com.sample.templates;
      
      import com.fasterxml.jackson.databind.JsonNode;
      import com.fasterxml.jackson.databind.node.*;
      import freemarker.core.CollectionAndSequence;
      import freemarker.ext.beans.BeanModel;
      import freemarker.ext.beans.BeansWrapper;
      import freemarker.ext.beans.IteratorModel;
      import freemarker.ext.util.ModelFactory;
      import freemarker.template.*;
      
      import java.util.ArrayList;
      import java.util.Iterator;
      import java.util.List;
      import java.util.Map;
      
      class JsonAwareObjectWrapper extends DefaultObjectWrapper {
          public JsonAwareObjectWrapper(Version incompatibleImprovements) {
              super(incompatibleImprovements);
          }
      
      
          private static class JsonNullNodeModel {
      
              static final ModelFactory FACTORY = new ModelFactory() {
      
                  public TemplateModel create(Object object, ObjectWrapper wrapper) {
                      return null;
                  }
              };
      
          }
      
          private static class JsonTextNodeModel extends BeanModel
                  implements TemplateScalarModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonTextNodeModel(object, (BeansWrapper) wrapper);
                          }
                      };
      
              JsonTextNodeModel(Object object, BeansWrapper wrapper) {
                  super(object, wrapper);
              }
      
              /**
               * Returns the result of calling {@link TextNode#asText()} on the wrapped
               * TextNode.
               */
              public String getAsString() {
                  return ((TextNode) object).asText();
              }
          }
      
          private static class JsonNumberNodeModel extends
                  BeanModel
                  implements
                  TemplateNumberModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonNumberNodeModel(object, (BeansWrapper) wrapper);
                          }
                      };
      
              JsonNumberNodeModel(Object object, BeansWrapper wrapper) {
                  super(object, wrapper);
              }
      
              @Override
              public Number getAsNumber() throws TemplateModelException {
                  return ((NumericNode) object).numberValue();
              }
          }
      
          private static class JsonBooleanNodeModel extends BeanModel implements TemplateBooleanModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonBooleanNodeModel(object, (BeansWrapper) wrapper);
                          }
                      };
      
      
              JsonBooleanNodeModel(Object object, BeansWrapper wrapper) {
                  super(object, wrapper);
              }
      
              @Override
              public boolean getAsBoolean() throws TemplateModelException {
                  return ((BooleanNode) object).asBoolean();
              }
          }
      
          private static class JsonPOJONodeModel extends BeanModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonPOJONodeModel(((POJONode) object).getPojo(), (BeansWrapper) wrapper);
                          }
                      };
      
      
              JsonPOJONodeModel(Object object, BeansWrapper wrapper) {
                  super(object, wrapper);
              }
          }
      
          private static class JsonObjectNodeModel extends BeanModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonObjectNodeModel(object, (BeansWrapper) wrapper);
                          }
                      };
              JsonObjectNodeModel(Object object, BeansWrapper wrapper) {
                  super(object, wrapper);
              }
      
              @Override
              public TemplateModel get(String key) throws TemplateModelException {
                  ObjectNode objectNode = (ObjectNode) object;
                  final JsonNode jsonNode = objectNode.get(key);
                  if (jsonNode != null)
                      return wrap(jsonNode);
                  else
                      return null;
              }
      
              @Override
              public boolean isEmpty() {
                  ObjectNode objectNode = (ObjectNode) object;
                  return objectNode.size() == 0;
              }
      
              @Override
              public int size() {
                  ObjectNode objectNode = (ObjectNode) object;
                  return objectNode.size();
              }
      
              @Override
              public TemplateCollectionModel keys() {
                  ObjectNode objectNode = (ObjectNode) object;
                  return new IteratorModel(objectNode.fieldNames(), wrapper);
              }
      
              @Override
              public TemplateCollectionModel values() throws TemplateModelException {
                  ObjectNode objectNode = (ObjectNode) object;
      
                  List<JsonNode> values = new ArrayList<>(size());
                  final Iterator<Map.Entry<String, JsonNode>> it = objectNode.fields();
                  while (it.hasNext()) {
                      JsonNode value = it.next().getValue();
                      values.add(value);
                  }
                  return new CollectionAndSequence(new SimpleSequence(values, wrapper));
              }
          }
      
          private static class JsonArrayNodeModel extends BeanModel
                  implements
                  TemplateCollectionModel,
                  TemplateSequenceModel {
              static final ModelFactory FACTORY =
                      new ModelFactory() {
                          public TemplateModel create(Object object, ObjectWrapper wrapper) {
                              return new JsonArrayNodeModel(object, (BeansWrapper) wrapper);
                          }
                      };
      
      
              private class Iterator
                      implements
                      TemplateSequenceModel,
                      TemplateModelIterator {
                  private int position = 0;
      
                  public boolean hasNext() {
                      return position < length;
                  }
      
                  public TemplateModel get(int index)
                          throws
                          TemplateModelException {
                      return JsonArrayNodeModel.this.get(index);
                  }
      
                  public TemplateModel next()
                          throws
                          TemplateModelException {
                      return position < length ? get(position++) : null;
                  }
      
                  public int size() {
                      return JsonArrayNodeModel.this.size();
                  }
              }
      
              private final int length;
      
              JsonArrayNodeModel(Object array, BeansWrapper wrapper) {
                  super(array, wrapper);
                  ArrayNode arrayNode = (ArrayNode) array;
      
                  length = arrayNode.size();
              }
      
              @Override
              public TemplateModelIterator iterator() {
                  return new Iterator();
              }
      
              @Override
              public TemplateModel get(int index) throws TemplateModelException {
                  try {
                      return wrap(((ArrayNode) object).get(index));
                  } catch (IndexOutOfBoundsException e) {
                      return null;
                  }
              }
      
              @Override
              public int size() {
                  return length;
              }
      
              @Override
              public boolean isEmpty() {
                  return length == 0;
              }
          }
      
          @Override
          protected ModelFactory getModelFactory(Class clazz) {
              if (TextNode.class.isAssignableFrom(clazz)) {
                  return JsonTextNodeModel.FACTORY;
              } else if (NumericNode.class.isAssignableFrom(clazz)) {
                  return JsonNumberNodeModel.FACTORY;
              } else if (BooleanNode.class.isAssignableFrom(clazz)) {
                  return JsonBooleanNodeModel.FACTORY;
              } else if (POJONode.class.isAssignableFrom(clazz)) {
                  return JsonPOJONodeModel.FACTORY;
              } else if (ArrayNode.class.isAssignableFrom(clazz)) {
                  return JsonArrayNodeModel.FACTORY;
              } else if (ObjectNode.class.isAssignableFrom(clazz)) {
                  return JsonObjectNodeModel.FACTORY;
              } else if (NullNode.class.isAssignableFrom(clazz)) {
                  return JsonNullNodeModel.FACTORY;
              } else {
                  return super.getModelFactory(clazz);
              }
          }
      }
      

      示例用法:

      final ObjectNode objectNode = objectMapper.createObjectNode();
      objectNode.put("string", "jsonValue");
      objectNode.put("int", 10);
      objectNode.put("float", 3.14f);
      objectNode.put("bool", true);
      
      final ArrayNode arrayNode = objectMapper.createArrayNode();
      arrayNode.add("arrayValue0");
      arrayNode.add("arrayValue1");
      objectNode.set("array", arrayNode);
      
      final ObjectNode subObject = objectMapper.createObjectNode();
      subObject.put("bool", false);
      objectNode.set("subobject", subObject);
      
      PojoSample pojoSample = new PojoSample();
      pojoSample.setName("pojo");
      pojoSample.setValue(100);
      objectNode.set("pojo", new POJONode(pojoSample));
      
      SimpleHash model = new SimpleHash(objectWrapper);
      model.put("form", request.getForm());
      model.put("env", request.getEnvironment());
      model.put("json", objectNode);
      
      
      stringLoader.putTemplate("simple",
                  "Json test: \n" +
                  "String ${json.string}\n" +
                  "Integer ${json.int}\n" +
                  "Float ${json.float}\n" +
                  "Boolean ${json.bool?c}\n" +
                  "Sub Object ${json.subobject.bool?c}\n" +
                  "Sub Array ${json.array[1]}\n" +
                  "Pojo: name = ${json.pojo.name}, value = ${json.pojo.value}");
      Template temp = cfg.getTemplate("simple");
      StringWriter out = new StringWriter();
      temp.process(model, out);
      

      【讨论】:

      • 解决方案缺少对 Jackson NullNode 的处理。 NullNode 应该通过返回一个总是产生 null 的模型工厂来处理。
      • 哦,谢谢@Nathan,我也遇到了这个问题,但忘记更新答案
      【解决方案3】:

      尝试在你的 freemarker 页面中添加 ?eval

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2022-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 1970-01-01
      • 2013-04-23
      • 1970-01-01
      相关资源
      最近更新 更多