SAX解析器在Java中提供了解析XML文档的API。SAX解析器与DOM解析器不同,因为它不会将整个XML加载到内存中,而是按顺序读取XML文档。
SAX解析器
javax.xml.parsers.SAXParser
提供了使用事件处理程序解析 XML 文档的方法。这个类实现了 XMLReader
接口,并提供了重载版本的 parse()
方法,用于从文件、输入流、SAX InputSource 和字符串 URI 中读取 XML 文档。实际的解析是由 Handler 类完成的。我们需要创建自己的 Handler 类来解析 XML 文档。我们需要实现 org.xml.sax.ContentHandler
接口来创建我们自己的处理程序类。这个接口包含了在事件发生时接收通知的回调方法。例如 StartDocument、EndDocument、StartElement、EndElement、CharacterData 等等。org.xml.sax.helpers.DefaultHandler
提供了 ContentHandler 接口的默认实现,我们可以扩展这个类来创建我们自己的处理程序。推荐扩展这个类,因为我们可能只需要实现其中的几个方法。扩展这个类将使我们的代码更清晰和可维护。
SAX 解析器示例
现在让我们来看 SAX 解析器示例程序,稍后我会详细解释不同的特性。 employees.xml
<?xml version="1.0" encoding="UTF-8"?>
<Employees>
<Employee id="1">
<age>29</age>
<name>Pankaj</name>
<gender>Male</gender>
<role>Java Developer</role>
</Employee>
<Employee id="2">
<age>35</age>
<name>Lisa</name>
<gender>Female</gender>
<role>CEO</role>
</Employee>
<Employee id="3">
<age>40</age>
<name>Tom</name>
<gender>Male</gender>
<role>Manager</role>
</Employee>
<Employee id="4">
<age>25</age>
<name>Meghna</name>
<gender>Female</gender>
<role>Manager</role>
</Employee>
</Employees>
所以我们在文件系统中某个地方有一个 XML 文件,通过查看它,我们可以得出结论它包含一组雇员。每个雇员都有一个 id
属性和字段 age
、name
、gender
和 role
。我们将使用 SAX 解析器来解析这个 XML,并创建一个雇员对象的列表。这里是表示 XML 中雇员元素的雇员对象。
package com.journaldev.xml;
public class Employee {
private int id;
private String name;
private String gender;
private int age;
private String role;
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
@Override
public String toString() {
return "Employee:: ID="+this.id+" Name=" + this.name + " Age=" + this.age + " Gender=" + this.gender +
" Role=" + this.role;
}
}
让我们创建一个扩展 DefaultHandler 类的自定义 SAX 解析器处理类。
package com.journaldev.xml.sax;
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 com.journaldev.xml.Employee;
public class MyHandler extends DefaultHandler {
// 用于保存雇员对象的列表
private List empList = null;
private Employee emp = null;
private StringBuilder data = null;
// 雇员列表的 getter 方法
public List getEmpList() {
return empList;
}
boolean bAge = false;
boolean bName = false;
boolean bGender = false;
boolean bRole = false;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("Employee")) {
// 创建一个新的雇员并将其放入 Map
String id = attributes.getValue("id");
// 初始化雇员对象并设置 id 属性
emp = new Employee();
emp.setId(Integer.parseInt(id));
// 初始化列表
if (empList == null)
empList = new ArrayList<>();
} else if (qName.equalsIgnoreCase("name")) {
// 为字段设置布尔值,在设置雇员变量时将使用
bName = true;
} else if (qName.equalsIgnoreCase("age")) {
bAge = true;
} else if (qName.equalsIgnoreCase("gender")) {
bGender = true;
} else if (qName.equalsIgnoreCase("role")) {
bRole = true;
}
// 创建数据容器
data = new StringBuilder();
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (bAge) {
// age 元素,设置雇员年龄
emp.setAge(Integer.parseInt(data.toString()));
bAge = false;
} else if (bName) {
emp.setName(data.toString());
bName = false;
} else if (bRole) {
emp.setRole(data.toString());
bRole = false;
} else if (bGender) {
emp.setGender(data.toString());
bGender = false;
}
if (qName.equalsIgnoreCase("Employee")) {
// 将雇员对象添加到列表
empList.add(emp);
}
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
data.append(new String(ch, start, length));
}
}
MyHandler 包含 Employee
对象的列表作为一个字段,只有一个 getter 方法。 Employee
对象是在事件处理程序方法中添加的。此外,我们有一个雇员字段,将用于创建一个雇员对象,一旦所有字段都设置好,就将其添加到雇员列表中。
要重写的 SAX 解析器方法
重写的重要方法是startElement()
、endElement()
和characters()
。当发现任何开始元素时,SAXParser
开始解析文档,会调用startElement()
方法。我们重写这个方法来设置布尔变量,这些变量将用于识别元素。我们还使用这个方法来在每次发现员工开始元素时创建一个新的Employee对象。检查这里如何读取id属性以设置Employee对象的id
字段。当SAXParser在元素内找到字符数据时,会调用characters()
方法。请注意,SAX解析器可能会将数据分成多个块并多次调用characters()
方法(请阅读ContentHandler类的characters()方法文档)。这就是为什么我们使用StringBuilder来保留这些数据,使用append()方法。endElement()
是我们使用StringBuilder数据设置员工对象属性并在找到员工结束元素标签时将Employee对象添加到列表的地方。以下是使用MyHandler
解析上述XML到Employee对象列表的测试程序。
package com.journaldev.xml.sax;
import java.io.File;
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 com.journaldev.xml.Employee;
public class XMLParserSAX {
public static void main(String[] args) {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
try {
SAXParser saxParser = saxParserFactory.newSAXParser();
MyHandler handler = new MyHandler();
saxParser.parse(new File("/Users/pankaj/employees.xml"), handler);
//获取员工列表
List empList = handler.getEmpList();
//打印员工信息
for(Employee emp : empList)
System.out.println(emp);
} catch (ParserConfigurationException | SAXException | IOException e) {
e.printStackTrace();
}
}
}
这是上述程序的输出。
Employee:: ID=1 Name=Pankaj Age=29 Gender=Male Role=Java Developer
Employee:: ID=2 Name=Lisa Age=35 Gender=Female Role=CEO
Employee:: ID=3 Name=Tom Age=40 Gender=Male Role=Manager
Employee:: ID=4 Name=Meghna Age=25 Gender=Female Role=Manager
SAXParserFactory
提供了获取SAXParser
实例的工厂方法。我们将File对象传递给解析方法,同时传递MyHandler实例来处理回调事件。SAXParser在开始时可能有点混乱,但如果您正在处理大型XML文档,它提供了一种比DOM解析器更有效的读取XML的方式。这就是关于Java中SAX解析器的全部内容。
你可以从我们的GitHub仓库下载项目。
参考: SAXParser, DefaultHandler
Source:
https://www.digitalocean.com/community/tutorials/java-sax-parser-example