Java SAX解析器示例

时间:2020-02-23 14:36:49  来源:igfitidea点击:

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

SAX解析器

javax.xml.parsers.SAXParser提供了使用事件处理程序解析XML文档的方法。
此类实现XMLReader接口,并提供parse()方法的重载版本,以从File,InputStream,SAX InputSource和String 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>hyman</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列表。
每个员工都有" id"属性,并字段"年龄","姓名","性别"和"角色"。

我们将使用SAX解析器来解析此XML并创建Employee对象的列表。

这是代表XML中的Employee元素的Employee对象。

package com.theitroad.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 Parser Handler类,并扩展DefaultHandler类。

package com.theitroad.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.theitroad.xml.Employee;

public class MyHandler extends DefaultHandler {

	//List to hold Employees object
	private List<Employee> empList = null;
	private Employee emp = null;
	private StringBuilder data = null;

	//getter method for employee list
	public List<Employee> 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")) {
			//create a new Employee and put it in Map
			String id = attributes.getValue("id");
			//initialize Employee object and set id attribute
			emp = new Employee();
			emp.setId(Integer.parseInt(id));
			//initialize list
			if (empList == null)
				empList = new ArrayList<>();
		} else if (qName.equalsIgnoreCase("name")) {
			//set boolean values for fields, will be used in setting Employee variables
			bName = true;
		} else if (qName.equalsIgnoreCase("age")) {
			bAge = true;
		} else if (qName.equalsIgnoreCase("gender")) {
			bGender = true;
		} else if (qName.equalsIgnoreCase("role")) {
			bRole = true;
		}
		//create the data container
		data = new StringBuilder();
	}

	@Override
	public void endElement(String uri, String localName, String qName) throws SAXException {
		if (bAge) {
			//age element, set 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")) {
			//add Employee object to list
			empList.add(emp);
		}
	}

	@Override
	public void characters(char ch[], int start, int length) throws SAXException {
		data.append(new String(ch, start, length));
	}
}

MyHandler仅使用getter方法将" Employee"对象的列表作为字段包含在内。
在事件处理程序方法中添加了" Employee"对象。
另外,我们还有一个Employee字段,它将用于创建Employee对象,并且一旦设置了所有字段,就将其添加到employee列表中。

SAX解析器方法重写

覆盖的重要方法是startElement()endElement()characters()

当发现任何起始元素时,SAXParser开始解析文档,调用startElement()方法。
我们重写此方法来设置将用于标识元素的布尔变量。

每当找到Employee start元素时,我们也使用此方法创建一个新的Employee对象。
检查此处如何读取id属性,以设置Employee Objectid字段。

SAXParser在元素内部发现字符数据时,将调用" characters()"方法。
请注意,SAX解析器可能会将数据分为多个块,并多次调用characters()方法(请参阅ContentHandler类character()方法文档)。
这就是为什么我们使用StringBuilder通过append()方法保留此数据的原因。

" endElement()"是我们使用StringBuilder数据设置员工对象属性并将Employee对象添加到列表的地方,只要我们找到Employee end element标签即可。

下面是使用MyHandler解析XML到Employee对象列表的测试程序。

package com.theitroad.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.theitroad.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/hyman/employees.xml"), handler);
      //Get Employees list
      List<Employee> empList = handler.getEmpList();
      //print employee information
      for(Employee emp : empList)
          System.out.println(emp);
  } catch (ParserConfigurationException | SAXException | IOException e) {
      e.printStackTrace();
  }
  }

}

这是上面程序的输出。

Employee:: ID=1 Name=hyman 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 Parser更有效的XML读取方式。
Java的SAX解析器就这些了。