关于freemarker自定义标签作为模板在struts中生成静态html的问题

vicklin 2013-02-22 03:53:51

这个问题其实之前也已经有人问过了类似的:http://bbs.csdn.net/topics/320155882
,但是我还是找不到满意的答案。
详细描述如下:

自定义标签 [@vcms_entity][/@vcms_entity],这个标签只有三个参数,entity,package,id,传入这三个参数后可到数据库取出相应的对象

/**
* 通用取数据对象模板(entity:类名,需与实际名同,可指定包名package;其他属性名需与类属性名同,因为要调用相应的getter)
*
* @example <@vcms_entity entity="Channel" package="com.vcms.cms.entity" id="1">
* </@vcms_entity>
*
* @param entity
* 类名 必填 该类名大小写正确,例如有一个类是UserInfo,如果写成userInfo或Userinfo都是错误
*
* @param package 包名 选填 默认是com.vcms.cms.entity
*
* @param id
* 对象对应的id 必填
*
* @author vick
*
*/
@Component("vcms_entity")
public class VcmsEntityDirective implements TemplateDirectiveModel {

/**
* 结果集合
*/
private static final String RESULT = "obj";
private int id;

private BasicService basicService;

@SuppressWarnings("rawtypes")
@Override
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
Class<?> clazz = null; // 查找对象
TemplateModel className = null;
String packageName = null;

// 遍历Map
Iterator paramIter = params.entrySet().iterator();
Map.Entry ent;
String paramName;
TemplateModel paramValue;
while (paramIter.hasNext()) {
ent = (Map.Entry) paramIter.next();
paramName = (String) ent.getKey();
paramValue = (TemplateModel) ent.getValue();

// 处理"entity"
if ("entity".equals(paramName)) {
className = paramValue;
}
// 处理"package"
if ("package".equals(paramName)) {
packageName = paramValue.toString().trim();
}

// 处理Id
if ("id".equals(paramName)) {
id = FreeMarkertUtil.getNumberValue(paramName, paramValue)
.intValue();
}
}

/**
* 设置类
*/
if (null != packageName && !"".equals(packageName)) {
clazz = FreeMarkertUtil.findOutClass(className, packageName);
} else {
clazz = FreeMarkertUtil.findOutClass(className);
}
// 检查id
if (id < 1) {
throw new TemplateModelException(
"The \"id\" parameter is required.And the value of it should be larger than 0");
}

// 查询结果
Object obj = basicService.findById(clazz, id);
// // 找不到对象时,创建空对象
// if (null == obj) {
// try {
// obj = clazz.newInstance();
// } catch (Exception e) {
// throw new TemplateModelException(
// "Found no object with the parameter \"id\"=\"" + id+
// "\",error occured while trying to get newInstance of clazz:\""
// + className + "\"");}
// }

// 保存结果集
env.setVariable(RESULT, ObjectWrapper.DEFAULT_WRAPPER.wrap(obj));
if (body != null) {
body.render(env.getOut());
}
}

@Autowired
public void setBasicService(BasicService basicService) {
this.basicService = basicService;
}

}



如上定义,我可以在
a.ftl中使用<@vcms_entity entity="Channel" package="com.vcms.cms.entity" id="1"></@vcms_entity>
这样的方式取得数据,使用方法是访问相应的struts action,然后返回类型<result name="success" type="freemarker">a.ftl</result>

但是,目前我希望采用生成html的方式处理,即:
读入a.ftl模板,生成successPath="a.html"(或者其他名字),然后<result name="html">${successPath}</result>
问题就出现了:

在StaticFreemarker.java中读入的a.ftl,没法执行其中的自定义标签,导致读入模板报错。(执行到[color=#800000]template.process(map, out);
这句的时候报错
)[/color]

StaticFreemarker.java


@Component
public class StaticFreemarker {

/**
* 初始化模板引擎
*
* @param ftl
* 模板名称
* @param htmlName
* 需要生成html页面的名称
* @param map
* 模板中需要的参数集合
* @param relativePath
* 模板相对于根路径的相对路径, 例如:/WebRoot/module/test.ftl,则这里传入的参数是module
* @throws IOException
* @throws TemplateException
*/
@SuppressWarnings("rawtypes")
public void init(String ftl, String htmlName, Map map, String relativePath)
throws IOException, TemplateException {

// 获取模板
Configuration cfg = new Configuration();
cfg.setServletContextForTemplateLoading(
ServletActionContext.getServletContext(), "/" + relativePath);
cfg.setEncoding(Locale.getDefault(), "utf-8");
Template template = cfg.getTemplate(ftl);
// 设置模板编码
template.setEncoding("utf-8");

String path = ServletActionContext.getServletContext().getRealPath("/");
File htmlFile = new File(path + htmlName);
Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(htmlFile), "utf-8"));
template.process(map, out);
out.flush();
out.close();
}

}




