XML学习笔记(七):使用freemark与apache填充xml模板

时间:2022-08-24 09:47:02

前言

1、在实际项目中,对于xml的解析的使用,除了dom4j和jaxb以外,xml模板的使用也是常有的事情,如短信模板,邮件模板,或者ESB的使用,都会涉及到xml模板的情况。

2、对于xml模板的使用与解析,dom4j当然是万能的,但是freemark与apache 有很好的封装的方法可以使用。

3、freemark:需要的jar包为,freemarker.jar(Configuration.java),spring-context-support.jar(FreeMarkerTemplateUtils.java).

4、apache:velocity.jar(VelocityContext.java 和 VelocityEngine)


一、模板 

1、位置:模板user-request.xml 

XML学习笔记(七):使用freemark与apache填充xml模板


2、代码:xml模板的使用具有一定的格式:如${name} 或者$!{name}

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<USER>
    <AGE>${age}</AGE>
    <NAME>${name}</NAME>
    <PhoneList>
        <UserPhone>
            <num>${num1}</num>
            <type>${type1}</type>
        </UserPhone>
        <UserPhone>
            <num>${num2}</num>
            <type>${type2}</type>
        </UserPhone>
    </PhoneList>
    <UserAddress>
        <HomeAddress>${homeAddress}</HomeAddress>
        <WorkAddress>${workAddress}</WorkAddress>
    </UserAddress>
</USER>



3、模板对应的Bean数据

1)Bean数据的使用,看实际情况,正常来说可还是需要的,专门负责数据传输的DTO 。

2)这边的Bean数据,沿用了Jaxb的数据(XML学习笔记(六):Jaxb负责xml与javaBean映射)。

package xml.code.bean;

import java.util.List;


/**
 * 
 * UserBean.java
 *
 * @title User的传输数据类
 * @description
 * @author SAM-SHO 
 * @Date 2014-11-25
 */

public class UserBean {
	private String name;
	private String key;
	private String age;
	private UserAddress userAddress;//地址
	private List<UserPhone> phoneList ;//手机

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAge() {
		return age;
	}

	public void setAge(String age) {
		this.age = age;
	}

	public UserAddress getUserAddress() {
		return userAddress;
	}

	public void setUserAddress(UserAddress userAddress) {
		this.userAddress = userAddress;
	}
	public List<UserPhone> getPhoneList() {
		return phoneList;
	}

	public void setPhoneList(List<UserPhone> phoneList) {
		this.phoneList = phoneList;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}
	
}
package xml.code.bean;

public class UserPhone {
	
	private String type;//电话号码类型
	private String num;//电话号码
	
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getNum() {
		return num;
	}
	public void setNum(String num) {
		this.num = num;
	}
	
}

package xml.code.bean;


public class UserAddress {
	
	private String homeAddress;//家庭地址
	private String workAddress;//公司地址
	
	public String getHomeAddress() {
		return homeAddress;
	}
	public void setHomeAddress(String homeAddress) {
		this.homeAddress = homeAddress;
	}


	public String getWorkAddress() {
		return workAddress;
	}
	public void setWorkAddress(String workAddress) {
		this.workAddress = workAddress;
	}

}



二、使用 FreeMarker

1、代码

	/**
	 * 使用FreeMarker填充xml 模板
	 * 
	 * @param tUserBean
	 * @return
	 */
	public String getUserRequestXml(UserBean tUserBean) {

		// 定义局部变量
		String templatePath = "/templete/"; // 报文模板路径
		String templateFileName = "user-request.xml"; // 报文模板文件名
		String requestXml = ""; // 请求报文

		Configuration config = new Configuration();
		try {

			// 方式1 绝对路径
			// config.setDirectoryForTemplateLoading(new
			// File("D:/apache-worksapce/MyEclipseNewWork2014/JavaTool/resources/templete"));
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板

			// 方式2-最常用 利用classloader
			config.setClassForTemplateLoading(this.getClass(), "/");
			Template template = config.getTemplate(templatePath + templateFileName, "UTF-8");// 报文模板

			// 方式3 需要 servletContext
			// config.setServletContextForTemplateLoading("", "/ftl"); //就是/WebRoot/ftl目录。
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板

			// 设置模板参数
			Map<String, Object> content = new HashMap<String, Object>();

			content.put("name", tUserBean.getName());
			content.put("age", tUserBean.getAge());
			content.put("num1", tUserBean.getPhoneList().get(0).getNum());
			content.put("num2", tUserBean.getPhoneList().get(1).getNum());
			content.put("type1", tUserBean.getPhoneList().get(0).getType());
			content.put("type2", tUserBean.getPhoneList().get(1).getType());
			content.put("homeAddress", tUserBean.getUserAddress().getHomeAddress());
			content.put("workAddress", tUserBean.getUserAddress().getWorkAddress());

			requestXml = FreeMarkerTemplateUtils.processTemplateIntoString(template, content);

			if (requestXml == null || "".equals(requestXml)) {
				return null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
		// 返回报文字符串
		return requestXml;
	}

2、分析:


1)模板的获取方式有三种:

①、绝对路径:

// 方式1 绝对路径
			// config.setDirectoryForTemplateLoading(new
			// File("D:/apache-worksapce/MyEclipseNewWork2014/JavaTool/resources/templete"));
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板


②、相对路径,原理为类加载器。网上一些其他文章的分析都是错误的,这边的取得是类加载器的路劲,而不是这个类本身的路径。

			// 方式2-最常用 利用classloader
			config.setClassForTemplateLoading(this.getClass(), "/");
			Template template = config.getTemplate(templatePath + templateFileName, "UTF-8");// 报文模板


③、需要使用 ServletContext 

			// 方式3 需要 servletContext
			// config.setServletContextForTemplateLoading("", "/ftl"); //就是/WebRoot/ftl目录。
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板


④、获取配置文件的方式,详见文章:

Servlet学习笔记(四):Servlet的请求与响应以及ServletContext详解

java进阶(六):Java类加载器


2)设置模板参数,注意key 与 模板${key}的一致性。

