【文件處理】xml 文件 SAX解析
SAX的全稱是Simple APIs for XML,也即XML簡單應用程序接口。
與DOM不同,SAX提供的訪問模式是一種順序模式,這是一種快速讀寫XML數據的方式。
當使用SAX分析器對XML文檔進行分析時,會觸發一系列事件,並激活相應的事件處理函數,應用程序通過這些事件處理函數實現對XML文檔的訪問,因而SAX接口也被稱作事件驅動接口。
局限性:
1. SAX分析器在對XML文檔進行分析時,觸發了一系列的事件,由於事件觸發本身是有時序性的,因此,SAX提供的是一種順序訪問機制,對於已經分析過的部分,不能再倒回去重新處理。
即,一旦經過了某個元素,我們沒有辦法返回去再去訪問它。
2. SAX分析器只做了一些簡單的工作,大部分工作還要由應用程序自己去做。
也就是說,SAX分析器在實現時,只是順序地檢查XML文檔中的字節流,判斷當前字節是XML語法中的哪一部分、是否符合XML語法,然後再觸發相應的事件,而事件處理函數本身則要由應用程序自己來實現。
同DOM分析器相比,SAX分析器缺乏靈活性。
優勢:
然而,由於SAX分析器實現簡單,對內存要求比較低,(SAX不必將整個XML文檔加載到內存當中,因此它占據內存要比DOM小), 因此實現效率比較高。
對於大型的XML文檔來說,通常會用SAX而不是DOM。
並且對於那些只需要訪問XML文檔中的數據而不對文檔進行更改的應用程序來說,SAX分析器更為合適。
SAX分析器
XML解析器實際上就是一段代碼,它讀入一個XML文檔並分析其結構。
分類:
帶校驗的解析器
不校驗的解析器(效率高)
支持DOM的解析器(W3C的官方標準)
支持SAX的解析器(事實上的工業標準)
SAX是事件驅動的,文檔的讀入過程就是SAX的解析過程。
在讀入的過程中,遇到不同的項目,解析器會調用不同的處理方法。
【XML文件內容】
<?xml version = "1.0" encoding = "UTF-8"?> <students> <student id="1"> <name>tonyliu</name> <age>19</age> <sex>男</sex> <group> <id>1</id> <name>Group1</name> </group> </student> <student id="2"> <name>Susywang</name> <age>18</age> <sex>女</sex> <group> <id>2</id> <name>Group2</name> </group> </student> <student id="3"> <name>JackZhang</name> <age>35</age> <sex>男</sex> <group> <id>3</id> <name>Group3</name> </group> </student> </students>
【SAX 解析代碼】
根據xml文件內容分為2個類,一個是Student類另一個為Group類,分別記入相對應的字段。
再定義一個枚舉,分別存入一些常量,方便調用。這裏要註意的是,仔細看一下xml內容,會發現Student以及group類中都包含了id跟name的字段。所以當我們在寫代碼中為了讓計算機清楚的知道我們指的name,id到底是Student中的還是Group中的,要通過Enum定義的常量來區分。
package Entity; public class Student { private int id; private String name; private int age; private String sex; private Group group; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Group getGroup() { return group; } public void setGroup(Group group) { this.group = group; } public Student(int id, String name, int age, String sex, Group group) { super(); this.id = id; this.name = name; this.age = age; this.sex = sex; this.group = group; } public Student() { super(); // TODO Auto-generated constructor stub } @Override public String toString() { return "Student [age=" + age + ", group=" + group + ", id=" + id + ", name=" + name + ", sex=" + sex + "]"; } }Student 類
package Entity; public class Group { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Group() { super(); // TODO Auto-generated constructor stub } public Group(int id, String name) { super(); this.id = id; this.name = name; } @Override public String toString() { return "Group [id=" + id + ", name=" + name + "]"; } }Group 類
package Tool; public enum StudentEnum { stuName,age,sex,groupId,groupName,none }Enum(枚舉)
package Tool; import java.util.ArrayList; import java.util.List; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import Entity.Group; import Entity.Student; //繼承DefaultHandler這個類,為了使用startDocument,startElement,charecter,endelement,enddocument方法 public class StudentParser extends DefaultHandler{ private List<Student> students = null; //創建一個集合方便存放數據 private boolean isgroupState = false; //定義標簽區分group以及student中的id,name這樣的相同字段。因為只有2個類因此boolean類型即可 private StudentEnum state = StudentEnum.none; //把枚舉的狀態設置為none private Group group = null; //初始化Group類 private Student student = null; //初始化Student類 //設定一個集合方法為了方便外層調用students這個集合中的內容 public List<Student> getResult(){ return this.students; } @Override public void startDocument() throws SAXException { students = new ArrayList<Student>(); //初始化集合 } @Override public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { //通過startElement方法中的qName來指定xml內容中的student字段 if(qName.equals("student")){ String stuId = attrs.getValue("id"); //<Student>的屬性只有一個id,因此通過attrs.getValue這個方法獲取到id屬性 student = new Student(); //很重要,很重要,很重要。這裏一定要初始化Student這個類,為了就是下面通過.setId方法來指定id值 student.setId(Integer.parseInt(stuId)); } if(qName.equals("name")){ //這裏就看出來Enum的重要性了。如果是我們上面設定的isgroupState的話就調用Enum類中的group相關字段。相反就調用Enum類中Student相關常量 if(isgroupState) state = StudentEnum.groupName; else state = StudentEnum.stuName; return; } if(qName.equals("age")){ state = StudentEnum.age; //把狀態標註為枚舉中的age return; } if(qName.equals("sex")){ state = StudentEnum.sex; //同上 return; } if(qName.equals("group")){ group = new Group(); //如果為group那麽先要初始化Group這個類,方便調用group類中的方法 isgroupState = true; //因為默認isgroupState的狀態為false。這裏如果是group那麽自然狀態就變成了true return; } //group類中只有2個字段,而name在我們上述中已經有了判斷。那麽id跟Student一樣先判斷qName是否是id,再判斷是否是groupState中的id,如果是就把state狀態標註成Enum中的groupId. if(qName.equals("id")){ if(isgroupState){ state = StudentEnum.groupId; return; } } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if(state == StudentEnum.none){ return; } //定義一個String類,存放characters方法中給出了三個參數. String str = new String(ch,start,length); //通過switch ,case分別把每個字段記錄進去並通過student,group類中的set方法來進行設定。設定完畢後一定要把每個狀態標註成none switch (state) { case stuName: student.setName(str); state = StudentEnum.none; break; case age: student.setAge(Integer.parseInt(str)); state = StudentEnum.none; break; case sex: student.setSex(str); state = StudentEnum.none; break; case groupId: group.setId(Integer.parseInt(str)); state = StudentEnum.none; break; case groupName: group.setName(str); state = StudentEnum.none; break; default: break; } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { //這裏是指在什麽節點結束。xml內容我們是以</group></Student>為結束的因此這裏還是根據qName來判斷並標註狀態。 if(qName.equals("group")){ isgroupState = false; student.setGroup(group); //通過student類中的.setGroup方法把group中的內容追加到Student類中 return; } if(qName.equals("student")){ students.add(student); //最後把student類中的內容全部加載到students這個集合中 return; } } @Override public void endDocument() throws SAXException { // TODO Auto-generated method stub super.endDocument(); } }StudentParser
StudentParser這個類中內容是我們SAX解析中的重點。同DOM一樣也是一個解析器,通過逐步讀取數據的方式進行解析不需要一下子把數據讀取到內容中後再解析。
【Test測試】
package View; import java.io.IOException; import java.util.List; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.SAXException; import Entity.Student; import Tool.StudentParser; public class Test { public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
//調用SAXParserFactory以及SAXParer工廠類。 SAXParserFactory factory = SAXParserFactory.newInstance(); SAXParser parser = factory.newSAXParser(); StudentParser handler = new StudentParser(); //初始化我們的解析器 parser.parse("C:/Users/IBM_ADMIN/Desktop/xml/student.xml", handler);//通過parser.parse方法指定文件地址以及加載我們的解析器 List<Student> students = handler.getResult(); //調用解析中的.getResult方法來調用students集合中的內容。 //循環這個集合並打印 for(Student stu:students){ System.out.println(stu); } } }
【結果】
下面結果可以看出我們成功的對XML文件中的內容進行了解析。日後比如說有個Group4,並有了新的成員的情況下,我們也不需要再重寫我們的代碼。只要xml結構不變,那麽內容也會自動的被讀取。
Student [age=19, group=Group [id=1, name=Group1], id=1, name=tonyliu, sex=男] Student [age=18, group=Group [id=2, name=Group2], id=2, name=Susywang, sex=女] Student [age=35, group=Group [id=3, name=Group3], id=3, name=JackZhang, sex=男]
【文件處理】xml 文件 SAX解析