通过正则表达式实现简单xml文件解析
这是我通过正则表达式实现的xml文件解析工具,有些XHTML文件中包含特殊符号,暂时还无法正常使用。
设计思路:常见的xml文件都是单根树结构,工具的目的是通过递归的方式将整个文档树装载进一个Node对象。xml文档树上的每一个节点都能看做一个Node对象,它拥有title、attribute和text三个自身变量以及一个childrenNode集合用来存放子节点,使用正则表达式完整装载。
一、编写Node类
Node对象是文档解析的基础,最终可以通过对象的不同属性实现对文档信息的访问。
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry; public class Node implements Serializable {
// 可以对Node对象持久化保存
private static final long serialVersionUID = 1L;
private int id;
// 节点类型
private String title;
// 节点内容
private String text;
// 节点属性集合
private Map<String, String> attributes = new HashMap<String, String>();
// 子节点集合
private List<Node> childNodes = new LinkedList<Node>(); public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public Map<String, String> getAttribute() {
return attributes;
} public void setAttribute(Map<String, String> attribute) {
this.attributes = attribute;
} public String getText() {
return text;
} public void setText(String text) {
this.text = text;
} public List<Node> getChildNode() {
return childNodes;
} public void setChildNode(List<Node> childNode) {
this.childNodes = childNode;
} // 将属性集合转换成一条完整的字符串
private String attrToString() {
if (attributes.isEmpty()) {
return "";
}
Iterator<Entry<String, String>> its = attributes.entrySet().iterator();
StringBuffer buff = new StringBuffer();
while (its.hasNext()) {
Entry<String, String> entry = its.next();
buff.append(entry.getKey() + "=\"" + entry.getValue() + "\" ");
}
return " " + buff.toString().trim();
} // 输出完整的节点字符串也用到了递归
@Override
public String toString() {
String attr = attrToString();
if (childNodes.isEmpty() && text == null) {
return "<" + title + attr + "/>\n";
} else if (childNodes.isEmpty() && text != null) {
return "<" + title + attr + ">\n" + text + "\n" + "</" + title + ">\n";
} else {
StringBuffer buff = new StringBuffer();
buff.append("<" + title + attr + ">\n");
if (!text.isEmpty()) {
buff.append(text + "\n");
}
for (Node n : childNodes) {
buff.append(n.toString());
}
buff.append("</" + title + ">\n");
return buff.toString();
}
}
}
Node.java
二、创建接口
把文档的读取和分析抽象成接口方便今后替换实现。
过滤器:读取文档的字符流并删除注释的部分。这些信息通常是提供给人阅读的,程序分析直接忽略。
/*
* 过滤器的作用是删除xml文件中不重要的部分。
* 通常都是一些注释性文字,不需要被机器解析。
*/
public interface XmlFilter {
String filter(); // 提供自定义正则表达式,识别符合过滤条件的字符串
String filter(String[] regex);
}
XmlFilter.java
解析器:将一个父节点解析成多条子节点的字符串。如果返回值为null,代表当前节点下不存在可以继续解析的对象。
import java.util.List;
/*
* 解析器可以对一段完整的父节点字符串提供解析服务。
* 将一条父节点的字符串解析成为多条子节点字符串
*/
public interface XmlParser {
// 解析一段父节点,返回子节点字符串
List<String> parser(String str);
}
XmlParser.java
三、根据接口编写实现类
回车、换行、制表符以及各种注释部分的内容都被删除,简化字符输出。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; public class SimpleXmlFilter implements XmlFilter {
private String text;
// 常用的过滤正则表达式
public final static String[] REG = { "\t", "<\\?.*?\\?>", "<!.*?>", "<%.*?%>", "\\s{2,}" }; // 读取xml文档返回字符串
public SimpleXmlFilter(File file) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(file));
StringBuffer buff = new StringBuffer();
String temp = null;
while ((temp = in.readLine()) != null) {
buff.append(temp);
}
in.close();
text = buff.toString().trim();
} @Override
public String filter() {
return filter(REG);
} @Override
public String filter(String[] regex) {
String result = text;
for (String reg : regex) {
result = result.replaceAll(reg, "");
}
return result;
} }
SimpleXmlFilter.java
主要是通过正则表达式区分一个节点内部的子节点,考虑到节点的类型我将它们分为自闭合与非自闭合两种类型。<title attributes .../>这样的节点属于自闭合类型,它们不包含子节点和text属性,它们属于文档树的叶子节点。<title attributes ...>text ...</title>这样的节点属于非自闭合类型,它们属于文档树的分支节点。
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class SimpleXmlParser implements XmlParser { @Override
public List<String> parser(String text) {
List<String> childrenDocs = new ArrayList<String>();
// 捕获根节点中间的文本
Pattern p = Pattern.compile("<.*?>(.*)</.*?>");
Matcher m = p.matcher(text);
if (m.matches()) {
String inner = m.group(1);
// 匹配节点字符串
p = Pattern.compile("<(.*?)>");
m = p.matcher(inner);
while (m.find()) {
String s1 = m.group(1);
// 如果节点以/结尾,代表此节点不包含子节点
if (s1.endsWith("/")) {
childrenDocs.add(m.group());
// 如果节点既不以/开头,也不以/结尾则表示需要查找对应的闭合节点
} else if (!s1.startsWith("/") && !s1.endsWith("/")) {
// 计算起始字符数
int start = m.end() - m.group().length();
// 如果捕获到未闭合节点则index++,如果捕获到闭合节点则index--
int index = 1;
while (m.find()) {
String s2 = m.group(1);
if (!s2.startsWith("/") && !s2.endsWith("/")) {
index++;
} else if (s2.startsWith("/")) {
index--;
}
// 找到符合条件的闭合节点则循环终止
if (index == 0) {
break;
}
}
// 计算结束字符数
int end = m.end();
// 截取对应字符串
childrenDocs.add(inner.substring(start, end));
}
}
}
return childrenDocs;
} }
SimpleXmlParser.java
四、编写NodeBuilder类
根据过滤器和解析器获取Node节点各属性的值。
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern; // 生成Node
public class NodeBuilder {
private Node root = new Node();
private XmlParser parser;
private XmlFilter filter; // 提供合适的过滤器和解析器
public NodeBuilder(XmlParser parser, XmlFilter filter) {
this.parser = parser;
this.filter = filter;
} public Node getRoot(String... regex) {
String str = null;
if (regex.length == 0) {
str = filter.filter();
} else {
str = filter.filter(regex);
}
buildNodeTree(str, root);
return root;
} // 设置节点类型
private void buildNodeTitle(String str, Node n) {
Pattern p = Pattern.compile("<.*?>");
Matcher m = p.matcher(str);
if (m.find()) {
String temp = m.group();
String s = 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]);
}
}
} // 设置节点属性集合
private void buildNodeAttribute(String str, Node n) {
Pattern p = Pattern.compile("<.*?>");
Matcher m = p.matcher(str);
if (m.find()) {
String temp = m.group();
String s = temp.substring(1, temp.length() - 1);
// 匹配字符串
p = Pattern.compile("(\\S*)=\"(.*?)\"");
m = p.matcher(s);
while (m.find()) {
String key = m.group(1).trim();
String value = m.group(2).trim();
n.getAttribute().put(key, value);
}
// 匹配数字
p = Pattern.compile("(\\S*)=(-?\\d+(\\.\\d+)?)");
m = p.matcher(s);
while (m.find()) {
String key = m.group(1).trim();
String value = m.group(2).trim();
n.getAttribute().put(key, value);
}
}
} // 设置节点内容,节点的内容是删除了所有子节点字符串以后剩下的部分
private void buildNodeText(String str, Node n) {
Pattern p = Pattern.compile("<.*?>(.*)</.*?>");
Matcher m = p.matcher(str);
List<String> childrenDocs = parser.parser(str);
if (m.find()) {
String temp = m.group(1);
for (String s : childrenDocs) {
temp = temp.replaceAll(s, "");
}
n.setText(temp.trim());
}
} // 通过递归生成完整节点树
private void buildNodeTree(String str, Node n) {
buildNodeTitle(str, n);
buildNodeAttribute(str, n);
buildNodeText(str, n);
// 如果存在子节点则继续下面的操作
if (!parser.parser(str).isEmpty()) {
// 对每一个子节点都应该继续调用直到递归结束
for (String temp : parser.parser(str)) {
Node child = new Node();
buildNodeTitle(temp, child);
buildNodeAttribute(temp, child);
buildNodeText(temp, child);
n.getChildNode().add(child);
buildNodeTree(temp, child);
}
}
}
}
NodeBuilder.java
五、测试
编写xml测试文件
<package>
<!-- 这里是注释1 -->
package message before!
<class id="exp1" path="www.sina.com"/>
<class id="exp2">
<class id="inner">
class message inner.
</class>
</class>
package message middle!
<!-- 这里是注释2 -->
<class id="exp3">
<method id="md" name="setter" order=1>
<!-- 这里是注释3 -->
<!-- 这里是注释4 -->
<para ref="String"/>
<para ref="exp1">
method message inner!
</para>
</method>
</class>
package message after!
</package>
测试文件
编写测试类
import java.io.File;
import java.io.IOException; public class Demo {
public static void main(String[] args) {
File f = new File("xxx");
XmlFilter filter = null;
try {
filter = new SimpleXmlFilter(f);
} catch (IOException e) {
e.printStackTrace();
}
XmlParser parser = new SimpleXmlParser();
NodeBuilder builder = new NodeBuilder(parser, filter);
Node node = builder.getRoot();
System.out.println(node);
}
}
Demo.java
输出
<package>
package message before!package message middle!package message after!
<class path="www.sina.com" id="exp1"/>
<class id="exp2">
<class id="inner">
class message inner.
</class>
</class>
<class id="exp3">
<method name="setter" id="md" order="1">
<para ref="String"/>
<para ref="exp1">
method message inner!
</para>
</method>
</class>
</package>
System.out.println...
六、后记
这个工具是我春节期间在旅游的途中无聊的时候写的,如果以后有用可能还会回来修改。暂时先写到这里吧...
通过正则表达式实现简单xml文件解析的更多相关文章
- android基础知识13:AndroidManifest.xml文件解析
注:本文转载于:http://blog.csdn.net/xianming01/article/details/7526987 AndroidManifest.xml文件解析. 1.重要性 Andro ...
- Android之AndroidManifest.xml文件解析
转自:Android学习笔记之AndroidManifest.xml文件解析 一.关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文 ...
- Python实现XML文件解析
1. XML简介 XML(eXtensible Markup Language)指可扩展标记语言,被设计用来传输和存储数据,已经日趋成为当前许多新生技术的核心,在不同的领域都有着不同的应用.它是web ...
- XML文件解析-DOM4J方式和SAX方式
最近遇到的工作内容都是和xml内容解析相关的. 1图片数据以base64编码的方式保存在xml的一个标签中,xml文件通过接口的方式发送给我,然后我去解析出图片数据,对图片进行进一步处理. 2.xml ...
- XML文件解析之JDOM解析
1.JDOM介绍 JDOM的官方网站是http://www.jdom.org/,JDOM解析用到的jar包可以在http://www.jdom.org/dist/binary/中下载,最新的JDOM2 ...
- 八、Android学习第七天——XML文件解析方法(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 八.Android学习第七天——XML文件解析方法 XML文件:exten ...
- 9.XML文件解析
一.XML简介 XML(EXtensible Markup Language),可扩展标记语言 特点:XML与操作系统.编程语言的开发平台无关 实现不同系统之间的数据交换 作用:数据交互 配置应用程序 ...
- Python3将xml文件解析为Python对象
一.说明 从最开始写javascript开始,我就很烦感使用getElementById()等函数来获取节点的方法,获取了一个节点要访问其子孙节点要么child半天要么就再来一个getElementB ...
- java基础之概谈xml文件解析
XML已经成为一种非常通用的数据交换格式,它的平台无关性,语言无关性,系统无关性,给数据集成与交互带来了极大的方便. 诸多web应用框架,其可配置的编程方式,给我们的开发带来了非常大程度的便捷,但细细 ...
随机推荐
- SQL Server 2008 R2数据库镜像部署
概述 “数据库镜像”是一种针对数据库高可用性的基于软件的解决方案.其维护着一个数据库的两个相同的副本,这两个副本分别放置在不同的SQL Server数据库实例中.建议使用不同位置的两台服务器来承载.在 ...
- windows下virtualbox安装Ubuntu虚拟机
安装Ubuntu的部分省略.下面是设置文件夹共享. 1. 安装virtualBox增强功能包 点击"设备"->"安装增强功能" 2. 在windows下建 ...
- springboot+redis
上篇整合了DB层,现在开始整合缓存层,使用redis. springboot驱动注解,使用spring注入JedisPool便可封装自己的redis工具类. package hello.configu ...
- wifi万能钥匙自媒体平台开放注册(付注册流程)
12月13日,有网友爆料,wifi万能钥匙自媒体开放注册,看来自媒体还没有达到饱和阶段,也印证了自媒体时代才刚刚到来.现在这个自媒体的时代,几乎大多互联网企业都开通了自己的自媒体,比较知名的像今日头条 ...
- find_elements后点击不了抓取的元素
1.莫名其妙抓不到元素,要去看句柄,是不是没有切换 h=driver.current_window_handle nh=driver.window_handles for i in nh: if i! ...
- CACHE COHERENCE AND THE MESI PROTOCOL
COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION In contemporary multi ...
- sublime 添加代码片段(snippets)
1.工具-新代码片段(Tools -> New Snippet) 2. <snippet> <content><![CDATA[ if(\$rs && ...
- 《Linux内核分析》期末总结
Linux内核设计期中总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 前八周博客汇总及总结 Linux内核设计第一周——从汇编语言出发理解计算机工作原理 我们学习了汇编语言的基础知识,这一 ...
- 页面上使用 Thymeleaf 的内联js不当造成了 java.lang.StackOverflowError: null 问题
由于在页面上内联js使用不当,从而在从 Controller 跳转到页面时发生了以下错误: java.lang.StackOverflowError: null at org.thymeleaf.ut ...
- LeetCode Minimum Moves to Equal Array Elements II
原题链接在这里:https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/ 题目: Given a non-empt ...