报的错误是:
freemarker.core.InvalidReferenceException: Expression user is undefined on line 6, column 19 in a.ftl.


附上a.ftl


[@vcms_list entity="User" package="com.vcms.cms.entity" size=20 page=2 snames="birthday,registerTime"
svalues="2013-02-11,2013-02-13 23:08:32"]
[#list resultset.list as user]
<hr>
${user.userId}
<br>
${user.userName}
<br>
${user.registerTime}
<br>
${user.birthday}
<br>
[/#list]
[/@vcms_list]





请各位大神指教。


以上说了一通,我总结一下:
我希望使用struts+freemarker及自定义freemarker标签生成html,在读入自定义标签模板的时候,报错。断点自定义标签的directive,并没有进入。
...全文
383 5 点赞 打赏 收藏 举报
写回复
5 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
vicklin 2013-02-22
引用 2 楼 vicklin 的回复:
发现a.ftl粘贴错误了,粘贴了另一个标签,正确的代码应该是下面, XML/HTML code?123[@vcms_entity entity="Site" id=1] ${obj.siteId}-${obj.siteName}-${obj.sitePath}-${obj.finalStep}-${obj.protocol}[/@vcms_entity] ……
哦,当时的a.ftl文件是这样的




[@vcms_entity entity="Site" id=1]
	${obj.siteId}-${obj.siteName}-${obj.sitePath}-${obj.finalStep}-${obj.protocol}
[/@vcms_entity]
所以第5行就是 ${obj.siteId}-${obj.siteName}-${obj.sitePath}-${obj.finalStep}-${obj.protocol}
  • 打赏
  • 举报
回复
vicklin 2013-02-22
好吧,转念一想,这个问题解决了:在取template之前先渲染好configuration,即调用以下函数

protected Configuration updateConfiguration(Configuration configuration)
			throws TemplateException {

		// 设置标签类型([]、<>),[]这种标记解析要快些
		configuration.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);

		// 设置允许属性为空
		configuration.setClassicCompatible(true);

		// 取出上下文
		ApplicationContext applicationContext = WebApplicationContextUtils
				.getRequiredWebApplicationContext(ServletActionContext
						.getServletContext());

		// 获取实现TemplateDirectiveModel的bean
		Map<String, TemplateDirectiveModel> beans = applicationContext
				.getBeansOfType(TemplateDirectiveModel.class);

		for (String key : beans.keySet()) {
			Object obj = beans.get(key);
			if (obj != null && obj instanceof TemplateDirectiveModel) {
				configuration.setSharedVariable(key, obj);
			}
		}

		return configuration;
	}
在 Template template = cfg.getTemplate(ftl);加cfg=updateConfiguration(cfg); OK~
  • 打赏
  • 举报
回复
Expression obj is undefined on line 5, column 11 in a.ftl.第五行是什么? 报错意思表达式为定义。看看是否字母啥的,写错没有
  • 打赏
  • 举报
回复
vicklin 2013-02-22
发现a.ftl粘贴错误了,粘贴了另一个标签,正确的代码应该是下面,

[@vcms_entity entity="Site" id=1]
	${obj.siteId}-${obj.siteName}-${obj.sitePath}-${obj.finalStep}-${obj.protocol}
[/@vcms_entity]
其对应报错是 freemarker.core.InvalidReferenceException: Expression obj is undefined on line 5, column 11 in a.ftl.
  • 打赏
  • 举报
回复
vicklin 2013-02-22
补上action中的方法

	public String testHtml() throws IOException, TemplateException {

		successPath = "fuckyou.html";
		
		staticFreemarker.init("a.ftl", successPath, null, "test");

		return "html";
	}
和对应的struts.xml

<package name="test" extends="json-default" namespace="/test">
		<action name="*_*" class="{1}Action" method="{2}">
			<result name="success" type="freemarker" >/test/test.ftl</result>
			<result name="html" >/${successPath}</result>
		</action>
	</package>
  • 打赏
  • 举报
回复
相关推荐
发帖
Web 开发
加入

8.0w+

社区成员

Java Web 开发
申请成为版主
帖子事件
创建了帖子
2013-02-22 03:53
社区公告
暂无公告