3)使用 FreeMarkerTemplateUtils.processTemplateIntoString() 即可得到结合好的xml数据。FreeMarkerTemplateUtils 类是 spring 中的封装。

4)测试:

		UserBean tUserBean = new UserBean();
		tUserBean.setName("SAM-SHO");
		tUserBean.setAge("27");
		tUserBean.setKey("属性111");

		UserAddress tUserAddress = new UserAddress();
		tUserAddress.setWorkAddress("苏州园区");
		tUserAddress.setHomeAddress("苏州高新区");
		tUserBean.setUserAddress(tUserAddress);

		List<UserPhone> phoneList = new ArrayList<UserPhone>();
		UserPhone tUserPhone = new UserPhone();
		tUserPhone.setType("移动");
		tUserPhone.setNum("13612345678");
		phoneList.add(tUserPhone);

		tUserPhone = new UserPhone();
		tUserPhone.setType("联通");
		tUserPhone.setNum("13798765432");
		phoneList.add(tUserPhone);
		tUserBean.setPhoneList(phoneList);

		ObjectAndXmlHandler handler = new ObjectAndXmlHandler();
		
		// 1- FreeMarker
		String tRequestXml = handler.getUserRequestXml(tUserBean);
		System.out.println(tRequestXml);

【输出】

得到的xml文件: 
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<USER>
    <AGE>27</AGE>
    <NAME>SAM-SHO</NAME>
    <PhoneList>
        <UserPhone>
            <num>13612345678</num>
            <type>移动</type>
        </UserPhone>
        <UserPhone>
            <num>13798765432</num>
            <type>联通</type>
        </UserPhone>
    </PhoneList>
    <UserAddress>
        <HomeAddress>苏州高新区</HomeAddress>
        <WorkAddress>苏州园区</WorkAddress>
    </UserAddress>
</USER>



三、使用 apache的 velocity 

1、这边封装了一个工具类,主要是两个类型的方法

1)填充模板的方法,填充内容至工程中的模板。

2)封装的内容写入字符串模板中。

package xml.code.templete.util;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

/**
 * 
 * TemplateTool.java
 * 
 * @title 封装的工具类
 * @description
 * @author SAM-SHO
 * @Date 2014-11-28
 */
public class TemplateTool {
	
