Java SAX 解析器示例

在Java中,SAX解析器提供了解析XML文档的API。SAX解析器与DOM解析器不同,因为它不会将完整的XML加载到内存中,并且按顺序读取XML文档。

SAX解析器

javax.xml.parsers.SAXParser 提供了使用事件处理程序解析 XML 文档的方法。该类实现了 XMLReader 接口,并提供了重载的 parse() 方法,用于从文件、输入流、SAX InputSource 和字符串 URI 中读取 XML 文档。实际的解析由 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檔案,通過查看它,我們可以得出結論,它包含了Employee的列表。每個Employee都有一個“id”屬性和字段“age”、“name”、“gender”和“role”。我們將使用SAX解析器來解析這個XML並創建一個Employee對象的列表。這裡是表示XML中的Employee元素的Employee對象。

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;
    }
    
}

讓我們創建一個自己的SAX解析器處理程序類,擴展DefaultHandler類。


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 {

	// 用於保存Employee對象的列表
	private List empList = null;
	private Employee emp = null;
	private StringBuilder data = null;

	// employee列表的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")) {
			// 創建一個新的Employee並將其放入Map中
			String id = attributes.getValue("id");
			// 初始化Employee對象並設置id屬性
			emp = new Employee();
			emp.setId(Integer.parseInt(id));
			// 初始化列表
			if (empList == null)
				empList = new ArrayList<>();
		} else if (qName.equalsIgnoreCase("name")) {
			// 為字段設置布爾值,將在設置Employee變量時使用
			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元素,設置Employee的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")) {
			// 將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對象在事件處理方法中被添加。同時,我們還有一個Employee字段,將用於創建Employee對象,一旦所有字段都設置完成,將其添加到employee列表中。

要覆蓋的SAX解析器方法

重要的覆寫方法有startElement()endElement()characters()。當SAXParser找到任何開始元素時,會開始解析文件,並調用startElement()方法。我們覆寫這個方法來設置用於識別元素的布爾變量。我們還使用這個方法在每次找到Employee開始元素時創建一個新的Employee對象。請查看此處如何讀取id屬性以設置Employee對象的id字段。當SAXParser在元素內找到字符數據時,會調用characters()方法。請注意,SAX解析器可能將數據分為多個塊並多次調用characters()方法(請參閱ContentHandler類的characters()方法文檔)。這就是為什麼我們使用StringBuilder使用append()方法來保存這些數據。endElement()是我們使用StringBuilder數據來設置Employee對象屬性並在找到Employee結束元素標記時將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實例一起傳遞給parse方法來處理回調事件。SAXParser在開始時可能有點困惑,但如果您正在處理大型XML文檔,它提供了比DOM解析器更高效的讀取XML的方式。這就是Java中SAX解析器的全部內容。

您可以從我們的 GitHub 儲存庫 下載該專案。

參考SAXParserDefaultHandler

Source:
https://www.digitalocean.com/community/tutorials/java-sax-parser-example