Java解析XML文件的方式
在项目里,我们往往会把一些配置信息放到xml文件里,或者各部门间会通过xml文件来交换业务数据,所以有时候我们会遇到“解析xml文件”的需求。一般来讲,有基于DOM树和SAX的两种解析xml文件的方式,在这部分里,将分别给大家演示通过这两种方式解析xml文件的一般步骤。
1 XML的文件格式
XML是可扩展标记语言(Extensible Markup Language)的缩写,在其中,开始标签和结束标签必须配套地出现,我们来看下book.xml这个例子。
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <books>
3 <book id="01">
4 <name>Java</name>
5 <price>15</price>
6 <memo>good book</memo>
7 </book>
8 <book id="02">
9 <name>FrameWork</name>
10 <price>20</price>
11 <memo>new book</memo>
12 </book>
13 </books>
整个xml文件是一个文档(document),其中第1行表示文件头,在第2和第13行里,我们能看到配套出现的books标签,从标签头到标签尾的部分那我们称之为元素(element)。
所以我们可以这样说,在books元素里,我们分别于第3到第7行和第8到第12行定义了2个book元素,在每个book元素,比如从第4到第6行,又包含着3个元素,比如第一本书的name元素是<name>Java</name>,它的name元素值是Java。
在第3行里,我们还能看到元素里的属性(attribute),比如这个book元素具有id这个属性,具体id的属性值是01。
2 基于DOM树的解析方式
DOM是Document Object Model(文档对象模型)的缩写,在基于DOM树的解析方式里,解析代码会先把xml文档读到内存里,并整理成DOM树的形式,随后再读取。根据之前部分里给出的book.xml文档,我们可以绘制出如下形式的DOM树。
其中,books属于根(root)结点,也叫根元素,由于它包含着两个book元素,所以第二层是两个book结点,每个book元素包含着3个元素,所以第三层是6个元素。在下面的ParserXmlByDom.java的代码里,我们来看下通过DOM树方式解析book.xml文档的详细步骤。
1 //省略import相关类库的代码
2 public class ParserXmlByDom {
3 public static void main(String[] args) {
4 //创建DOM工厂
5 DocumentBuilderFactory domFactory=DocumentBuilderFactory.newInstance();
6 InputStream input = null;
7 try {
8 //通过DOM工厂获得DOM解析器
9 DocumentBuilder domBuilder=domFactory.newDocumentBuilder();
10 //把XML文档转化为输入流
11 input=new FileInputStream("src/book.xml");
12 //解析XML文档的输入流,得到一个Document
13 Document doc=domBuilder.parse(input);
从第5行到第13行,我们完成了用DOM树解析XML文件的准备工作,具体包括,在第5行里创建了DOM工厂,在第9行通过DOM工厂创建了解析xml文件DocumentBuilder类型对象,在第11行把待解析的xml文件放入到一个InputStream类型的对象里,在第13行通过parse方法把xml文档解析成一个基于DOM树结构的Document类型对象。
14 //得到XML文档的根节点,只有根节点是Element类型
15 Element root=doc.getDocumentElement();
16 // 得到子节点
17 NodeList books = root.getChildNodes();
整个XML文件包含在第13行定义的doc对象里,在第15行里,我们通过getDocumentElement方法得到了根节点(也就是books节点),在第17行,通过getChildNoes方法得到该books节点下的所有子节点,随后开始解析整个xml文档。
需要说明的是,在解析前,我们会通过观察xml文档来了解其中的元素名和属性名,所以在后继的代码里,我们会针对元素名和属性名进行编程。
18 if(books!=null){
19 for(int i=0;i<books.getLength();i++){
20 Node book=books.item(i);
21 //获取id属性
22 if(book.getNodeType()==Node.ELEMENT_NODE){
23 String id=book.getAttributes().getNamedItem("id").getNodeValue();
24 System.out.println("id is:" + id);
25 //遍历book下的子节点
26 for(Node node=book.getFirstChild(); node!=null;node=node.getNextSibling()){
27 if(node.getNodeType()==Node.ELEMENT_NODE){
28 //依次读取book里的name,price和memo三个子元素
29 if(node.getNodeName().equals("name")){
30 String name=node.getFirstChild().getNodeValue();
31 System.out.println("name is:" + name);
32 }
33 if(node.getNodeName().equals("price")){
34 String price=node.getFirstChild().getNodeValue();
35 System.out.println("price is:" + price);
36 }
37 if(node.getNodeName().equals("memo")){
38 String memo=node.getFirstChild().getNodeValue();
39 System.out.println("memo is:" + memo);
40 }
41 }
42 }
43 }
44 }
45 }
第19行的for循环里,我们是遍历book元素通过观察xml文件,我们发现book元素出现了2次,所有这个循环会运行两次,而且,book元素有1个id属性,所有我们需要通过第23行的代码,得到id属性的值。
在文档里,book元素有3个子节点,分别是name,price和memo,所以在代码的26行里,再次使用for循环遍历其中的子节点。在遍历时,我们通过29到32行的代码获取到了book元素里name的值,通过类似的代码后继的33到40行代码里得到了price和memo这两个元素的值。
46 } catch (ParserConfigurationException e) {
47 e.printStackTrace();
48 } catch (FileNotFoundException e) {
49 e.printStackTrace();
50 } catch (IOException e) {
51 e.printStackTrace();
52 } catch (SAXException e) {
53 e.printStackTrace();
54 } catch (Exception e) {
55 e.printStackTrace();
56 }
57 //在finally里关闭io流
58 finally{
59 try {
60 input.close();
61 } catch (IOException e) {
62 e.printStackTrace();
63 }
64 }
65 }
66 }
同样地,在解析完成后,在finally从句里,我们关闭了之前用到的IO流(input对象)。
3 基于事件的解析方式
SAX是Simple API for XML的缩写,不同于DOM的文档驱动,它是事件驱动的,也就是说,它是一种基于回调(callback)函数的解析方式,比如开始解析xml文档时,会调用我们自己定义的startDocument函数,从下表里,我们能看到基于SAX方式里的各种回调函数以及它们被调用的时间点。
函数名 |
调用时间点 |
startDocument |
开始解析xml文档时(解析xml文档第一个字符时)会被调用 |
endDocument |
当解析完xml文档时(解析到xml文档最后一个字符时)会被调用 |
startElement |
当解析到开始标签时会被调用,比如在解析“<name>FrameWork</name>”这个element时,当读到开始标签“<name>”时,会被调用 |
endElement |
当解析到结束标签时会被调用,比如在解析“<name>FrameWork</name>”这个element时,当读到结束标签“</name>”时,会被调用 |
characters |
1行开始后,遇到开始或结束标签之前存在字符,则会调用 2两个标签之间,存在字符,则会调用,比如在解析“<name>FrameWork</name>”时,发现存在FrameWork,则会被调用 3标签和行结束符之前存在字符,则会调用 |
从上表里我们能看到characters方法会在多个场合被回调,但我们最期望的调用场景是第2种,这就要求我们最好在解析xml文档前整理下它的格式,尽量避免第1和第3种情况。在ParserXmlBySAX.java这个案例中,我们通过了编写上述的回调函数,实现了SAX方式解析xml文档的功能。
1 //省略import的代码
2 //基于SAX的解析代码需要继承DefaultHandler类
3 public class ParserXmlBySAX extends DefaultHandler{
4 // 记录当前解析到的节点名
5 private String tagName;
6 //主方法
7 public static void main(String[] argv) {
8 String uri = "src/book.xml";
9 try {
10 SAXParserFactory parserFactory = SAXParserFactory.newInstance();
11 ParserXmlBySAX myParser = new ParserXmlBySAX();
12 SAXParser parser = parserFactory.newSAXParser();
13 parser.parse(uri, myParser);
14 } catch (IOException ex) {
15 ex.printStackTrace();
16 } catch (SAXException ex) {
17 ex.printStackTrace();
18 } catch (ParserConfigurationException ex) {
19 ex.printStackTrace();
20 } catch (FactoryConfigurationError ex) {
21 ex.printStackTrace();
22 }
23 }
在main方法的第8行里,我们指定了待解析xml文档的路径和文件名,在第10行里,我们创建了SAXParserFactory这个类型的SAX解析工厂对象。在第12行,我们通过SAX解析工厂对象,创建了SAXParser这个类型的解析类。在第13行,通过了parse方法启动了解析。
在上文里我们就已经知道,在SAX的方式里,是通过调用各种回调函数来完成解析的,所以在代码里,我们还得自定义各个回调函数,代码如下。
// 处理到文档结尾时,直接输出,不做任何动作
25 public void endDocument() throws SAXException {
26 System.out.println("endDocument");
27 }
28 // 处理到结束标签时,把记录当前标签名的tagName设置成null
29 public void endElement(String uri, String localName, String qName) throws SAXException {
30 tagName = null;
31 }
32 // 开始处理文档时,直接输出,不做任何动作
33 public void startDocument() throws SAXException {
34 System.out.println("startDocument");
35 }
36 // 处理开始标签
37 public void startElement(String uri, String localName, String name,Attributes attributes) throws SAXException {
38 if ("book".equals(name)) { //解析book标签的属性
39 for (int i = 0; i < attributes.getLength(); i++) {
40 System.out.println("attribute name is:" + attributes.getLocalName(i) + " attribute value:" + attributes.getValue(i));
41 }
42 }
43 //把当前标签的名字记录到tagName这个变量里
44 tagName = name;
45 }
46 //通过这个方法解析book的三个子元素的值
47 public void characters(char[] ch, int start, int length)
48 throws SAXException {
49 if(this.tagName!=null){
50 String val=new String(ch,start,length);
51 //如果是name,price或memo,则输出它们的值
52 if("name".equals(tagName))
53 { System.out.println("name is:" + val); }
54 if("price".equals(tagName))
55 { System.out.println("price is:" + val); }
56 if("memo".equals(tagName))
57 { System.out.println("memo is:" + val); }
58 }
59 }
60 }
我们用tagName来保存当前的标签名,是为了解析book元素的name,price和memo这三个子元素。
<name>FrameWork</name>
比如当解析到name这个开始标签时,在第44行里,startElement会把tagname值设置成name,当解析到FramWork时,由于它包含在两个标签之间,所以会被触发第47行的characters方法,在其中的第52行的if判断里,由于得知当前的标签名是name,所以会输出FrameWork这个name元素的值,当解析到</name>这个结束标签时,会触发第29行的endElement方法,在其中的30行里,会把tagName值清空。
这段代码的输出结果如下,其中第1行和第10行分别是在开始解析和完成解析时输出的。
第2行针对id属性的输出是在startElement方法的第40行里被打印的,第3到第5行针对3个book子元素的输出是在characters方法里被打印的。
第2到第5行是针对第一个book元素的输出,而第6到第9行是针对第2个book。
1 startDocument
2 attribute name is:id attribute value:01
3 name is:Java
4 price is:15
5 memo is:good book
6 attribute name is:id attribute value:02
7 name is:FrameWork
8 price is:20
9 memo is:new book
10 endDocument
4 DOM和SAX两种解析方式的应用场景
在基于DOM的方式里,由于我们会把整个xml文档以DOM树的方式装载到内存里,所以可以边解析边修改,而且还能再次解析已经被解析过的内容。
而在SAX的方式里,由于我们是以基于回调函数的方式来解析,所以并不需要把整个文档载入到内存,这样能节省内存资源。
所以说,选择 DOM 还是 SAX,这取决于如下三个个因素。
第一,如果我们在解析时还打算更新xml里的数据,那么建议使用DOM方式。
第二,如果待解析的文件过大,把它全部装载到内存时可能会影响到内存性能,那么建议使用SAX的方式。
第三,如果我们对解析的速度有一定的要求,那么建议使用SAX方式,因为它比DOM方式要快些。
Java解析XML文件的方式的更多相关文章
- 使用Java解析XML文件或XML字符串的例子
转: 使用Java解析XML文件或XML字符串的例子 2017年09月16日 11:36:18 inter_peng 阅读数:4561 标签: JavaXML-Parserdom4j 更多 个人分类: ...
- JAVA解析XML之SAX方式
JAVA解析XML之SAX方式 SAX解析xml步骤 通过SAXParseFactory的静态newInstance()方法获取SAXParserFactory实例factory 通过SAXParse ...
- JAVA解析XML之DOM方式
JAVA解析XML之DOM方式 准备工作 创建DocumentBuilderFactory对象; 创建DocumentBuilder对象; 通过DocumentBuilder对象的parse方法 ...
- java解析xml文件并输出
使用java解析xml文件,通过dom4j,代码运行前需先导入dom4j架包. ParseXml类代码如下: import java.io.File; import java.util.ArrayLi ...
- android解析xml文件的方式
android解析xml文件的方式 作者:东子哥 ,发布于2012-11-26,来源:博客园 在androd手机中处理xml数据时很常见的事情,通常在不同平台传输数据的时候,我们就可能使用xm ...
- Java解析xml文件遇到特殊符号&会出现异常的解决方案
文/朱季谦 在一次Java解析xml文件的开发过程中,使用SAX解析时,出现了这样一个异常信息: Error on line 60 of document : 对实体 "xxx" ...
- java解析XML文件
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常非常优秀的Java XML API,具有性能优异.功能强大和极端易用使用的特点,同时它也是一个开放源 ...
- JAVA解析XML文件(DOM,SAX,JDOM,DOM4j附代码实现)
1.解析XML主要有四种方式 1.DOM方式解析XML(与平台无关,JAVA提供,一次性加载XML文件内容,形成树结构,不适用于大文件) 2.SAX方式解析XML(基于事件驱动,逐条解析,适用于只处理 ...
- 关于java解析xml文件出现的问题
DOM解析xml文件 问题1:导入javax.xml.parsers.DocumentBuilderFactory出现问题,如图: 解决办法是:由于创建工程时有个默认的jre,重新创建工程改掉就解决了 ...
随机推荐
- Loj3033 JOISC 2019 Day2两个天线
Loj3033 JOISC 2019 Day2两个天线 下午唯一听懂的题目但,但还是比较模糊.写一篇题解来加深一下印象. 题目大意:给定\(n\)根天线,第\(i\)跟天线的高度为\(h_i\),切它 ...
- 转 java面试题及答案(基础题122道,代码题19道)
JAVA相关基础知识1.面向对象的特征有哪些方面 1.抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择其中的一部分,暂时 ...
- 判断移动端还是PC端
window.onload=function(){ var sUserAgent = navigator.userAgent.toLowerCase(); var bIsIpad = sUserAge ...
- IdentityServer4 Clients
原文地址 Clients 的定义 Client是指那些从 identityserver获取 token的应用 通常需要为client定义下面通用的设置 唯一的client id secret, 如果需 ...
- Java 学习笔记(9)——java常用类
之前将Java的大部分语法都回顾完了,后面添加一些常见的操作,基础语法就结束了.至于在这里再次提到常用类是由于有一部分体现在使用它的继承类或者接口之类的.这些需要有面向对象编程的基础 Object类 ...
- Java 学习笔记(8)——匿名对象与内部类
一般在编写代码时可能会遇到这样的场景--在某些时候,我需要定义并某个类,但是只会使用这一次,或者是某个类对象只会使用一次,为它们专门取名可能会显的很麻烦.为了应对这种情况,Java中允许使用匿名对象和 ...
- poj1737-----这题有毒
这题有毒,不取模还会溢出,我哭了 <进阶指南>p337动态规划 公式就是个这了,代码就不贴了,反正是错的,用java算了
- DEVOPS技术实践_23:判断文件下载成功作为执行条件
在实际生产中,我们经常会需要通过判断一个结果作为一个条件去执行另一个内容,比如判断一个文件是否存在,判官一个命令是否执行成功等等 现在我们选择其中一个场景进行实验,当某个目录下存在,则执行操作 1. ...
- System类StringBuilder小结
- Revealjs网页版PPT让你复制粘贴另类装逼,简洁优雅又低调,不懂编程也看过来
Revealjs网页版PPT让你复制粘贴另类装逼,简洁优雅又低调,不懂编程也看过来 要了解一个新知识我们可以从三个方面入手:是什么,有什么用,怎么用.下面我们就从这三个方面进行讲解Reveal.js噢 ...