Java基于正则表达式实现xml文件的解析功能详解
本文实例讲述了Java基于正则表达式实现xml文件的解析功能。分享给大家供大家参考,具体如下:
这是我通过正则表达式实现的xml文件解析工具,有些XHTML文件中包含特殊符号,暂时还无法正常使用。
设计思路:常见的xml文件都是单根树结构,工具的目的是通过递归的方式将整个文档树装载进一个Node对象。xml文档树上的每一个节点都能看做一个Node对象,它拥有title、attribute和text三个自身变量以及一个childrenNode集合用来存放子节点,使用正则表达式完整装载。
一、编写Node类
Node对象是文档解析的基础,最终可以通过对象的不同属性实现对文档信息的访问。
Node.java:
importjava.io.Serializable;
importjava.util.HashMap;
importjava.util.Iterator;
importjava.util.LinkedList;
importjava.util.List;
importjava.util.Map;
importjava.util.Map.Entry;
publicclassNodeimplementsSerializable{
//可以对Node对象持久化保存
privatestaticfinallongserialVersionUID=1L;
privateintid;
//节点类型
privateStringtitle;
//节点内容
privateStringtext;
//节点属性集合
privateMapattributes=newHashMap();
//子节点集合
privateListchildNodes=newLinkedList();
publicintgetId(){
returnid;
}
publicvoidsetId(intid){
this.id=id;
}
publicStringgetTitle(){
returntitle;
}
publicvoidsetTitle(Stringtitle){
this.title=title;
}
publicMapgetAttribute(){
returnattributes;
}
publicvoidsetAttribute(Mapattribute){
this.attributes=attribute;
}
publicStringgetText(){
returntext;
}
publicvoidsetText(Stringtext){
this.text=text;
}
publicListgetChildNode(){
returnchildNodes;
}
publicvoidsetChildNode(ListchildNode){
this.childNodes=childNode;
}
//将属性集合转换成一条完整的字符串
privateStringattrToString(){
if(attributes.isEmpty()){
return"";
}
Iterator>its=attributes.entrySet().iterator();
StringBufferbuff=newStringBuffer();
while(its.hasNext()){
Entryentry=its.next();
buff.append(entry.getKey()+"=\""+entry.getValue()+"\"");
}
return""+buff.toString().trim();
}
//输出完整的节点字符串也用到了递归
@Override
publicStringtoString(){
Stringattr=attrToString();
if(childNodes.isEmpty()&&text==null){
return"<"+title+attr+"/>\n";
}elseif(childNodes.isEmpty()&&text!=null){
return"<"+title+attr+">\n"+text+"\n"+""+title+">\n";
}else{
StringBufferbuff=newStringBuffer();
buff.append("<"+title+attr+">\n");
if(!text.isEmpty()){
buff.append(text+"\n");
}
for(Noden:childNodes){
buff.append(n.toString());
}
buff.append(""+title+">\n");
returnbuff.toString();
}
}
}
二、创建接口
把文档的读取和分析抽象成接口方便今后替换实现。
过滤器:读取文档的字符流并删除注释的部分。这些信息通常是提供给人阅读的,程序分析直接忽略。
XmlFilter.java:
/*
*过滤器的作用是删除xml文件中不重要的部分。
*通常都是一些注释性文字,不需要被机器解析。
*/
publicinterfaceXmlFilter{
Stringfilter();
//提供自定义正则表达式,识别符合过滤条件的字符串
Stringfilter(String[]regex);
}
解析器:将一个父节点解析成多条子节点的字符串。如果返回值为null,代表当前节点下不存在可以继续解析的对象。
XmlParser.java:
importjava.util.List;
/*
*解析器可以对一段完整的父节点字符串提供解析服务。
*将一条父节点的字符串解析成为多条子节点字符串
*/
publicinterfaceXmlParser{
//解析一段父节点,返回子节点字符串
Listparser(Stringstr);
}
三、根据接口编写实现类
回车、换行、制表符以及各种注释部分的内容都被删除,简化字符输出。
SimpleXmlFilter.java:
importjava.io.BufferedReader;
importjava.io.File;
importjava.io.FileNotFoundException;
importjava.io.FileReader;
importjava.io.IOException;
publicclassSimpleXmlFilterimplementsXmlFilter{
privateStringtext;
//常用的过滤正则表达式
publicfinalstaticString[]REG={"\t","<\\?.*?\\?>","","<%.*?%>","\\s{2,}"};
//读取xml文档返回字符串
publicSimpleXmlFilter(Filefile)throwsIOException{
BufferedReaderin=newBufferedReader(newFileReader(file));
StringBufferbuff=newStringBuffer();
Stringtemp=null;
while((temp=in.readLine())!=null){
buff.append(temp);
}
in.close();
text=buff.toString().trim();
}
@Override
publicStringfilter(){
returnfilter(REG);
}
@Override
publicStringfilter(String[]regex){
Stringresult=text;
for(Stringreg:regex){
result=result.replaceAll(reg,"");
}
returnresult;
}
}
主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。
SimpleXmlParser.java:
importjava.util.ArrayList;
importjava.util.List;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
publicclassSimpleXmlParserimplementsXmlParser{
@Override
publicListparser(Stringtext){
ListchildrenDocs=newArrayList();
//捕获根节点中间的文本
Patternp=Pattern.compile("<.*?>(.*)");
Matcherm=p.matcher(text);
if(m.matches()){
Stringinner=m.group(1);
//匹配节点字符串
p=Pattern.compile("<(.*?)>");
m=p.matcher(inner);
while(m.find()){
Strings1=m.group(1);
//如果节点以/结尾,代表此节点不包含子节点
if(s1.endsWith("/")){
childrenDocs.add(m.group());
//如果节点既不以/开头,也不以/结尾则表示需要查找对应的闭合节点
}elseif(!s1.startsWith("/")&&!s1.endsWith("/")){
//计算起始字符数
intstart=m.end()-m.group().length();
//如果捕获到未闭合节点则index++,如果捕获到闭合节点则index--
intindex=1;
while(m.find()){
Strings2=m.group(1);
if(!s2.startsWith("/")&&!s2.endsWith("/")){
index++;
}elseif(s2.startsWith("/")){
index--;
}
//找到符合条件的闭合节点则循环终止
if(index==0){
break;
}
}
//计算结束字符数
intend=m.end();
//截取对应字符串
childrenDocs.add(inner.substring(start,end));
}
}
}
returnchildrenDocs;
}
}
四、编写NodeBuilder类
根据过滤器和解析器获取Node节点各属性的值。
NodeBuilder.java:
importjava.io.File;
importjava.io.IOException;
importjava.util.List;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
//生成Node
publicclassNodeBuilder{
privateNoderoot=newNode();
privateXmlParserparser;
privateXmlFilterfilter;
//提供合适的过滤器和解析器
publicNodeBuilder(XmlParserparser,XmlFilterfilter){
this.parser=parser;
this.filter=filter;
}
publicNodegetRoot(String...regex){
Stringstr=null;
if(regex.length==0){
str=filter.filter();
}else{
str=filter.filter(regex);
}
buildNodeTree(str,root);
returnroot;
}
//设置节点类型
privatevoidbuildNodeTitle(Stringstr,Noden){
Patternp=Pattern.compile("<.*?>");
Matcherm=p.matcher(str);
if(m.find()){
Stringtemp=m.group();
Strings=temp.substring(1,temp.length()-1).split("")[0];
if(s.endsWith("/")){
n.setTitle(s.substring(0,s.length()-1));
}else{
n.setTitle(s.split("")[0]);
}
}
}
//设置节点属性集合
privatevoidbuildNodeAttribute(Stringstr,Noden){
Patternp=Pattern.compile("<.*?>");
Matcherm=p.matcher(str);
if(m.find()){
Stringtemp=m.group();
Strings=temp.substring(1,temp.length()-1);
//匹配字符串
p=Pattern.compile("(\\S*)=\"(.*?)\"");
m=p.matcher(s);
while(m.find()){
Stringkey=m.group(1).trim();
Stringvalue=m.group(2).trim();
n.getAttribute().put(key,value);
}
//匹配数字
p=Pattern.compile("(\\S*)=(-?\\d+(\\.\\d+)?)");
m=p.matcher(s);
while(m.find()){
Stringkey=m.group(1).trim();
Stringvalue=m.group(2).trim();
n.getAttribute().put(key,value);
}
}
}
//设置节点内容,节点的内容是删除了所有子节点字符串以后剩下的部分
privatevoidbuildNodeText(Stringstr,Noden){
Patternp=Pattern.compile("<.*?>(.*)");
Matcherm=p.matcher(str);
ListchildrenDocs=parser.parser(str);
if(m.find()){
Stringtemp=m.group(1);
for(Strings:childrenDocs){
temp=temp.replaceAll(s,"");
}
n.setText(temp.trim());
}
}
//通过递归生成完整节点树
privatevoidbuildNodeTree(Stringstr,Noden){
buildNodeTitle(str,n);
buildNodeAttribute(str,n);
buildNodeText(str,n);
//如果存在子节点则继续下面的操作
if(!parser.parser(str).isEmpty()){
//对每一个子节点都应该继续调用直到递归结束
for(Stringtemp:parser.parser(str)){
Nodechild=newNode();
buildNodeTitle(temp,child);
buildNodeAttribute(temp,child);
buildNodeText(temp,child);
n.getChildNode().add(child);
buildNodeTree(temp,child);
}
}
}
}
五、测试
编写xml测试文件
测试文件:
packagemessagebefore! classmessageinner. packagemessagemiddle! methodmessageinner! packagemessageafter!
编写测试类
Demo.java:
importjava.io.File;
importjava.io.IOException;
publicclassDemo{
publicstaticvoidmain(String[]args){
Filef=newFile("xxx");
XmlFilterfilter=null;
try{
filter=newSimpleXmlFilter(f);
}catch(IOExceptione){
e.printStackTrace();
}
XmlParserparser=newSimpleXmlParser();
NodeBuilderbuilder=newNodeBuilder(parser,filter);
Nodenode=builder.getRoot();
System.out.println(node);
}
}
输出:
packagemessagebefore!packagemessagemiddle!packagemessageafter! classmessageinner. methodmessageinner!
PS:这里再为大家提供2款非常方便的正则表达式工具供大家参考使用:
JavaScript正则表达式在线测试工具:
http://tools.jb51.net/regex/javascript
正则表达式在线生成工具:
http://tools.jb51.net/regex/create_reg
更多关于java算法相关内容感兴趣的读者可查看本站专题:《Java正则表达式技巧大全》、《Java数据结构与算法教程》、《Java操作DOM节点技巧总结》、《Java文件与目录操作技巧汇总》和《Java缓存操作技巧汇总》
希望本文所述对大家java程序设计有所帮助。