	/**
	 * 填充模板的方法
	 * @param context
	 * @param tmdir
	 * @param tmFile
	 * @return
	 */
	public static String fill(VelocityContext context, String tmdir, String tmFile) {
		String result = "";
		try {
			Properties p = new Properties();

			p.setProperty("file.resource.loader.path", tmdir);
			VelocityEngine ve = new VelocityEngine();
			ve.init(p);
			Template t = ve.getTemplate(tmFile);
			StringWriter writer = new StringWriter();

			t.merge(context, writer);
			result = writer.toString();
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 填充模板的方法
	 * 
	 * @param context
	 *            封装的数据
	 * @param tmdir
	 *            路径
	 * @param tmFile
	 *            文件名
	 * @param inputEncode
	 *            入编码
	 * @param ouputEncode
	 *            出编码
	 * @return
	 */
	public static String fill(VelocityContext context, String tmdir, String tmFile, String inputEncode, String ouputEncode) {
		String result = "";
		try {
			Properties p = new Properties();

			p.setProperty("file.resource.loader.path", tmdir);
			p.setProperty("ISO-8859-1", "UTF-8");
			p.setProperty("input.encoding", inputEncode);
			p.setProperty("output.encoding", ouputEncode);

			VelocityEngine ve = new VelocityEngine();
			ve.init(p);
			Template t = ve.getTemplate(tmFile);
			StringWriter writer = new StringWriter();

			t.merge(context, writer);
			result = writer.toString();
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 封装的内容写入字符串模板中
	 * 
	 * @param context
	 * @param templateString
	 * @return
	 */
	public static String fill(VelocityContext context, String templateString) {
		String result = "";
		try {
			VelocityEngine ve = new VelocityEngine();
			ve.init();
			StringWriter writer = new StringWriter();
			StringReader reader = new StringReader(templateString);
			ve.evaluate(context, writer, "temp", reader);
			result = writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public static String test(int i) {
		return "array:" + i;
	}

	/**
	 * 测试
	 * 
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		ArrayList<Object> list = new ArrayList<Object>();
		Map<String,String> map = new HashMap<String,String>();
		map.put("name", "Cow");
		map.put("price", "100.00");
		list.add(map);
		map = new HashMap<String,String>();
		map.put("name", "Eagle");
		map.put("price", "59.99");
		list.add(map);
		map = new HashMap<String,String>();
		map.put("name", "Shark");
		map.put("price", "3.99");
		list.add(map);

		VelocityContext context = new VelocityContext();
		context.put("TemplateTool", new TemplateTool());
		context.put("workAddress", list);
		context.put("num", Integer.valueOf(10));
		context.put("name", "SAM-SHO");
		context.put("homeAddress", "苏州高新区");
		context.put("age", "27");

		// 1-写入项目中定义的模板
		String tmdir = TemplateTool.class.getClassLoader().getResource("templete/").getPath();
		String result = fill(context, tmdir, "user-request.xml", "UTF-8", "UTF-8");
		System.out.println("写入工程模板 :    \r\n" + result);

		// 2-写入字符串模板
		String result2 = fill(context, "${name},${homeAddress}");
		System.out.println("写入字符串模板:   " + result2);
	}
}

【测试输出】

写入工程模板 :    
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<USER>
    <AGE>27</AGE>
    <NAME>SAM-SHO</NAME>
    <PhoneList>
        <UserPhone>
            <num>${num1}</num>
            <type>${type1}</type>
        </UserPhone>
        <UserPhone>
            <num>${num2}</num>
            <type>${type2}</type>
        </UserPhone>
    </PhoneList>
    <UserAddress>
        <HomeAddress>苏州高新区</HomeAddress>
        <WorkAddress>[{price=100.00, name=Cow}, {price=59.99, name=Eagle}, {price=3.99, name=Shark}]</WorkAddress>
    </UserAddress>
</USER>



写入字符串模板:   SAM-SHO,苏州高新区


2、使用 TemplateTool 填充模板。

1)方法:

	/**
	 * 利用apache 填充xml 模板
	 * @param tUserBean
	 * @return
	 */
	public String getRequestMsg(UserBean tUserBean) {
		// 定义局部变量
		String templatePath = "/templete/"; // 报文模板路径
		String templateFileName = "user-request.xml"; // 报文模板文件名
		String requestXml = "";

		// 构建请求报文,并转换成报文字符串
		VelocityContext content = new VelocityContext();
		
		content.put("name", tUserBean.getName());
		content.put("age", tUserBean.getAge());
		content.put("num1", tUserBean.getPhoneList().get(0).getNum());
		content.put("num2", tUserBean.getPhoneList().get(1).getNum());
		content.put("type1", tUserBean.getPhoneList().get(0).getType());
		
		content.put("type2", tUserBean.getPhoneList().get(1).getType());
		content.put("homeAddress", tUserBean.getUserAddress().getHomeAddress());
		content.put("workAddress", tUserBean.getUserAddress().getWorkAddress());

		requestXml = TemplateTool.fill(content, this.getClass().getResource(templatePath).getPath(), templateFileName, "GBK", "GBK");

		if (requestXml == null || "".equals(requestXml)) {
			return null;
		}

		// 返回报文字符串
		return requestXml;
	}

2)测试:

		// 2-apache.velocity
		String tRequestXml = handler.getRequestMsg(tUserBean);
		System.out.println("apache.velocity 得到的xml文件: \r\n" +tRequestXml);

【输出】

		// 2-apache.velocity
		String tRequestXml = handler.getRequestMsg(tUserBean);
		System.out.println("apache.velocity 得到的xml文件: \r\n" +tRequestXml);


四、把两种类型的解析方式进行封装。

package xml.code.templete.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;

import xml.code.bean.UserAddress;
import xml.code.bean.UserBean;
import xml.code.bean.UserPhone;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * 
 * ObjectAndXmlHandler.java
 * 
 * @title xml模板使用
 * @description 利用 freemarker 和 apache.velocity
 * @author SAM-SHO
 * @Date 2014-11-27
 */
@Component
public class ObjectAndXmlHandler {

	Logger logger = Logger.getLogger(this.getClass());

	public static void main(String[] args) {

		UserBean tUserBean = new UserBean();
		tUserBean.setName("SAM-SHO");
		tUserBean.setAge("27");
		tUserBean.setKey("属性111");

		UserAddress tUserAddress = new UserAddress();
		tUserAddress.setWorkAddress("苏州园区");
		tUserAddress.setHomeAddress("苏州高新区");
		tUserBean.setUserAddress(tUserAddress);

		List<UserPhone> phoneList = new ArrayList<UserPhone>();
		UserPhone tUserPhone = new UserPhone();
		tUserPhone.setType("移动");
		tUserPhone.setNum("13612345678");
		phoneList.add(tUserPhone);

		tUserPhone = new UserPhone();
		tUserPhone.setType("联通");
		tUserPhone.setNum("13798765432");
		phoneList.add(tUserPhone);
		tUserBean.setPhoneList(phoneList);

		ObjectAndXmlHandler handler = new ObjectAndXmlHandler();
		
		// 1- FreeMarker
//		String tRequestXml = handler.getUserRequestXml(tUserBean);
//		System.out.println("FreeMarker 得到的xml文件: \r\n" + tRequestXml);
		
		// 2-apache.velocity
		String tRequestXml = handler.getRequestMsg(tUserBean);
		System.out.println("apache.velocity 得到的xml文件: \r\n" +tRequestXml);

	}

	/**
	 * 使用FreeMarker填充xml 模板
	 * 
	 * @param tUserBean
	 * @return
	 */
	public String getUserRequestXml(UserBean tUserBean) {

		// 定义局部变量
		String templatePath = "/templete/"; // 报文模板路径
		String templateFileName = "user-request.xml"; // 报文模板文件名
		String requestXml = ""; // 请求报文

		Configuration config = new Configuration();
		try {

			// 方式1 绝对路径
			// config.setDirectoryForTemplateLoading(new
			// File("D:/apache-worksapce/MyEclipseNewWork2014/JavaTool/resources/templete"));
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板

			// 方式2-最常用 利用classloader
			config.setClassForTemplateLoading(this.getClass(), "/");
			Template template = config.getTemplate(templatePath + templateFileName, "UTF-8");// 报文模板

			// 方式3 需要 servletContext
			// config.setServletContextForTemplateLoading("", "/ftl"); //就是/WebRoot/ftl目录。
			// Template template = config.getTemplate(templateFileName ,"UTF-8");// 报文模板

			// 设置模板参数
			Map<String, Object> content = new HashMap<String, Object>();

			content.put("name", tUserBean.getName());
			content.put("age", tUserBean.getAge());
			content.put("num1", tUserBean.getPhoneList().get(0).getNum());
			content.put("num2", tUserBean.getPhoneList().get(1).getNum());
			content.put("type1", tUserBean.getPhoneList().get(0).getType());
			content.put("type2", tUserBean.getPhoneList().get(1).getType());
			content.put("homeAddress", tUserBean.getUserAddress().getHomeAddress());
			content.put("workAddress", tUserBean.getUserAddress().getWorkAddress());

			requestXml = FreeMarkerTemplateUtils.processTemplateIntoString(template, content);

			if (requestXml == null || "".equals(requestXml)) {
				return null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
		// 返回报文字符串
		return requestXml;
	}

	/**
	 * 利用apache 填充xml 模板
	 * @param tUserBean
	 * @return
	 */
	public String getRequestMsg(UserBean tUserBean) {
		// 定义局部变量
		String templatePath = "/templete/"; // 报文模板路径
		String templateFileName = "user-request.xml"; // 报文模板文件名
		String requestXml = "";

		// 构建请求报文,并转换成报文字符串
		VelocityContext content = new VelocityContext();
		
		content.put("name", tUserBean.getName());
		content.put("age", tUserBean.getAge());
		content.put("num1", tUserBean.getPhoneList().get(0).getNum());
		content.put("num2", tUserBean.getPhoneList().get(1).getNum());
		content.put("type1", tUserBean.getPhoneList().get(0).getType());
		
		content.put("type2", tUserBean.getPhoneList().get(1).getType());
		content.put("homeAddress", tUserBean.getUserAddress().getHomeAddress());
		content.put("workAddress", tUserBean.getUserAddress().getWorkAddress());

		requestXml = TemplateTool.fill(content, this.getClass().getResource(templatePath).getPath(), templateFileName, "GBK", "GBK");

		if (requestXml == null || "".equals(requestXml)) {
			return null;
		}

		// 返回报文字符串
		return requestXml;
	}

}