前言
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; } }