在今后的Java学习过程中,我们会遇到很多使用XML文件等来存储必要信息的情况,每次都要进行解析,这样岂不是很繁琐。在学习Java的过程中,我们必须要有一种思想–工具化。将所有重复动作工具化,使其解决具有普遍性的情况。下面就将之前所讲的《XML文件解析》进行工具化,使其适用于大多数解析情况。
如果看不懂这篇文章,可以先看看前面提到的,了解一下XML解析的基本过程。
在此强调一下,本次编写的工具,只是根据“标签名”进行解析。
一、思路
为了使解析过程工具化,首先,我们必须找到解析文件的共同点;其次,因为每个文件需要解析出来的内容不一样,该如何处理这部分也是一个问题。
1.文件读取方式
无论解析任何一个文件,先要找到这个文件,并对文件进行读取。为了适应大多数情况,给出两种读取文件的方式。
1)以输入流形式读取
private static Document getDocument(InputStream is) {
Document document = null;
try {
document =db.parse(is);
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return document;
}
2)以字符串形式读取
本质上,以字符串形式读取还是调用了输入流形式读取的方法,不过,一般在确定读取的文件时,我们是以字符串形式传入,而不是输入流形式。
public static Document getDocument(String xmlPath) {
InputStream is = XMLParser.class.getResourceAsStream(xmlPath);
if(is == null) {
System.out.println("xmlPath["+xmlPath+"]不存在!");
return null;
}
return getDocument(is);
}
2.不同类型节点解析
在制作工具的过程中,我们针对XML文件结构将其分为两种,文档节点解析和元素节点解析。
1)文档节点(Document)解析
/*
* @param document 解析的文件节点
* @param tagName 需要解析的标签
*/
public void parseTag(Document document,String tagName) {
NodeList nodeList = document.getElementsByTagName(tagName);
for(int index = 0; index<nodeList.getLength(); index++) {
Element ele = (Element) nodeList.item(index);
//后面对ele的进一步解析:取属性、取textContent或者进一步取Element,都是使用工具的人做的事,
//所以此处使用抽象方法,有使用该工具的人进行具体实现
dealElement(ele, index);
}
}
2)元素(Element)节点解析
/**
* @param element 需要解析的元素节点
* @param tagName 需要解析的标签
*/
public void parseTag(Element element,String tagName) {
NodeList nodeList = element.getElementsByTagName(tagName);
for(int index = 0; index<nodeList.getLength(); index++) {
Element ele =(Element)nodeList.item(index);
dealElement(ele, index);
}
}
其实文档节点解析和元素节点解析,都是差不多,仔细观察就会发现两者除了第一句里调用getElementsByTagName()
方法的对象不同以外,其他过程都是一样的。
3.具体解析过程
由于每个文件内容不一样,所以无法将这部分进行工具化,故使用抽象方法,将来这部分由使用该工具的人来进行具体实现。
public abstract void dealElement(Element element,int index) ;
二、工具的具体实现
看了之前文章的人必然知道,在解析时有四句固定搭配,不过在上面的思路中已经解决了其中两句,还有两句将会在代码中给出具体处理。
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public abstract class XMLParser {
private static final DocumentBuilderFactory dbf;
private static DocumentBuilder db;
//单例模式,只在该程序第一次运行时执行一次,之后再运行不再执行
static {
dbf = DocumentBuilderFactory.newInstance();
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
public XMLParser() {
}
//读取文件,输入流
private static Document getDocument(InputStream is) {
Document document = null;
try {
document =db.parse(is);
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return document;
}
//读取文件,字符串
public static Document getDocument(String xmlPath) {
InputStream is = XMLParser.class.getResourceAsStream(xmlPath);
if(is == null) {
System.out.println("xmlPath["+xmlPath+"]不存在!");
return null;
}
return getDocument(is);
}
public abstract void dealElement(Element element,int index) ;
/*
* @param document 解析的文件
* @param tagName 需要解析的标签
*/
public void parseTag(Document document,String tagName) {
NodeList nodeList = document.getElementsByTagName(tagName);
for(int index = 0; index<nodeList.getLength(); index++) {
Element ele = (Element) nodeList.item(index);
//后面对ele的进一步解析:取属性、取textContent或者进一步取Element,都是使用工具人做的事
//此处该使用抽象方法
dealElement(ele, index);
}
}
/**
* @param element 需要解析的元素
* @param tagName 需要解析的标签
*/
public void parseTag(Element element,String tagName) {
NodeList nodeList = element.getElementsByTagName(tagName);
for(int index = 0; index<nodeList.getLength(); index++) {
Element ele =(Element)nodeList.item(index);
dealElement(ele, index);
}
}
}
三、实例–使用工具
要使用工具,需要将之前代码转为jar包,并且导入需要使用该工具的项目中。在这里,我就不进行描述了,需要的直接百度即可。
<?xml version="1.0" encoding="UTF-8"?>
<students>
<student id="00001" name="张小三" sex="男" birtdhday="2002-10-3">
<hobby>游泳</hobby>
<hobby>跳绳</hobby>
<hobby>打游戏</hobby>
<hobby>跑步</hobby>
<hobby>睡觉</hobby>
<introduce>
活泼开朗的小鲜肉!
</introduce>
</student>
<student id="00002" name="朱子玉" sex="女" birtdhday="2008-10-21">
<hobbies>
<hobby>游泳</hobby>
<hobby>画画</hobby>
<hobby>学习</hobby>
<hobby>古筝</hobby>
</hobbies>
<introduce>
小仙女一枚!
</introduce>
</student>
</students>
在上述xml文件中,student标签为文档节点,hobbies和introduce为元素节点(可能理解有误),如果不理解,可看这篇文章——《解析XML Node与Element的区别》。
需要注意的是,由于我们是根据标签名解析,所以在解析过程中,只需要处理有内容的标签即可。比如我们可以不处理 “根标签students” ,因为我们需要解析的全部内容直接通过 "标签student"获取。
package com.mec.gewei.xml_parser.core;
import org.w3c.dom.Element;
import com.mec.util.XMLParser;
public class TestUtil{
public static void main(String[] args) {
//使用匿名内部类的方式调用工具
//此处调用了文档节点解析的方法
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
Student stu = new Student();
String id = element.getAttribute("id");
stu.setId(id);
String name = element.getAttribute("name");
stu.setName(name);
String sex = element.getAttribute("sex");
stu.setSex(sex);
String birthday = element.getAttribute("birtdhday");
stu.setBirthday(birthday);
//此处调用元素节点解析
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
String hobbystr = element.getTextContent();
stu.addHobbies(hobbystr);
}
}.parseTag(element,"hobby");
//此处调用元素节点解析
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
String introduce = element.getTextContent().trim();
stu.setIntroduce(introduce);
}
}.parseTag(element, "introduce");
System.out.println(stu);
}
}.parseTag(XMLParser.getDocument("/student.att.xml"), "student");
}
}
运行结果: