前言
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
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");// 报文模板
④、获取配置文件的方式,详见文章:
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;
}
}