最近弄一个项目,需要读取xml文件里面的内容生成word文档,网上找了很多方案,最后选择用freemarker来处理,一是读写起来比较简单,而是不需要设置复杂的Word格式且一旦格式有变化重新生成模板就成,不需要改java代码。这篇文档有介绍怎么生成xml模板以及静态的例子,我也是参考这篇来处理的。链接:
http://blog.csdn.net/lun379292733/article/details/18673081
我这里写个简单的过程吧,加深下印象。
一、创建模板文件:
1、找到需要做成模板的word文档,将其中的关键数据用EL表达式来替换,譬如:
姓名:wjl
改成:
姓名:${name}
2、将替换成EL之后的word另存为xml(注意保存时选择“只保存数据”);
3、修改xml文件的后缀名,改成ftl格式。
4、我的word模板里面有个需要循环的列表,列表里面的内容我定义成了${content},但是这个内容有很多,类似于循环一样,因此需要添加<#list>来体现循环,如图:
其中,真正的标签是<#list>,fileList是要循环的list,file是别面,用<c:foreach>来说就是:fileList是放在item里面的东西,file类似于var里面的东西。
5、我的这个ftl文件名称为:word.ftl,放在config文件夹下(不是包,参考的那篇文章是放在包下的,我这个放在文件夹下)。
二、读取xml文件生成word:
1、createWordFile用来组装业务,包括读取xml数据生成word,并返回给调用出的结果。
/**
* 该方法用来生成word文件
* @param:父级文件夹名称
* @param:需要处理的文件所在目录
* */
public boolean createWordFile(String parentFileName,String newFilePath) throws Exception{
boolean result=false;
File xmlFile = new File(newFilePath+File.separator+"200105.xml");
if(xmlFile.isFile() && xmlFile.exists()){//是文件且存在
List list = readWord(xmlFile);//组装数据、以及文件名称
if(list!=null && list.size()>0){
String wordName = list.get(0).toString();//文件名称
Map dataMap = (Map)list.get(1);//集合数据
String templateFilePath = new File("").getAbsolutePath()+File.separator+"config";//设定为当前文件夹
// System.out.println("模板文件所在目录:"+templateFilePath);
boolean flag = createWord(dataMap,templateFilePath,"word.ftl",classFile.getAbsolutePath(),wordName+".doc");//生成word
if(flag){
result=true;
}else{
errorInfo = "word文件生成失败!";
}
}else{
errorInfo="无法读取"+(newFilePath+File.separator+"200105.xml")+"文件中的数据信息!";
}
}
return result;
}
2、readWord用来读取xml文件组装数据,并返回数据以及word文件名称(因为word文件名称来源于xml文件)。
/***
* 该方法用来组装数据
* @param xmlFile:需要进行解析的xml文件
* */
public List readWord(File xmlFile) {
/** 用于组装word页面需要的数据 */
Map<String, Object> dataMap = new HashMap<String, Object>();
List list = null;
StringBuffer wordName=new StringBuffer();//word文档名称:案件编号+名称+内部编号
try {
//读取XML
SAXReader sax = getSAXReader();
Document doc = sax.read(xmlFile);//读取文档
String fileContent="",appAJ="";
//1、案件编号
Node node = doc.selectSingleNode("//ANJIANBH");
if(node!=null ){
fileContent=node.getText().trim();
}else{
fileContent="";
}
wordName.append(fileContent);
dataMap.put("caseNum",fileContent);//案件编号
//2、名称
node = doc.selectSingleNode("//FAMINGCZMC");
if(node!=null ){
fileContent=node.getText().trim();
}else{
fileContent="";
}
wordName.append(escapeExprSpecialWord(fileContent));//去除特殊字符
dataMap.put("name",fileContent);//名称
//3、内部编号/申请号
fileContent="内部编号:";
node = doc.selectSingleNode("//NEIBUBH");
if(node!=null ){//说明没有内部编号
fileContent="内部编号:"+node.getText().trim();
appAJ = node.getText().trim();
if(appAJ.toUpperCase().startsWith("AJ")){//避免出现小写aj的情况,所以全部转成大写进行比较
//内部编号用最后一个下划线截取
appAJ = appAJ.substring(appAJ.lastIndexOf("_")+1);
}
wordName.append(appAJ);
}else{//那么查找是否有申请号
node = doc.selectSingleNode("//SHENQINGH");
if(node!=null ){
fileContent=node.getText().trim();
wordName.append(fileContent.split(":")[1]);
}
}
dataMap.put("num",fileContent);
//4、提交人姓名或名称
node = doc.selectSingleNode("//XINGMINGHMC");
if(node!=null ){
fileContent=node.getText().trim();
}else{
fileContent="";
}
dataMap.put("proxy",fileContent);
//5、 收到时间
node = doc.selectSingleNode("//QIANMINGSJC");
if(node!=null ){
fileContent=node.getText().trim();
}else{
fileContent="";
}
dataMap.put("time",fileContent);//收到时间
//6、收到文件情况
StringBuffer content= new StringBuffer();
Element element=null;
List<Map<String, Object>> fileList=new ArrayList<Map<String,Object>>();
List<Element> els = doc.selectNodes("//SHOUDAOWJ");
if(els!=null && els.size()>0){
for(int i=0;i<els.size();i++){
Map<String, Object> map=new HashMap<String, Object>();
element = els.get(i);
content.delete(0,content.length());//清空
content.append((i+1)+"、");//序号
node = element.selectSingleNode(".//WENJIANMC");//文件名称
content.append(node.getText().trim()+" ");
node = element.selectSingleNode(".//WENJIANGS");//文件格式
content.append(node.getText().trim()+"格式 ");
node = element.selectSingleNode(".//WENJIANDX");//文件大小
content.append("文件大小"+node.getText().trim());
// System.out.println(content.toString());
map.put("content", content.toString());
fileList.add(map);
}
}else{
Map<String, Object> map=new HashMap<String, Object>();
map.put("content","");
fileList.add(map);
}
dataMap.put("fileList",fileList);
// 落款时间
node = doc.selectSingleNode("//SHOUDAOSJ");
if(node!=null ){
fileContent = node.getText().trim();
}else{
fileContent="";
}
dataMap.put("date",fileContent);//落款时间
list = new ArrayList();
list.add(wordName);
list.add(dataMap);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return list;
}
/**
* 该方法用来获取SXAReader对象
* */
public SAXReader getSAXReader(){
SAXReader saxReader = new SAXReader();
/* 在读取文件时,去掉dtd的验证,可以缩短运行时间 */
try {
// saxReader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);//可能需要网络,所以不用它
saxReader.setFeature(Constants.XERCES_FEATURE_PREFIX + Constants.LOAD_EXTERNAL_DTD_FEATURE, false); //设置不需要校验头文件
} catch (SAXException e) {
e.printStackTrace();
}
return saxReader;
}
/**
* 转义正则特殊字符 ($()*+.[]?\^{},|/\:?"<>)
* @param keyword
* @return
*/
public String escapeExprSpecialWord(String keyword) {
if(keyword!=null && keyword.trim().length()>0){
String[] fbsArr = {"/","\\",":","*","?","\"","<",">","|", "(", ")","+", "[", "]", "^", "{", "}","、"};
for (String key : fbsArr) {
if (keyword.contains(key)) {
keyword = keyword.replace(key,"");
}
}
}
return keyword;
}
3、createWord用来生成word文档。
/**
* 该方法用来生成word文件
* @param dataMap:数据集合
* @param templateFilePath:模板文件所在目录
* @param templateFileName:模板文件名称
* @param docFilePath:生成的doc文件保存路径
*@param docFileName:生成的doc文件名称:名称+.doc
*/
public static boolean createWord(Map dataMap,
String templateFilePath,String templateFileName,
String docFilePath,String docFileName){
try {
Configuration configuration = new Configuration();//创建配置实例
configuration.setDefaultEncoding("UTF-8");//设置编码
configuration.setDirectoryForTemplateLoading(new File(templateFilePath));//加载模板文件
Template template = configuration.getTemplate(templateFileName);//获取模板
if(docFileName==null || !docFileName.toUpperCase().endsWith(".DOC")){//为空或者不是以doc结尾的,添加后缀名
docFileName = docFileName+".doc";
}
File outFile = new File(docFilePath+File.separator+docFileName);
//如果输出目标文件夹不存在,则创建
if (!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
//关闭流
out.flush();
out.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
三、需要用到的jar包:
freemarker-2.3.16.jar:使用freemarker生成word的主要包
dom4j-1.6.1.jar:解析xml文件的主要包
jaxen-1.1-beta-7.jar:解析xml文件的辅助包
已经上传到附件,有需要的可以下载。
三、需要注意的问题:
a、将word另存为xml得到的xml文件格式很乱,可能只有几行,可以通过格式化工具格式化一下。这样子易读性是增强了,但是可能会影响word格式。我这里有个落款时间就是因为格式化后出现了问题。格式化之前落款时间是居右显示,格式化之后竟然居中了,代码没有变化,可能就是中间出现了空格导致了问题。所以格式化之后最好测试话一下,或者不要格式化。
b、得到的xml文件里的$与参数分开了。譬如原本的word里面写的参数为${content},xml之后${content}被拆成了${,content,和}三部分。具体原因和解决办法可以看看 参考文件,我这就是老出现这个问题,弄了好几次还这样子,不得已只能直接改xml文件。
c、put到map里的参数一定要与xml中写的的参数一致,否则会报错。特别是有list的情况下,别名一定不能忘。我的例子就是${file.content}这个file一定不能忘,否则也会报错。值得庆幸的是,报错很明显,一查就能查不出来。