文章目录
P1 XML文件
之前设计的简单学生信息管理系统,除了交互性需要完善外,最重要的问题就是输入的数据无法保存,为了解决这个问题,这里引入XML文件来保存录入的数据
由于从程序中生成XML文件的过程较为复杂,此处只进行XML文件的创建和解析
1 XML文件概述
(1) 可扩展标记语言:XML
XML,即Extensible Markup Language,即可扩展标记语言,是一种允许用户对自己的标记语言进行定义的源语言,它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化语言
1998年,W3C发布了XML1.0规范,使用它来简化Internet的文档信息传输
XML是一种能描述层次结构的数据描述规范,使用它可以方便地描述和存储复杂数据
(2) XML文件的书写规则和语法要求
对于先前的学生信息,可以大概描述为:
学生1:
学号:20181001
姓名:张三
性别:男
出生日期:1999/10/1
爱好:
篮球
足球
羽毛球
简介:热爱运动
有这个模板生成XML文件前,需要先了解XML文件的语法和规则:
1,标签:标签是为了能够表达有一定意义的数据的起,止标记,分为开始标签和结束标签,在起止标签之间的文本称为内容,基本格式如下:
<student>
<id>20181001</id>
</student>
标签大小写敏感,起止标签必须成对出现,且保持严格的顺序
2,标签属性:在开始标签中,可以通过 键 = "值"的形式定义一或多个属性,标签属性中的值必须为字符串,即需要双引号
<student id = "20181001" name = "张三" sex = "男"></student>
3,XML文件头:XML文件应当以一个文件头开始,如
<?xml version="1.0"?>
4,根标签:XML文件里的众多标签,都应该在一个根标签中书写,如
<students>
<student id = "20181001">张三</student>
</students>
5,注释:XML文件中的注释在解析XML时,将被忽略
XML中的注释:
<!-- 注释文字 -->
2 创建一个XML文件
通过eclipse自动生成XML文件框架来编写XML文件内容:
file->new->other->XML->XML File->next->选择目录并命名
通过上述的步骤,由eclipse生成了一个XML文件的框架:
点击source进行编辑,下面给出一个学生信息XML文件示例:
P2 解析给定的XML文件
通过编程,可以生成,修改,添加,删除XML文件及其内容,也可以从XML文件中取得数据
这里着重于后者,即XML文件的解析,就是通过程序从XML文件中取出特定的标签内容,或者属性值,并将这些值转换成对应的类对象
1 XML解析器和W3C
java提供了专门的XML解析器,且不止一个,这里使用W3C标准化了的DOM解析器
DOM(Document Object Model),即文件对象模型,是一种树型解析器,对于上述的XML文件,明显存在着一对多的关系,即树型结构
2 从XML文件中解析数据
先来试着从XML文件中取出学生的学号:
解析程序:
package GetFromXML;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
//注意导包时使用w3c的包
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class Test {
public static void main(String[] args) {
try {
/**
* 用于找到xml文件并转换成document类的对象
*/
InputStream is = Class.class.getResourceAsStream("/student.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(is);
//从document中取出名为student的标签并存储到数组中
NodeList studentList = document.getElementsByTagName("student");
//此处长度为2,进行两次遍历取出两个学生的student标签
for(int index = 0; index < studentList.getLength(); index++) {
//使用数组的.item(下标)方法将取出的一行转换成Element类的对象
Element student = (Element)studentList.item(index);
//通过Element的getAttribute取出属性名为id的值存为String类型
String id = student.getAttribute("id");
System.out.println("id:" + id);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接下来解析学生标签中的所有属性:
上述的操作从student标签中取得了所有的属性,观察XML文件,发现在student标签下面还有两种标签hobby和introduce,这两种标签是包含在student标签内部的,作为student标签的一部分内容存在,为了解析它们,需要两个并列的循环嵌套在第一个循环中
解析程序
package GetFromXML;
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 class Test2 {
public static void main(String[] args) {
try {
InputStream is = Class.class.getResourceAsStream("/student.xml");
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(is);
NodeList studentList = document.getElementsByTagName("student");
for(int index = 0; index < studentList.getLength(); index++) {
StringBuffer str = new StringBuffer();
/**
* 解析student标签内的标签属性
*/
Element student = (Element)studentList.item(index);
String id = student.getAttribute("id");
str.append("id=").append(id);
String name = student.getAttribute("name");
str.append(" name=").append(name);
String sex = student.getAttribute("sex");
str.append(" sex=").append(sex);
String birth = student.getAttribute("birth");
str.append(" birthday :").append(birth);
/**
* 解析student标签中作为内容的hobby标签
*/
NodeList hobbies = student.getElementsByTagName("hobby");
for(int i = 0; i < hobbies.getLength(); i++) {
Element hobby = (Element)hobbies.item(i);
String hobbyId = hobby.getAttribute("id");
String hobbyName = hobby.getTextContent();
str.append("\n").append(hobbyId).append(":").append(hobbyName);
}
/**
* 解析student标签中作为内容的introduce标签
*/
NodeList introduceList = student.getElementsByTagName("introduce");
for(int j = 0; j < introduceList.getLength(); j++) {
Element introduce = (Element)introduceList.item(j);
String introduceText = introduce.getTextContent().trim();
str.append("\n简介: ").append(introduceText);
}
System.out.println(str);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
上述接卸学生信息XML文件中最需要注意的是XML文件的结构,通过初始化NodeList数组存储标签名中所含的所有信息,之后在循环中,通过下标将取得它们并转换成Element类型的对象,再通过getAttribute()方法和getTextContent()方法来获得数据
P3 设计一个XML文件解析工具
上述对学生信息XML文件解析的程序是没有任何泛用性的,它不能解析其他的XML文件,哪怕同样是学生信息但其中稍有变化,就无法完成任务,java的一项编程思想是面向工具编程,且做到代码复用,为此需要创建一个泛用的工具类来解析各式的XML文件,即把它做成jar包
1 静态块和本地块
在完成设计工具前,先了解静态块和本地块的概念:
通过上述简单的示例可以得知,本地块就是一段java代码,而本地块加上static就成了静态块,静态块发生在类加载过程后,类实例化过程前,即只会被执行一次
2 将XML解析过程工具化
首先分析上述解析学生信息XML文件的程序中的:
1 InputStream is = Class.class.getResourceAsStream("/student.xml");
2 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
3 DocumentBuilder db = dbf.newDocumentBuilder();
4 Document document = db.parse(is);
上述的这段程序可以看作一个固定的套路,而且从最后一行可以看出,2,3行的程序就是为了得到一个DocumentBuilder类的对象,且对于不同的XML文件解析,这个对象只需要一个就足够了,可以利用静态块只执行一次的特点,只产生一个DocumentBuilder对象,即单例模式
在完成XML解析类前,可以将这段程序设置为static成员作为一个静态块,且在类加载过程后,没有产生XML解析类对象前,就可以直接生成一个DocumentBuilder类对象以供使用
在得到了DocumentBuilder对象后,还需要通过parse(InputStream)方法才能打开要处理的XML文件,并得到所需要的Document类对象,且这一步需要用户提供的XML文件的路径参数,因此需要做成一个方法
至此XML解析工具就可以得到所要解析的XML文件的Document对象了
3 分析“静”与“动”
回顾上述的代码,其中的第26行和第50(64)行都是取得指定标签的节点列表,但是document对象和student对象的类型不同(document是要处理的文件主体,需要从里面拿出学生标签,student是文件中每个学生,需要从中得到这个学生的详细信息),分别是Document类和Element类,这需要两个参数类型不同的方法分别处理,而且由于标签名称也不相同,需要存在字符串类型的参数:
package GetFromXML;
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 class Demo {
private static DocumentBuilder db;
/**
* 静态块只产生一个DocumentBuilder类的对象
*/
static {
try {
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
/**
* 根据给定的XML文件路径,产生一个Document类的对象
*/
public Document openXML(String xmlPath) throws SAXException, IOException {
Document document = null;
InputStream is = Class.class.getResourceAsStream(xmlPath);
document = db.parse(is);
return document;
}
/**
* 处理传入的document,获得标签列表
*/
public void parseTag(Document document, String tagName) {
NodeList nodeList = document.getElementsByTagName(tagName);
for(int index = 0; index < nodeList.getLength(); index++) {
Element node = (Element)nodeList.item(index);
//TODO
}
}
/**
* 处理传入的element,获取内层信息
*/
public void parseTag(Element element, String tagName) {
NodeList nodeList = element.getElementsByTagName(tagName);
for(int index = 0; index < nodeList.getLength(); index++) {
Element node =(Element)nodeList.item(index);
//TODO
}
}
}
这样就粗略完成了一个解析工具,但是TODO部分的内容却不得而知,因为无法确定用户需要处理哪个XML文件,这个XML文件内的结构和标签里是什么东西,这时就可以使用抽象方法,只提供结构框架,让用户完成具体的抽象方法
4 将XML解析类做成抽象类,并解析XML文件
现在为解析类添加一个抽象方法,用来执行具体的处理:
package CoisiniUtil;
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 DocumentBuilder db;
public XMLParser() {
}
/**
* 静态块只产生一个DocumentBuilder类的对象
*/
static {
try {
db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
/**
* 根据给定的XML文件路径,产生一个Document类的对象
*/
public static Document openXML(String xmlPath)
throws XMLNotFoundException,SAXException, IOException {
Document document = null;
InputStream is = Class.class.getResourceAsStream(xmlPath);
if(null == is) {
throw new XMLNotFoundException("xml文件未找到");
}
document = db.parse(is);
return document;
}
/**
* 需要用户完善的抽象方法
*/
public abstract void dealElement(Element element, int index);
/**
* 处理传入的document,获得标签列表
*/
public void dealElementList(Document document, String tagName) {
NodeList nodeList = document.getElementsByTagName(tagName);
for(int index = 0; index < nodeList.getLength(); index++) {
Element node = (Element)nodeList.item(index);
//用户需要实现具体操作的parserElement方法
dealElement(node, index);
}
}
/**
* 处理传入的element,获取内层信息
*/
public void dealelementList(Element element, String tagName) {
NodeList nodeList = element.getElementsByTagName(tagName);
for(int index = 0; index < nodeList.getLength(); index++) {
Element node =(Element)nodeList.item(index);
//用户需要实现具体操作的parserElement方法
dealElement(node, index);
}
}
}
再使用XMLParser抽象类解析XML文件:
package GetFromXML;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import CoisiniUtil.XMLNotFoundException;
import CoisiniUtil.XMLParser;
public class Demo {
public static void main(String[] args) {
//创建一个XMLParser类的对象
//并使用匿名内部类完成它的抽象方法
XMLParser xmlParser = new XMLParser() {
@Override
public void dealElement(Element element, int index) {
/**
* 解析外层标签的属性
*/
StringBuffer str = new StringBuffer();
String id = element.getAttribute("id");
str.append("id=").append(id);
String name = element.getAttribute("name");
str.append(" name=").append(name);
String sex = element.getAttribute("sex");
str.append(" sex=").append(sex);
String birth = element.getAttribute("birth");
str.append(" birthday :").append(birth);
/**
* 创建两个匿名对象,并完成抽象方法
* 调用该类的dealElementList
* 使用方法重载获取详细信息
*/
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
String hobbyId = element.getAttribute("id");
String hobbyName = element.getTextContent();
str.append("\n").append(hobbyId).append(":").append(hobbyName);
}
}.dealelementList(element, "hobby");
new XMLParser() {
@Override
public void dealElement(Element element, int index) {
String introduceText = element.getTextContent().trim();
str.append("\n简介: ").append(introduceText);
}
}.dealelementList(element, "introduce");
System.out.println(str);
}
};
try {
//由文件路径获得Document类的对象
Document document = XMLParser.openXML("/student.xml");
//方法重载处理标签列表
xmlParser.dealElementList(document, "student");
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (XMLNotFoundException e) {
e.printStackTrace();
}
}
}
利用工具成功完成了XML文件的解析
P4 将XMLParser和XMLNotFoundException做成工具
1 生成jar工具包
现在将上面完成的XMLParser和XMLNotFoundException放在一个javaproject下的同一个包内,做成jar工具包
1,将整个工程导出成jar文件
2,配置条件,选择生成的目录
3,在给定的目录下生成了jar工具包
2 使用jar工具包
如果想要使用该jar包:
1,复制jar包,在需要的工程下src下创建一个lib目录,将jar包粘贴:
2,将jar包加入到工程中:
3,加入后再导包即可:
jar包是不可被更改的
P5 jd-gui
为了查看某些jar包内的源码内容,可以使用带有GUI的jar,jd-gui
也可下载更高版本,jd-gui可以反编译class文件,带有图形界面
使用时找到jd-gui所在的目录,打开cmd,输入:
java -jar jd-gui-版本号.jar
即可使用